blob: 13d2511eeacb281f6597a3c54fb5628c45422fbd [file] [log] [blame]
Skylar Changc958bdd2018-01-25 17:10:41 -08001/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
Amir Levy9659e592016-10-27 18:08:27 +03002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include "ipa_i.h"
13#include <linux/dmapool.h>
14#include <linux/delay.h>
15#include <linux/mm.h>
Skylar Chang6b41f8d2016-11-01 12:50:11 -070016#include "ipa_qmi_service.h"
Amir Levy9659e592016-10-27 18:08:27 +030017
18#define IPA_HOLB_TMR_DIS 0x0
Mohammed Javiddb111a32018-02-22 15:16:47 +053019#define IPA_HOLB_TMR_EN 0x1
Amir Levy9659e592016-10-27 18:08:27 +030020
21#define IPA_HW_INTERFACE_WDI_VERSION 0x0001
22#define IPA_HW_WDI_RX_MBOX_START_INDEX 48
23#define IPA_HW_WDI_TX_MBOX_START_INDEX 50
24#define IPA_WDI_RING_ALIGNMENT 8
25
26#define IPA_WDI_CONNECTED BIT(0)
27#define IPA_WDI_ENABLED BIT(1)
28#define IPA_WDI_RESUMED BIT(2)
29#define IPA_UC_POLL_SLEEP_USEC 100
30
Amir Levy9659e592016-10-27 18:08:27 +030031struct ipa_wdi_res {
32 struct ipa_wdi_buffer_info *res;
33 unsigned int nents;
34 bool valid;
35};
36
37static struct ipa_wdi_res wdi_res[IPA_WDI_MAX_RES];
38
39static void ipa3_uc_wdi_loaded_handler(void);
40
41/**
42 * enum ipa_hw_2_cpu_wdi_events - Values that represent HW event to be sent to
43 * CPU.
44 * @IPA_HW_2_CPU_EVENT_WDI_ERROR : Event to specify that HW detected an error
45 * in WDI
46 */
47enum ipa_hw_2_cpu_wdi_events {
48 IPA_HW_2_CPU_EVENT_WDI_ERROR =
49 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 0),
50};
51
52/**
53 * enum ipa_hw_wdi_channel_states - Values that represent WDI channel state
54 * machine.
55 * @IPA_HW_WDI_CHANNEL_STATE_INITED_DISABLED : Channel is initialized but
56 * disabled
57 * @IPA_HW_WDI_CHANNEL_STATE_ENABLED_SUSPEND : Channel is enabled but in
58 * suspended state
59 * @IPA_HW_WDI_CHANNEL_STATE_RUNNING : Channel is running. Entered after
60 * SET_UP_COMMAND is processed successfully
61 * @IPA_HW_WDI_CHANNEL_STATE_ERROR : Channel is in error state
62 * @IPA_HW_WDI_CHANNEL_STATE_INVALID : Invalid state. Shall not be in use in
63 * operational scenario
64 *
65 * These states apply to both Tx and Rx paths. These do not reflect the
66 * sub-state the state machine may be in.
67 */
68enum ipa_hw_wdi_channel_states {
69 IPA_HW_WDI_CHANNEL_STATE_INITED_DISABLED = 1,
70 IPA_HW_WDI_CHANNEL_STATE_ENABLED_SUSPEND = 2,
71 IPA_HW_WDI_CHANNEL_STATE_RUNNING = 3,
72 IPA_HW_WDI_CHANNEL_STATE_ERROR = 4,
73 IPA_HW_WDI_CHANNEL_STATE_INVALID = 0xFF
74};
75
76/**
77 * enum ipa3_cpu_2_hw_commands - Values that represent the WDI commands from
78 * CPU
79 * @IPA_CPU_2_HW_CMD_WDI_TX_SET_UP : Command to set up WDI Tx Path
80 * @IPA_CPU_2_HW_CMD_WDI_RX_SET_UP : Command to set up WDI Rx Path
81 * @IPA_CPU_2_HW_CMD_WDI_RX_EXT_CFG : Provide extended config info for Rx path
82 * @IPA_CPU_2_HW_CMD_WDI_CH_ENABLE : Command to enable a channel
83 * @IPA_CPU_2_HW_CMD_WDI_CH_DISABLE : Command to disable a channel
84 * @IPA_CPU_2_HW_CMD_WDI_CH_SUSPEND : Command to suspend a channel
85 * @IPA_CPU_2_HW_CMD_WDI_CH_RESUME : Command to resume a channel
86 * @IPA_CPU_2_HW_CMD_WDI_TEAR_DOWN : Command to tear down WDI Tx/ Rx Path
87 */
88enum ipa_cpu_2_hw_wdi_commands {
89 IPA_CPU_2_HW_CMD_WDI_TX_SET_UP =
90 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 0),
91 IPA_CPU_2_HW_CMD_WDI_RX_SET_UP =
92 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 1),
93 IPA_CPU_2_HW_CMD_WDI_RX_EXT_CFG =
94 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 2),
95 IPA_CPU_2_HW_CMD_WDI_CH_ENABLE =
96 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 3),
97 IPA_CPU_2_HW_CMD_WDI_CH_DISABLE =
98 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 4),
99 IPA_CPU_2_HW_CMD_WDI_CH_SUSPEND =
100 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 5),
101 IPA_CPU_2_HW_CMD_WDI_CH_RESUME =
102 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 6),
103 IPA_CPU_2_HW_CMD_WDI_TEAR_DOWN =
104 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 7),
105};
106
107/**
108 * enum ipa_hw_2_cpu_cmd_resp_status - Values that represent WDI related
109 * command response status to be sent to CPU.
110 */
111enum ipa_hw_2_cpu_cmd_resp_status {
112 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS =
113 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 0),
114 IPA_HW_2_CPU_MAX_WDI_TX_CHANNELS =
115 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 1),
116 IPA_HW_2_CPU_WDI_CE_RING_OVERRUN_POSSIBILITY =
117 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 2),
118 IPA_HW_2_CPU_WDI_CE_RING_SET_UP_FAILURE =
119 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 3),
120 IPA_HW_2_CPU_WDI_CE_RING_PARAMS_UNALIGNED =
121 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 4),
122 IPA_HW_2_CPU_WDI_COMP_RING_OVERRUN_POSSIBILITY =
123 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 5),
124 IPA_HW_2_CPU_WDI_COMP_RING_SET_UP_FAILURE =
125 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 6),
126 IPA_HW_2_CPU_WDI_COMP_RING_PARAMS_UNALIGNED =
127 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 7),
128 IPA_HW_2_CPU_WDI_UNKNOWN_TX_CHANNEL =
129 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 8),
130 IPA_HW_2_CPU_WDI_TX_INVALID_FSM_TRANSITION =
131 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 9),
132 IPA_HW_2_CPU_WDI_TX_FSM_TRANSITION_ERROR =
133 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 10),
134 IPA_HW_2_CPU_MAX_WDI_RX_CHANNELS =
135 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 11),
136 IPA_HW_2_CPU_WDI_RX_RING_PARAMS_UNALIGNED =
137 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 12),
138 IPA_HW_2_CPU_WDI_RX_RING_SET_UP_FAILURE =
139 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 13),
140 IPA_HW_2_CPU_WDI_UNKNOWN_RX_CHANNEL =
141 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 14),
142 IPA_HW_2_CPU_WDI_RX_INVALID_FSM_TRANSITION =
143 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 15),
144 IPA_HW_2_CPU_WDI_RX_FSM_TRANSITION_ERROR =
145 FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 16),
146};
147
148/**
149 * enum ipa_hw_wdi_errors - WDI specific error types.
150 * @IPA_HW_WDI_ERROR_NONE : No error persists
151 * @IPA_HW_WDI_CHANNEL_ERROR : Error is specific to channel
152 */
153enum ipa_hw_wdi_errors {
154 IPA_HW_WDI_ERROR_NONE = 0,
155 IPA_HW_WDI_CHANNEL_ERROR = 1
156};
157
158/**
159 * enum ipa_hw_wdi_ch_errors = List of WDI Channel error types. This is present
160 * in the event param.
161 * @IPA_HW_WDI_CH_ERR_NONE : No error persists
162 * @IPA_HW_WDI_TX_COMP_RING_WP_UPDATE_FAIL : Write pointer update failed in Tx
163 * Completion ring
164 * @IPA_HW_WDI_TX_FSM_ERROR : Error in the state machine transition
165 * @IPA_HW_WDI_TX_COMP_RE_FETCH_FAIL : Error while calculating num RE to bring
166 * @IPA_HW_WDI_CH_ERR_RESERVED : Reserved - Not available for CPU to use
167*/
168enum ipa_hw_wdi_ch_errors {
169 IPA_HW_WDI_CH_ERR_NONE = 0,
170 IPA_HW_WDI_TX_COMP_RING_WP_UPDATE_FAIL = 1,
171 IPA_HW_WDI_TX_FSM_ERROR = 2,
172 IPA_HW_WDI_TX_COMP_RE_FETCH_FAIL = 3,
173 IPA_HW_WDI_CH_ERR_RESERVED = 0xFF
174};
175
176/**
177 * struct IpaHwSharedMemWdiMapping_t - Structure referring to the common and
178 * WDI section of 128B shared memory located in offset zero of SW Partition in
179 * IPA SRAM.
180 *
181 * The shared memory is used for communication between IPA HW and CPU.
182 */
183struct IpaHwSharedMemWdiMapping_t {
184 struct IpaHwSharedMemCommonMapping_t common;
185 u32 reserved_2B_28;
186 u32 reserved_2F_2C;
187 u32 reserved_33_30;
188 u32 reserved_37_34;
189 u32 reserved_3B_38;
190 u32 reserved_3F_3C;
191 u16 interfaceVersionWdi;
192 u16 reserved_43_42;
193 u8 wdi_tx_ch_0_state;
194 u8 wdi_rx_ch_0_state;
195 u16 reserved_47_46;
196} __packed;
197
198/**
199 * struct IpaHwWdiTxSetUpCmdData_t - Structure holding the parameters for
200 * IPA_CPU_2_HW_CMD_WDI_TX_SET_UP command.
201 * @comp_ring_base_pa : This is the physical address of the base of the Tx
202 * completion ring
203 * @comp_ring_size : This is the size of the Tx completion ring
204 * @reserved_comp_ring : Reserved field for expansion of Completion ring params
205 * @ce_ring_base_pa : This is the physical address of the base of the Copy
206 * Engine Source Ring
207 * @ce_ring_size : Copy Engine Ring size
208 * @reserved_ce_ring : Reserved field for expansion of CE ring params
209 * @ce_ring_doorbell_pa : This is the physical address of the doorbell that the
210 * IPA uC has to write into to trigger the copy engine
211 * @num_tx_buffers : Number of pkt buffers allocated. The size of the CE ring
212 * and the Tx completion ring has to be atleast ( num_tx_buffers + 1)
213 * @ipa_pipe_number : This is the IPA pipe number that has to be used for the
214 * Tx path
215 * @reserved : Reserved field
216 *
217 * Parameters are sent as pointer thus should be reside in address accessible
218 * to HW
219 */
220struct IpaHwWdiTxSetUpCmdData_t {
221 u32 comp_ring_base_pa;
222 u16 comp_ring_size;
223 u16 reserved_comp_ring;
224 u32 ce_ring_base_pa;
225 u16 ce_ring_size;
226 u16 reserved_ce_ring;
227 u32 ce_ring_doorbell_pa;
228 u16 num_tx_buffers;
229 u8 ipa_pipe_number;
230 u8 reserved;
231} __packed;
232
233struct IpaHwWdi2TxSetUpCmdData_t {
234 u32 comp_ring_base_pa;
235 u32 comp_ring_base_pa_hi;
236 u16 comp_ring_size;
237 u16 reserved_comp_ring;
238 u32 ce_ring_base_pa;
239 u32 ce_ring_base_pa_hi;
240 u16 ce_ring_size;
241 u16 reserved_ce_ring;
242 u32 ce_ring_doorbell_pa;
243 u32 ce_ring_doorbell_pa_hi;
244 u16 num_tx_buffers;
245 u8 ipa_pipe_number;
246 u8 reserved;
247} __packed;
248/**
249 * struct IpaHwWdiRxSetUpCmdData_t - Structure holding the parameters for
250 * IPA_CPU_2_HW_CMD_WDI_RX_SET_UP command.
251 * @rx_ring_base_pa : This is the physical address of the base of the Rx ring
252 * (containing Rx buffers)
253 * @rx_ring_size : This is the size of the Rx ring
254 * @rx_ring_rp_pa : This is the physical address of the location through which
255 * IPA uc is expected to communicate about the Read pointer into the Rx Ring
256 * @ipa_pipe_number : This is the IPA pipe number that has to be used for the
257 * Rx path
258 *
259 * Parameters are sent as pointer thus should be reside in address accessible
260 * to HW
261*/
262struct IpaHwWdiRxSetUpCmdData_t {
263 u32 rx_ring_base_pa;
264 u32 rx_ring_size;
265 u32 rx_ring_rp_pa;
266 u8 ipa_pipe_number;
267} __packed;
268
269struct IpaHwWdi2RxSetUpCmdData_t {
270 u32 rx_ring_base_pa;
271 u32 rx_ring_base_pa_hi;
272 u32 rx_ring_size;
273 u32 rx_ring_rp_pa;
274 u32 rx_ring_rp_pa_hi;
275 u32 rx_comp_ring_base_pa;
276 u32 rx_comp_ring_base_pa_hi;
277 u32 rx_comp_ring_size;
278 u32 rx_comp_ring_wp_pa;
279 u32 rx_comp_ring_wp_pa_hi;
280 u8 ipa_pipe_number;
281} __packed;
282/**
283 * union IpaHwWdiRxExtCfgCmdData_t - Structure holding the parameters for
284 * IPA_CPU_2_HW_CMD_WDI_RX_EXT_CFG command.
285 * @ipa_pipe_number : The IPA pipe number for which this config is passed
286 * @qmap_id : QMAP ID to be set in the metadata register
287 * @reserved : Reserved
288 *
289 * The parameters are passed as immediate params in the shared memory
290*/
291union IpaHwWdiRxExtCfgCmdData_t {
292 struct IpaHwWdiRxExtCfgCmdParams_t {
293 u32 ipa_pipe_number:8;
294 u32 qmap_id:8;
295 u32 reserved:16;
296 } __packed params;
297 u32 raw32b;
298} __packed;
299
300/**
301 * union IpaHwWdiCommonChCmdData_t - Structure holding the parameters for
302 * IPA_CPU_2_HW_CMD_WDI_TEAR_DOWN,
303 * IPA_CPU_2_HW_CMD_WDI_CH_ENABLE,
304 * IPA_CPU_2_HW_CMD_WDI_CH_DISABLE,
305 * IPA_CPU_2_HW_CMD_WDI_CH_SUSPEND,
306 * IPA_CPU_2_HW_CMD_WDI_CH_RESUME command.
307 * @ipa_pipe_number : The IPA pipe number. This could be Tx or an Rx pipe
308 * @reserved : Reserved
309 *
310 * The parameters are passed as immediate params in the shared memory
311 */
312union IpaHwWdiCommonChCmdData_t {
313 struct IpaHwWdiCommonChCmdParams_t {
314 u32 ipa_pipe_number:8;
315 u32 reserved:24;
316 } __packed params;
317 u32 raw32b;
318} __packed;
319
320/**
321 * union IpaHwWdiErrorEventData_t - parameters for IPA_HW_2_CPU_EVENT_WDI_ERROR
322 * event.
323 * @wdi_error_type : The IPA pipe number to be torn down. This could be Tx or
324 * an Rx pipe
325 * @reserved : Reserved
326 * @ipa_pipe_number : IPA pipe number on which error has happened. Applicable
327 * only if error type indicates channel error
328 * @wdi_ch_err_type : Information about the channel error (if available)
329 *
330 * The parameters are passed as immediate params in the shared memory
331 */
332union IpaHwWdiErrorEventData_t {
333 struct IpaHwWdiErrorEventParams_t {
334 u32 wdi_error_type:8;
335 u32 reserved:8;
336 u32 ipa_pipe_number:8;
337 u32 wdi_ch_err_type:8;
338 } __packed params;
339 u32 raw32b;
340} __packed;
341
342static void ipa3_uc_wdi_event_log_info_handler(
343struct IpaHwEventLogInfoData_t *uc_event_top_mmio)
344
345{
346 if ((uc_event_top_mmio->featureMask & (1 << IPA_HW_FEATURE_WDI)) == 0) {
347 IPAERR("WDI feature missing 0x%x\n",
348 uc_event_top_mmio->featureMask);
349 return;
350 }
351
352 if (uc_event_top_mmio->statsInfo.featureInfo[IPA_HW_FEATURE_WDI].
353 params.size != sizeof(struct IpaHwStatsWDIInfoData_t)) {
354 IPAERR("wdi stats sz invalid exp=%zu is=%u\n",
355 sizeof(struct IpaHwStatsWDIInfoData_t),
356 uc_event_top_mmio->statsInfo.
357 featureInfo[IPA_HW_FEATURE_WDI].params.size);
358 return;
359 }
360
361 ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_ofst = uc_event_top_mmio->
362 statsInfo.baseAddrOffset + uc_event_top_mmio->statsInfo.
363 featureInfo[IPA_HW_FEATURE_WDI].params.offset;
364 IPAERR("WDI stats ofst=0x%x\n", ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_ofst);
365 if (ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_ofst +
366 sizeof(struct IpaHwStatsWDIInfoData_t) >=
367 ipa3_ctx->ctrl->ipa_reg_base_ofst +
368 ipahal_get_reg_n_ofst(IPA_SRAM_DIRECT_ACCESS_n, 0) +
369 ipa3_ctx->smem_sz) {
370 IPAERR("uc_wdi_stats 0x%x outside SRAM\n",
371 ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_ofst);
372 return;
373 }
374
375 ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_mmio =
376 ioremap(ipa3_ctx->ipa_wrapper_base +
377 ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_ofst,
378 sizeof(struct IpaHwStatsWDIInfoData_t));
379 if (!ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_mmio) {
380 IPAERR("fail to ioremap uc wdi stats\n");
381 return;
382 }
383}
384
385static void ipa3_uc_wdi_event_handler(struct IpaHwSharedMemCommonMapping_t
386 *uc_sram_mmio)
387
388{
389 union IpaHwWdiErrorEventData_t wdi_evt;
390 struct IpaHwSharedMemWdiMapping_t *wdi_sram_mmio_ext;
391
392 if (uc_sram_mmio->eventOp ==
393 IPA_HW_2_CPU_EVENT_WDI_ERROR) {
394 wdi_evt.raw32b = uc_sram_mmio->eventParams;
395 IPADBG("uC WDI evt errType=%u pipe=%d cherrType=%u\n",
396 wdi_evt.params.wdi_error_type,
397 wdi_evt.params.ipa_pipe_number,
398 wdi_evt.params.wdi_ch_err_type);
399 wdi_sram_mmio_ext =
400 (struct IpaHwSharedMemWdiMapping_t *)
401 uc_sram_mmio;
402 IPADBG("tx_ch_state=%u rx_ch_state=%u\n",
403 wdi_sram_mmio_ext->wdi_tx_ch_0_state,
404 wdi_sram_mmio_ext->wdi_rx_ch_0_state);
405 }
406}
407
408/**
409 * ipa3_get_wdi_stats() - Query WDI statistics from uc
410 * @stats: [inout] stats blob from client populated by driver
411 *
412 * Returns: 0 on success, negative on failure
413 *
414 * @note Cannot be called from atomic context
415 *
416 */
417int ipa3_get_wdi_stats(struct IpaHwStatsWDIInfoData_t *stats)
418{
419#define TX_STATS(y) stats->tx_ch_stats.y = \
420 ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_mmio->tx_ch_stats.y
421#define RX_STATS(y) stats->rx_ch_stats.y = \
422 ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_mmio->rx_ch_stats.y
423
424 if (!stats || !ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_mmio) {
425 IPAERR("bad parms stats=%p wdi_stats=%p\n",
426 stats,
427 ipa3_ctx->uc_wdi_ctx.wdi_uc_stats_mmio);
428 return -EINVAL;
429 }
430 IPA_ACTIVE_CLIENTS_INC_SIMPLE();
431
432 TX_STATS(num_pkts_processed);
433 TX_STATS(copy_engine_doorbell_value);
434 TX_STATS(num_db_fired);
435 TX_STATS(tx_comp_ring_stats.ringFull);
436 TX_STATS(tx_comp_ring_stats.ringEmpty);
437 TX_STATS(tx_comp_ring_stats.ringUsageHigh);
438 TX_STATS(tx_comp_ring_stats.ringUsageLow);
439 TX_STATS(tx_comp_ring_stats.RingUtilCount);
440 TX_STATS(bam_stats.bamFifoFull);
441 TX_STATS(bam_stats.bamFifoEmpty);
442 TX_STATS(bam_stats.bamFifoUsageHigh);
443 TX_STATS(bam_stats.bamFifoUsageLow);
444 TX_STATS(bam_stats.bamUtilCount);
445 TX_STATS(num_db);
446 TX_STATS(num_unexpected_db);
447 TX_STATS(num_bam_int_handled);
448 TX_STATS(num_bam_int_in_non_running_state);
449 TX_STATS(num_qmb_int_handled);
450 TX_STATS(num_bam_int_handled_while_wait_for_bam);
451
452 RX_STATS(max_outstanding_pkts);
453 RX_STATS(num_pkts_processed);
454 RX_STATS(rx_ring_rp_value);
455 RX_STATS(rx_ind_ring_stats.ringFull);
456 RX_STATS(rx_ind_ring_stats.ringEmpty);
457 RX_STATS(rx_ind_ring_stats.ringUsageHigh);
458 RX_STATS(rx_ind_ring_stats.ringUsageLow);
459 RX_STATS(rx_ind_ring_stats.RingUtilCount);
460 RX_STATS(bam_stats.bamFifoFull);
461 RX_STATS(bam_stats.bamFifoEmpty);
462 RX_STATS(bam_stats.bamFifoUsageHigh);
463 RX_STATS(bam_stats.bamFifoUsageLow);
464 RX_STATS(bam_stats.bamUtilCount);
465 RX_STATS(num_bam_int_handled);
466 RX_STATS(num_db);
467 RX_STATS(num_unexpected_db);
468 RX_STATS(num_pkts_in_dis_uninit_state);
Utkarsh Saxena971a03c2017-01-22 22:04:13 +0530469 RX_STATS(num_ic_inj_vdev_change);
470 RX_STATS(num_ic_inj_fw_desc_change);
471 RX_STATS(num_qmb_int_handled);
Amir Levy9659e592016-10-27 18:08:27 +0300472 RX_STATS(reserved1);
473 RX_STATS(reserved2);
474
475 IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
476
477 return 0;
478}
479
480int ipa3_wdi_init(void)
481{
482 struct ipa3_uc_hdlrs uc_wdi_cbs = { 0 };
483
484 uc_wdi_cbs.ipa_uc_event_hdlr = ipa3_uc_wdi_event_handler;
485 uc_wdi_cbs.ipa_uc_event_log_info_hdlr =
486 ipa3_uc_wdi_event_log_info_handler;
487 uc_wdi_cbs.ipa_uc_loaded_hdlr =
488 ipa3_uc_wdi_loaded_handler;
489
490 ipa3_uc_register_handlers(IPA_HW_FEATURE_WDI, &uc_wdi_cbs);
491
492 return 0;
493}
494
495static int ipa_create_uc_smmu_mapping_pa(phys_addr_t pa, size_t len,
496 bool device, unsigned long *iova)
497{
Skylar Changefc0a0f2018-03-29 11:17:40 -0700498 struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_UC);
Amir Levy9659e592016-10-27 18:08:27 +0300499 unsigned long va = roundup(cb->next_addr, PAGE_SIZE);
500 int prot = IOMMU_READ | IOMMU_WRITE;
501 size_t true_len = roundup(len + pa - rounddown(pa, PAGE_SIZE),
502 PAGE_SIZE);
503 int ret;
504
505 if (!cb->valid) {
506 IPAERR("No SMMU CB setup\n");
507 return -EINVAL;
508 }
509
510 ret = ipa3_iommu_map(cb->mapping->domain, va, rounddown(pa, PAGE_SIZE),
511 true_len,
Amir Levyf5625342016-12-25 10:21:02 +0200512 device ? (prot | IOMMU_MMIO) : prot);
Amir Levy9659e592016-10-27 18:08:27 +0300513 if (ret) {
514 IPAERR("iommu map failed for pa=%pa len=%zu\n", &pa, true_len);
515 return -EINVAL;
516 }
517
518 ipa3_ctx->wdi_map_cnt++;
519 cb->next_addr = va + true_len;
520 *iova = va + pa - rounddown(pa, PAGE_SIZE);
521 return 0;
522}
523
524static int ipa_create_uc_smmu_mapping_sgt(struct sg_table *sgt,
525 unsigned long *iova)
526{
Skylar Changefc0a0f2018-03-29 11:17:40 -0700527 struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_UC);
Amir Levy9659e592016-10-27 18:08:27 +0300528 unsigned long va = roundup(cb->next_addr, PAGE_SIZE);
529 int prot = IOMMU_READ | IOMMU_WRITE;
530 int ret;
531 int i;
532 struct scatterlist *sg;
533 unsigned long start_iova = va;
534 phys_addr_t phys;
535 size_t len;
536 int count = 0;
537
538 if (!cb->valid) {
539 IPAERR("No SMMU CB setup\n");
540 return -EINVAL;
541 }
542 if (!sgt) {
543 IPAERR("Bad parameters, scatter / gather list is NULL\n");
544 return -EINVAL;
545 }
546
547 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
548 /* directly get sg_tbl PA from wlan-driver */
549 phys = sg->dma_address;
550 len = PAGE_ALIGN(sg->offset + sg->length);
551
552 ret = ipa3_iommu_map(cb->mapping->domain, va, phys, len, prot);
553 if (ret) {
554 IPAERR("iommu map failed for pa=%pa len=%zu\n",
555 &phys, len);
556 goto bad_mapping;
557 }
558 va += len;
559 ipa3_ctx->wdi_map_cnt++;
560 count++;
561 }
562 cb->next_addr = va;
563 *iova = start_iova;
564
565 return 0;
566
567bad_mapping:
568 for_each_sg(sgt->sgl, sg, count, i)
569 iommu_unmap(cb->mapping->domain, sg_dma_address(sg),
570 sg_dma_len(sg));
571 return -EINVAL;
572}
573
574static void ipa_release_uc_smmu_mappings(enum ipa_client_type client)
575{
Skylar Changefc0a0f2018-03-29 11:17:40 -0700576 struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_UC);
Amir Levy9659e592016-10-27 18:08:27 +0300577 int i;
578 int j;
579 int start;
580 int end;
581
582 if (IPA_CLIENT_IS_CONS(client)) {
583 start = IPA_WDI_TX_RING_RES;
584 end = IPA_WDI_CE_DB_RES;
585 } else {
586 start = IPA_WDI_RX_RING_RES;
587 if (ipa3_ctx->ipa_wdi2)
588 end = IPA_WDI_RX_COMP_RING_WP_RES;
589 else
590 end = IPA_WDI_RX_RING_RP_RES;
591 }
592
593 for (i = start; i <= end; i++) {
594 if (wdi_res[i].valid) {
595 for (j = 0; j < wdi_res[i].nents; j++) {
596 iommu_unmap(cb->mapping->domain,
597 wdi_res[i].res[j].iova,
598 wdi_res[i].res[j].size);
599 ipa3_ctx->wdi_map_cnt--;
600 }
601 kfree(wdi_res[i].res);
602 wdi_res[i].valid = false;
603 }
604 }
605
606 if (ipa3_ctx->wdi_map_cnt == 0)
607 cb->next_addr = cb->va_end;
608
609}
610
611static void ipa_save_uc_smmu_mapping_pa(int res_idx, phys_addr_t pa,
612 unsigned long iova, size_t len)
613{
614 IPADBG("--res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx,
Michael Adisumarta658afb82017-11-01 18:16:48 -0700615 &pa, iova, len);
616 wdi_res[res_idx].res = kzalloc(sizeof(*wdi_res[res_idx].res),
617 GFP_KERNEL);
Amir Levy9659e592016-10-27 18:08:27 +0300618 if (!wdi_res[res_idx].res)
619 BUG();
620 wdi_res[res_idx].nents = 1;
621 wdi_res[res_idx].valid = true;
622 wdi_res[res_idx].res->pa = rounddown(pa, PAGE_SIZE);
623 wdi_res[res_idx].res->iova = rounddown(iova, PAGE_SIZE);
624 wdi_res[res_idx].res->size = roundup(len + pa - rounddown(pa,
625 PAGE_SIZE), PAGE_SIZE);
626 IPADBG("res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx,
627 &wdi_res[res_idx].res->pa, wdi_res[res_idx].res->iova,
628 wdi_res[res_idx].res->size);
629}
630
631static void ipa_save_uc_smmu_mapping_sgt(int res_idx, struct sg_table *sgt,
632 unsigned long iova)
633{
634 int i;
635 struct scatterlist *sg;
636 unsigned long curr_iova = iova;
637
638 if (!sgt) {
639 IPAERR("Bad parameters, scatter / gather list is NULL\n");
640 return;
641 }
642
Michael Adisumarta658afb82017-11-01 18:16:48 -0700643 wdi_res[res_idx].res = kcalloc(sgt->nents,
644 sizeof(*wdi_res[res_idx].res),
Amir Levy9659e592016-10-27 18:08:27 +0300645 GFP_KERNEL);
646 if (!wdi_res[res_idx].res)
647 BUG();
648 wdi_res[res_idx].nents = sgt->nents;
649 wdi_res[res_idx].valid = true;
650 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
651 /* directly get sg_tbl PA from wlan */
652 wdi_res[res_idx].res[i].pa = sg->dma_address;
653 wdi_res[res_idx].res[i].iova = curr_iova;
654 wdi_res[res_idx].res[i].size = PAGE_ALIGN(sg->offset +
655 sg->length);
656 IPADBG("res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx,
657 &wdi_res[res_idx].res[i].pa,
658 wdi_res[res_idx].res[i].iova,
659 wdi_res[res_idx].res[i].size);
660 curr_iova += wdi_res[res_idx].res[i].size;
661 }
662}
663
Shihuan Liu105b31f2017-11-21 11:43:58 -0800664int ipa_create_uc_smmu_mapping(int res_idx, bool wlan_smmu_en,
Amir Levy9659e592016-10-27 18:08:27 +0300665 phys_addr_t pa, struct sg_table *sgt, size_t len, bool device,
666 unsigned long *iova)
667{
668 /* support for SMMU on WLAN but no SMMU on IPA */
Michael Adisumarta93e97522017-10-06 15:49:46 -0700669 if (wlan_smmu_en && ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
Amir Levy9659e592016-10-27 18:08:27 +0300670 IPAERR("Unsupported SMMU pairing\n");
671 return -EINVAL;
672 }
673
674 /* legacy: no SMMUs on either end */
Michael Adisumarta93e97522017-10-06 15:49:46 -0700675 if (!wlan_smmu_en && ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
Amir Levy9659e592016-10-27 18:08:27 +0300676 *iova = pa;
677 return 0;
678 }
679
680 /* no SMMU on WLAN but SMMU on IPA */
Michael Adisumarta93e97522017-10-06 15:49:46 -0700681 if (!wlan_smmu_en && !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
Amir Levy9659e592016-10-27 18:08:27 +0300682 if (ipa_create_uc_smmu_mapping_pa(pa, len,
683 (res_idx == IPA_WDI_CE_DB_RES) ? true : false, iova)) {
684 IPAERR("Fail to create mapping res %d\n", res_idx);
685 return -EFAULT;
686 }
687 ipa_save_uc_smmu_mapping_pa(res_idx, pa, *iova, len);
688 return 0;
689 }
690
691 /* SMMU on WLAN and SMMU on IPA */
Michael Adisumarta93e97522017-10-06 15:49:46 -0700692 if (wlan_smmu_en && !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
Amir Levy9659e592016-10-27 18:08:27 +0300693 switch (res_idx) {
694 case IPA_WDI_RX_RING_RP_RES:
695 case IPA_WDI_RX_COMP_RING_WP_RES:
696 case IPA_WDI_CE_DB_RES:
Shihuan Liu105b31f2017-11-21 11:43:58 -0800697 case IPA_WDI_TX_DB_RES:
Amir Levy9659e592016-10-27 18:08:27 +0300698 if (ipa_create_uc_smmu_mapping_pa(pa, len,
699 (res_idx == IPA_WDI_CE_DB_RES) ? true : false,
700 iova)) {
701 IPAERR("Fail to create mapping res %d\n",
702 res_idx);
703 return -EFAULT;
704 }
705 ipa_save_uc_smmu_mapping_pa(res_idx, pa, *iova, len);
706 break;
707 case IPA_WDI_RX_RING_RES:
708 case IPA_WDI_RX_COMP_RING_RES:
709 case IPA_WDI_TX_RING_RES:
710 case IPA_WDI_CE_RING_RES:
711 if (ipa_create_uc_smmu_mapping_sgt(sgt, iova)) {
712 IPAERR("Fail to create mapping res %d\n",
713 res_idx);
714 return -EFAULT;
715 }
716 ipa_save_uc_smmu_mapping_sgt(res_idx, sgt, *iova);
717 break;
718 default:
719 BUG();
720 }
721 }
722
723 return 0;
724}
725
726/**
727 * ipa3_connect_wdi_pipe() - WDI client connect
728 * @in: [in] input parameters from client
729 * @out: [out] output params to client
730 *
731 * Returns: 0 on success, negative on failure
732 *
733 * Note: Should not be called from atomic context
734 */
735int ipa3_connect_wdi_pipe(struct ipa_wdi_in_params *in,
736 struct ipa_wdi_out_params *out)
737{
738 int ipa_ep_idx;
739 int result = -EFAULT;
740 struct ipa3_ep_context *ep;
741 struct ipa_mem_buffer cmd;
742 struct IpaHwWdiTxSetUpCmdData_t *tx;
743 struct IpaHwWdiRxSetUpCmdData_t *rx;
744 struct IpaHwWdi2TxSetUpCmdData_t *tx_2;
745 struct IpaHwWdi2RxSetUpCmdData_t *rx_2;
746
747 struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
748 unsigned long va;
749 phys_addr_t pa;
750 u32 len;
751
752 if (in == NULL || out == NULL || in->sys.client >= IPA_CLIENT_MAX) {
753 IPAERR("bad parm. in=%p out=%p\n", in, out);
754 if (in)
755 IPAERR("client = %d\n", in->sys.client);
756 return -EINVAL;
757 }
758
759 if (IPA_CLIENT_IS_CONS(in->sys.client)) {
760 if (in->u.dl.comp_ring_base_pa % IPA_WDI_RING_ALIGNMENT ||
761 in->u.dl.ce_ring_base_pa % IPA_WDI_RING_ALIGNMENT) {
762 IPAERR("alignment failure on TX\n");
763 return -EINVAL;
764 }
765 } else {
766 if (in->u.ul.rdy_ring_base_pa % IPA_WDI_RING_ALIGNMENT) {
767 IPAERR("alignment failure on RX\n");
768 return -EINVAL;
769 }
770 }
771
772 result = ipa3_uc_state_check();
773 if (result)
774 return result;
775
776 ipa_ep_idx = ipa3_get_ep_mapping(in->sys.client);
777 if (ipa_ep_idx == -1) {
778 IPAERR("fail to alloc EP.\n");
779 goto fail;
780 }
781
782 ep = &ipa3_ctx->ep[ipa_ep_idx];
783
784 if (ep->valid) {
785 IPAERR("EP already allocated.\n");
786 goto fail;
787 }
788
789 memset(&ipa3_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa3_ep_context));
790 IPA_ACTIVE_CLIENTS_INC_EP(in->sys.client);
791
792 IPADBG("client=%d ep=%d\n", in->sys.client, ipa_ep_idx);
793 if (IPA_CLIENT_IS_CONS(in->sys.client)) {
794 if (ipa3_ctx->ipa_wdi2)
795 cmd.size = sizeof(*tx_2);
796 else
797 cmd.size = sizeof(*tx);
798 IPADBG("comp_ring_base_pa=0x%pa\n",
799 &in->u.dl.comp_ring_base_pa);
800 IPADBG("comp_ring_size=%d\n", in->u.dl.comp_ring_size);
801 IPADBG("ce_ring_base_pa=0x%pa\n", &in->u.dl.ce_ring_base_pa);
802 IPADBG("ce_ring_size=%d\n", in->u.dl.ce_ring_size);
803 IPADBG("ce_ring_doorbell_pa=0x%pa\n",
804 &in->u.dl.ce_door_bell_pa);
805 IPADBG("num_tx_buffers=%d\n", in->u.dl.num_tx_buffers);
806 } else {
807 if (ipa3_ctx->ipa_wdi2)
808 cmd.size = sizeof(*rx_2);
809 else
810 cmd.size = sizeof(*rx);
811 IPADBG("rx_ring_base_pa=0x%pa\n",
812 &in->u.ul.rdy_ring_base_pa);
813 IPADBG("rx_ring_size=%d\n",
814 in->u.ul.rdy_ring_size);
815 IPADBG("rx_ring_rp_pa=0x%pa\n",
816 &in->u.ul.rdy_ring_rp_pa);
817 IPADBG("rx_comp_ring_base_pa=0x%pa\n",
818 &in->u.ul.rdy_comp_ring_base_pa);
819 IPADBG("rx_comp_ring_size=%d\n",
820 in->u.ul.rdy_comp_ring_size);
821 IPADBG("rx_comp_ring_wp_pa=0x%pa\n",
822 &in->u.ul.rdy_comp_ring_wp_pa);
823 ipa3_ctx->uc_ctx.rdy_ring_base_pa =
824 in->u.ul.rdy_ring_base_pa;
825 ipa3_ctx->uc_ctx.rdy_ring_rp_pa =
826 in->u.ul.rdy_ring_rp_pa;
827 ipa3_ctx->uc_ctx.rdy_ring_size =
828 in->u.ul.rdy_ring_size;
829 ipa3_ctx->uc_ctx.rdy_comp_ring_base_pa =
830 in->u.ul.rdy_comp_ring_base_pa;
831 ipa3_ctx->uc_ctx.rdy_comp_ring_wp_pa =
832 in->u.ul.rdy_comp_ring_wp_pa;
833 ipa3_ctx->uc_ctx.rdy_comp_ring_size =
834 in->u.ul.rdy_comp_ring_size;
Amir Levy9659e592016-10-27 18:08:27 +0300835 }
836
837 cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
838 &cmd.phys_base, GFP_KERNEL);
839 if (cmd.base == NULL) {
840 IPAERR("fail to get DMA memory.\n");
841 result = -ENOMEM;
842 goto dma_alloc_fail;
843 }
844
845 if (IPA_CLIENT_IS_CONS(in->sys.client)) {
846 if (ipa3_ctx->ipa_wdi2) {
847 tx_2 = (struct IpaHwWdi2TxSetUpCmdData_t *)cmd.base;
848
849 len = in->smmu_enabled ? in->u.dl_smmu.comp_ring_size :
850 in->u.dl.comp_ring_size;
851 IPADBG("TX_2 ring smmu_en=%d ring_size=%d %d\n",
852 in->smmu_enabled,
853 in->u.dl_smmu.comp_ring_size,
854 in->u.dl.comp_ring_size);
855 if (ipa_create_uc_smmu_mapping(IPA_WDI_TX_RING_RES,
856 in->smmu_enabled,
857 in->u.dl.comp_ring_base_pa,
858 &in->u.dl_smmu.comp_ring,
859 len,
860 false,
861 &va)) {
862 IPAERR("fail to create uc mapping TX ring.\n");
863 result = -ENOMEM;
864 goto uc_timeout;
865 }
866 tx_2->comp_ring_base_pa_hi =
867 (u32) ((va & 0xFFFFFFFF00000000) >> 32);
868 tx_2->comp_ring_base_pa = (u32) (va & 0xFFFFFFFF);
869 tx_2->comp_ring_size = len;
870 IPADBG("TX_2 comp_ring_base_pa_hi=0x%08x :0x%08x\n",
871 tx_2->comp_ring_base_pa_hi,
872 tx_2->comp_ring_base_pa);
873
874 len = in->smmu_enabled ? in->u.dl_smmu.ce_ring_size :
875 in->u.dl.ce_ring_size;
876 IPADBG("TX_2 CE ring smmu_en=%d ring_size=%d %d\n",
877 in->smmu_enabled,
878 in->u.dl_smmu.ce_ring_size,
879 in->u.dl.ce_ring_size);
880 /* WA: wlan passed ce_ring sg_table PA directly */
881 if (ipa_create_uc_smmu_mapping(IPA_WDI_CE_RING_RES,
882 in->smmu_enabled,
883 in->u.dl.ce_ring_base_pa,
884 &in->u.dl_smmu.ce_ring,
885 len,
886 false,
887 &va)) {
888 IPAERR("fail to create uc mapping CE ring.\n");
889 result = -ENOMEM;
890 goto uc_timeout;
891 }
892 tx_2->ce_ring_base_pa_hi =
893 (u32) ((va & 0xFFFFFFFF00000000) >> 32);
894 tx_2->ce_ring_base_pa = (u32) (va & 0xFFFFFFFF);
895 tx_2->ce_ring_size = len;
896 IPADBG("TX_2 ce_ring_base_pa_hi=0x%08x :0x%08x\n",
897 tx_2->ce_ring_base_pa_hi,
898 tx_2->ce_ring_base_pa);
899
900 pa = in->smmu_enabled ? in->u.dl_smmu.ce_door_bell_pa :
901 in->u.dl.ce_door_bell_pa;
902 if (ipa_create_uc_smmu_mapping(IPA_WDI_CE_DB_RES,
903 in->smmu_enabled,
904 pa,
905 NULL,
906 4,
907 true,
908 &va)) {
909 IPAERR("fail to create uc mapping CE DB.\n");
910 result = -ENOMEM;
911 goto uc_timeout;
912 }
913 tx_2->ce_ring_doorbell_pa_hi =
914 (u32) ((va & 0xFFFFFFFF00000000) >> 32);
915 tx_2->ce_ring_doorbell_pa = (u32) (va & 0xFFFFFFFF);
916 IPADBG("TX_2 ce_ring_doorbell_pa_hi=0x%08x :0x%08x\n",
917 tx_2->ce_ring_doorbell_pa_hi,
918 tx_2->ce_ring_doorbell_pa);
919
920 tx_2->num_tx_buffers = in->smmu_enabled ?
921 in->u.dl_smmu.num_tx_buffers :
922 in->u.dl.num_tx_buffers;
923 tx_2->ipa_pipe_number = ipa_ep_idx;
924 } else {
925 tx = (struct IpaHwWdiTxSetUpCmdData_t *)cmd.base;
926
927 len = in->smmu_enabled ? in->u.dl_smmu.comp_ring_size :
928 in->u.dl.comp_ring_size;
929 IPADBG("TX ring smmu_en=%d ring_size=%d %d\n",
930 in->smmu_enabled,
931 in->u.dl_smmu.comp_ring_size,
932 in->u.dl.comp_ring_size);
933 if (ipa_create_uc_smmu_mapping(IPA_WDI_TX_RING_RES,
934 in->smmu_enabled,
935 in->u.dl.comp_ring_base_pa,
936 &in->u.dl_smmu.comp_ring,
937 len,
938 false,
939 &va)) {
940 IPAERR("fail to create uc mapping TX ring.\n");
941 result = -ENOMEM;
942 goto uc_timeout;
943 }
944 tx->comp_ring_base_pa = va;
945 tx->comp_ring_size = len;
946 len = in->smmu_enabled ? in->u.dl_smmu.ce_ring_size :
947 in->u.dl.ce_ring_size;
948 IPADBG("TX CE ring smmu_en=%d ring_size=%d %d\n",
949 in->smmu_enabled,
950 in->u.dl_smmu.ce_ring_size,
951 in->u.dl.ce_ring_size);
952 if (ipa_create_uc_smmu_mapping(IPA_WDI_CE_RING_RES,
953 in->smmu_enabled,
954 in->u.dl.ce_ring_base_pa,
955 &in->u.dl_smmu.ce_ring,
956 len,
957 false,
958 &va)) {
959 IPAERR("fail to create uc mapping CE ring.\n");
960 result = -ENOMEM;
961 goto uc_timeout;
962 }
963 tx->ce_ring_base_pa = va;
964 tx->ce_ring_size = len;
965 pa = in->smmu_enabled ? in->u.dl_smmu.ce_door_bell_pa :
966 in->u.dl.ce_door_bell_pa;
967 if (ipa_create_uc_smmu_mapping(IPA_WDI_CE_DB_RES,
968 in->smmu_enabled,
969 pa,
970 NULL,
971 4,
972 true,
973 &va)) {
974 IPAERR("fail to create uc mapping CE DB.\n");
975 result = -ENOMEM;
976 goto uc_timeout;
977 }
978 tx->ce_ring_doorbell_pa = va;
979 tx->num_tx_buffers = in->u.dl.num_tx_buffers;
980 tx->ipa_pipe_number = ipa_ep_idx;
981 }
982 out->uc_door_bell_pa = ipa3_ctx->ipa_wrapper_base +
983 ipahal_get_reg_base() +
984 ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n,
985 IPA_HW_WDI_TX_MBOX_START_INDEX/32,
986 IPA_HW_WDI_TX_MBOX_START_INDEX % 32);
987 } else {
988 if (ipa3_ctx->ipa_wdi2) {
989 rx_2 = (struct IpaHwWdi2RxSetUpCmdData_t *)cmd.base;
990
991 len = in->smmu_enabled ? in->u.ul_smmu.rdy_ring_size :
992 in->u.ul.rdy_ring_size;
993 IPADBG("RX_2 ring smmu_en=%d ring_size=%d %d\n",
994 in->smmu_enabled,
995 in->u.ul_smmu.rdy_ring_size,
996 in->u.ul.rdy_ring_size);
997 if (ipa_create_uc_smmu_mapping(IPA_WDI_RX_RING_RES,
998 in->smmu_enabled,
999 in->u.ul.rdy_ring_base_pa,
1000 &in->u.ul_smmu.rdy_ring,
1001 len,
1002 false,
1003 &va)) {
1004 IPAERR("fail to create uc RX_2 ring.\n");
1005 result = -ENOMEM;
1006 goto uc_timeout;
1007 }
1008 rx_2->rx_ring_base_pa_hi =
1009 (u32) ((va & 0xFFFFFFFF00000000) >> 32);
1010 rx_2->rx_ring_base_pa = (u32) (va & 0xFFFFFFFF);
1011 rx_2->rx_ring_size = len;
1012 IPADBG("RX_2 rx_ring_base_pa_hi=0x%08x:0x%08x\n",
1013 rx_2->rx_ring_base_pa_hi,
1014 rx_2->rx_ring_base_pa);
1015
1016 pa = in->smmu_enabled ? in->u.ul_smmu.rdy_ring_rp_pa :
1017 in->u.ul.rdy_ring_rp_pa;
1018 if (ipa_create_uc_smmu_mapping(IPA_WDI_RX_RING_RP_RES,
1019 in->smmu_enabled,
1020 pa,
1021 NULL,
1022 4,
1023 false,
1024 &va)) {
1025 IPAERR("fail to create uc RX_2 rng RP\n");
1026 result = -ENOMEM;
1027 goto uc_timeout;
1028 }
1029 rx_2->rx_ring_rp_pa_hi =
1030 (u32) ((va & 0xFFFFFFFF00000000) >> 32);
1031 rx_2->rx_ring_rp_pa = (u32) (va & 0xFFFFFFFF);
1032 IPADBG("RX_2 rx_ring_rp_pa_hi=0x%08x :0x%08x\n",
1033 rx_2->rx_ring_rp_pa_hi,
1034 rx_2->rx_ring_rp_pa);
1035 len = in->smmu_enabled ?
1036 in->u.ul_smmu.rdy_comp_ring_size :
1037 in->u.ul.rdy_comp_ring_size;
1038 IPADBG("RX_2 ring smmu_en=%d comp_ring_size=%d %d\n",
1039 in->smmu_enabled,
1040 in->u.ul_smmu.rdy_comp_ring_size,
1041 in->u.ul.rdy_comp_ring_size);
1042 if (ipa_create_uc_smmu_mapping(IPA_WDI_RX_COMP_RING_RES,
1043 in->smmu_enabled,
1044 in->u.ul.rdy_comp_ring_base_pa,
1045 &in->u.ul_smmu.rdy_comp_ring,
1046 len,
1047 false,
1048 &va)) {
1049 IPAERR("fail to create uc RX_2 comp_ring.\n");
1050 result = -ENOMEM;
1051 goto uc_timeout;
1052 }
1053 rx_2->rx_comp_ring_base_pa_hi =
1054 (u32) ((va & 0xFFFFFFFF00000000) >> 32);
1055 rx_2->rx_comp_ring_base_pa = (u32) (va & 0xFFFFFFFF);
1056 rx_2->rx_comp_ring_size = len;
1057 IPADBG("RX_2 rx_comp_ring_base_pa_hi=0x%08x:0x%08x\n",
1058 rx_2->rx_comp_ring_base_pa_hi,
1059 rx_2->rx_comp_ring_base_pa);
1060
1061 pa = in->smmu_enabled ?
1062 in->u.ul_smmu.rdy_comp_ring_wp_pa :
1063 in->u.ul.rdy_comp_ring_wp_pa;
1064 if (ipa_create_uc_smmu_mapping(
1065 IPA_WDI_RX_COMP_RING_WP_RES,
1066 in->smmu_enabled,
1067 pa,
1068 NULL,
1069 4,
1070 false,
1071 &va)) {
1072 IPAERR("fail to create uc RX_2 comp_rng WP\n");
1073 result = -ENOMEM;
1074 goto uc_timeout;
1075 }
1076 rx_2->rx_comp_ring_wp_pa_hi =
1077 (u32) ((va & 0xFFFFFFFF00000000) >> 32);
1078 rx_2->rx_comp_ring_wp_pa = (u32) (va & 0xFFFFFFFF);
1079 IPADBG("RX_2 rx_comp_ring_wp_pa_hi=0x%08x:0x%08x\n",
1080 rx_2->rx_comp_ring_wp_pa_hi,
1081 rx_2->rx_comp_ring_wp_pa);
1082 rx_2->ipa_pipe_number = ipa_ep_idx;
1083 } else {
1084 rx = (struct IpaHwWdiRxSetUpCmdData_t *)cmd.base;
1085
1086 len = in->smmu_enabled ? in->u.ul_smmu.rdy_ring_size :
1087 in->u.ul.rdy_ring_size;
1088 IPADBG("RX ring smmu_en=%d ring_size=%d %d\n",
1089 in->smmu_enabled,
1090 in->u.ul_smmu.rdy_ring_size,
1091 in->u.ul.rdy_ring_size);
1092 if (ipa_create_uc_smmu_mapping(IPA_WDI_RX_RING_RES,
1093 in->smmu_enabled,
1094 in->u.ul.rdy_ring_base_pa,
1095 &in->u.ul_smmu.rdy_ring,
1096 len,
1097 false,
1098 &va)) {
1099 IPAERR("fail to create uc mapping RX ring.\n");
1100 result = -ENOMEM;
1101 goto uc_timeout;
1102 }
1103 rx->rx_ring_base_pa = va;
1104 rx->rx_ring_size = len;
1105
1106 pa = in->smmu_enabled ? in->u.ul_smmu.rdy_ring_rp_pa :
1107 in->u.ul.rdy_ring_rp_pa;
1108 if (ipa_create_uc_smmu_mapping(IPA_WDI_RX_RING_RP_RES,
1109 in->smmu_enabled,
1110 pa,
1111 NULL,
1112 4,
1113 false,
1114 &va)) {
1115 IPAERR("fail to create uc mapping RX rng RP\n");
1116 result = -ENOMEM;
1117 goto uc_timeout;
1118 }
1119 rx->rx_ring_rp_pa = va;
1120 rx->ipa_pipe_number = ipa_ep_idx;
1121 }
1122 out->uc_door_bell_pa = ipa3_ctx->ipa_wrapper_base +
1123 ipahal_get_reg_base() +
1124 ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n,
1125 IPA_HW_WDI_RX_MBOX_START_INDEX/32,
1126 IPA_HW_WDI_RX_MBOX_START_INDEX % 32);
1127 }
1128
1129 ep->valid = 1;
1130 ep->client = in->sys.client;
1131 ep->keep_ipa_awake = in->sys.keep_ipa_awake;
1132 result = ipa3_disable_data_path(ipa_ep_idx);
1133 if (result) {
1134 IPAERR("disable data path failed res=%d clnt=%d.\n", result,
1135 ipa_ep_idx);
1136 goto uc_timeout;
1137 }
1138 if (IPA_CLIENT_IS_PROD(in->sys.client)) {
1139 memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
1140 ep_cfg_ctrl.ipa_ep_delay = true;
1141 ipa3_cfg_ep_ctrl(ipa_ep_idx, &ep_cfg_ctrl);
1142 }
1143
1144 result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
1145 IPA_CLIENT_IS_CONS(in->sys.client) ?
1146 IPA_CPU_2_HW_CMD_WDI_TX_SET_UP :
1147 IPA_CPU_2_HW_CMD_WDI_RX_SET_UP,
1148 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
1149 false, 10*HZ);
1150
1151 if (result) {
1152 result = -EFAULT;
1153 goto uc_timeout;
1154 }
1155
1156 ep->skip_ep_cfg = in->sys.skip_ep_cfg;
1157 ep->client_notify = in->sys.notify;
1158 ep->priv = in->sys.priv;
1159
Skylar Chang6b41f8d2016-11-01 12:50:11 -07001160 /* for AP+STA stats update */
1161 if (in->wdi_notify)
1162 ipa3_ctx->uc_wdi_ctx.stats_notify = in->wdi_notify;
1163 else
1164 IPADBG("in->wdi_notify is null\n");
1165
Amir Levy9659e592016-10-27 18:08:27 +03001166 if (!ep->skip_ep_cfg) {
1167 if (ipa3_cfg_ep(ipa_ep_idx, &in->sys.ipa_ep_cfg)) {
1168 IPAERR("fail to configure EP.\n");
1169 goto ipa_cfg_ep_fail;
1170 }
1171 IPADBG("ep configuration successful\n");
1172 } else {
1173 IPADBG("Skipping endpoint configuration.\n");
1174 }
1175
1176 out->clnt_hdl = ipa_ep_idx;
1177
1178 if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys.client))
1179 ipa3_install_dflt_flt_rules(ipa_ep_idx);
1180
1181 if (!ep->keep_ipa_awake)
1182 IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
1183
1184 dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
1185 ep->uc_offload_state |= IPA_WDI_CONNECTED;
1186 IPADBG("client %d (ep: %d) connected\n", in->sys.client, ipa_ep_idx);
1187
1188 return 0;
1189
1190ipa_cfg_ep_fail:
1191 memset(&ipa3_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa3_ep_context));
1192uc_timeout:
1193 ipa_release_uc_smmu_mappings(in->sys.client);
1194 dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
1195dma_alloc_fail:
1196 IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
1197fail:
1198 return result;
1199}
1200
1201/**
1202 * ipa3_disconnect_wdi_pipe() - WDI client disconnect
1203 * @clnt_hdl: [in] opaque client handle assigned by IPA to client
1204 *
1205 * Returns: 0 on success, negative on failure
1206 *
1207 * Note: Should not be called from atomic context
1208 */
1209int ipa3_disconnect_wdi_pipe(u32 clnt_hdl)
1210{
1211 int result = 0;
1212 struct ipa3_ep_context *ep;
1213 union IpaHwWdiCommonChCmdData_t tear;
1214
1215 if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
1216 ipa3_ctx->ep[clnt_hdl].valid == 0) {
1217 IPAERR("bad parm, %d\n", clnt_hdl);
1218 return -EINVAL;
1219 }
1220
1221 result = ipa3_uc_state_check();
1222 if (result)
1223 return result;
1224
1225 IPADBG("ep=%d\n", clnt_hdl);
1226
1227 ep = &ipa3_ctx->ep[clnt_hdl];
1228
1229 if (ep->uc_offload_state != IPA_WDI_CONNECTED) {
1230 IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
1231 return -EFAULT;
1232 }
1233
1234 if (!ep->keep_ipa_awake)
1235 IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
1236
1237 tear.params.ipa_pipe_number = clnt_hdl;
1238
1239 result = ipa3_uc_send_cmd(tear.raw32b,
1240 IPA_CPU_2_HW_CMD_WDI_TEAR_DOWN,
1241 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
1242 false, 10*HZ);
1243
1244 if (result) {
1245 result = -EFAULT;
1246 goto uc_timeout;
1247 }
1248
1249 ipa3_delete_dflt_flt_rules(clnt_hdl);
1250 ipa_release_uc_smmu_mappings(ep->client);
1251
1252 memset(&ipa3_ctx->ep[clnt_hdl], 0, sizeof(struct ipa3_ep_context));
1253 IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
1254
1255 IPADBG("client (ep: %d) disconnected\n", clnt_hdl);
1256
Skylar Chang6b41f8d2016-11-01 12:50:11 -07001257 /* for AP+STA stats update */
1258 if (ipa3_ctx->uc_wdi_ctx.stats_notify)
1259 ipa3_ctx->uc_wdi_ctx.stats_notify = NULL;
1260 else
1261 IPADBG("uc_wdi_ctx.stats_notify already null\n");
1262
Amir Levy9659e592016-10-27 18:08:27 +03001263uc_timeout:
1264 return result;
1265}
1266
1267/**
1268 * ipa3_enable_wdi_pipe() - WDI client enable
1269 * @clnt_hdl: [in] opaque client handle assigned by IPA to client
1270 *
1271 * Returns: 0 on success, negative on failure
1272 *
1273 * Note: Should not be called from atomic context
1274 */
1275int ipa3_enable_wdi_pipe(u32 clnt_hdl)
1276{
1277 int result = 0;
1278 struct ipa3_ep_context *ep;
1279 union IpaHwWdiCommonChCmdData_t enable;
1280 struct ipa_ep_cfg_holb holb_cfg;
Skylar Changc958bdd2018-01-25 17:10:41 -08001281 struct ipahal_reg_endp_init_rsrc_grp rsrc_grp;
Amir Levy9659e592016-10-27 18:08:27 +03001282
1283 if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
1284 ipa3_ctx->ep[clnt_hdl].valid == 0) {
1285 IPAERR("bad parm, %d\n", clnt_hdl);
1286 return -EINVAL;
1287 }
1288
1289 result = ipa3_uc_state_check();
1290 if (result)
1291 return result;
1292
1293 IPADBG("ep=%d\n", clnt_hdl);
1294
1295 ep = &ipa3_ctx->ep[clnt_hdl];
1296
1297 if (ep->uc_offload_state != IPA_WDI_CONNECTED) {
1298 IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
1299 return -EFAULT;
1300 }
1301 IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
1302 enable.params.ipa_pipe_number = clnt_hdl;
1303
1304 result = ipa3_uc_send_cmd(enable.raw32b,
1305 IPA_CPU_2_HW_CMD_WDI_CH_ENABLE,
1306 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
1307 false, 10*HZ);
1308
1309 if (result) {
1310 result = -EFAULT;
1311 goto uc_timeout;
1312 }
1313
Skylar Changc958bdd2018-01-25 17:10:41 -08001314 /* Assign the resource group for pipe */
1315 memset(&rsrc_grp, 0, sizeof(rsrc_grp));
1316 rsrc_grp.rsrc_grp = ipa_get_ep_group(ep->client);
1317 if (rsrc_grp.rsrc_grp == -1) {
1318 IPAERR("invalid group for client %d\n", ep->client);
1319 WARN_ON(1);
1320 return -EFAULT;
1321 }
1322
1323 IPADBG("Setting group %d for pipe %d\n",
1324 rsrc_grp.rsrc_grp, clnt_hdl);
1325 ipahal_write_reg_n_fields(IPA_ENDP_INIT_RSRC_GRP_n, clnt_hdl,
1326 &rsrc_grp);
1327
Amir Levy9659e592016-10-27 18:08:27 +03001328 if (IPA_CLIENT_IS_CONS(ep->client)) {
1329 memset(&holb_cfg, 0, sizeof(holb_cfg));
1330 holb_cfg.en = IPA_HOLB_TMR_DIS;
1331 holb_cfg.tmr_val = 0;
1332 result = ipa3_cfg_ep_holb(clnt_hdl, &holb_cfg);
1333 }
1334 IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
1335 ep->uc_offload_state |= IPA_WDI_ENABLED;
1336 IPADBG("client (ep: %d) enabled\n", clnt_hdl);
1337
1338uc_timeout:
1339 return result;
1340}
1341
1342/**
1343 * ipa3_disable_wdi_pipe() - WDI client disable
1344 * @clnt_hdl: [in] opaque client handle assigned by IPA to client
1345 *
1346 * Returns: 0 on success, negative on failure
1347 *
1348 * Note: Should not be called from atomic context
1349 */
1350int ipa3_disable_wdi_pipe(u32 clnt_hdl)
1351{
1352 int result = 0;
1353 struct ipa3_ep_context *ep;
1354 union IpaHwWdiCommonChCmdData_t disable;
1355 struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
1356 u32 prod_hdl;
Skylar Chang1be75e1b2017-01-23 13:53:56 -08001357
Amir Levy9659e592016-10-27 18:08:27 +03001358 if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
1359 ipa3_ctx->ep[clnt_hdl].valid == 0) {
1360 IPAERR("bad parm, %d\n", clnt_hdl);
1361 return -EINVAL;
1362 }
1363
1364 result = ipa3_uc_state_check();
1365 if (result)
1366 return result;
1367
Amir Levy9659e592016-10-27 18:08:27 +03001368 IPADBG("ep=%d\n", clnt_hdl);
1369
1370 ep = &ipa3_ctx->ep[clnt_hdl];
1371
1372 if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
1373 IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
1374 return -EFAULT;
1375 }
1376 IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
1377
1378 result = ipa3_disable_data_path(clnt_hdl);
1379 if (result) {
1380 IPAERR("disable data path failed res=%d clnt=%d.\n", result,
1381 clnt_hdl);
1382 result = -EPERM;
1383 goto uc_timeout;
1384 }
1385
1386 /**
1387 * To avoid data stall during continuous SAP on/off before
1388 * setting delay to IPA Consumer pipe, remove delay and enable
1389 * holb on IPA Producer pipe
1390 */
1391 if (IPA_CLIENT_IS_PROD(ep->client)) {
Skylar Change834a9e612016-12-22 14:20:32 -08001392 IPADBG("Stopping PROD channel - hdl=%d clnt=%d\n",
1393 clnt_hdl, ep->client);
Skylar Change834a9e612016-12-22 14:20:32 -08001394 /* remove delay on wlan-prod pipe*/
Amir Levy9659e592016-10-27 18:08:27 +03001395 memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
1396 ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
1397
1398 prod_hdl = ipa3_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
1399 if (ipa3_ctx->ep[prod_hdl].valid == 1) {
1400 result = ipa3_disable_data_path(prod_hdl);
1401 if (result) {
1402 IPAERR("disable data path failed\n");
1403 IPAERR("res=%d clnt=%d\n",
1404 result, prod_hdl);
1405 result = -EPERM;
1406 goto uc_timeout;
1407 }
1408 }
1409 usleep_range(IPA_UC_POLL_SLEEP_USEC * IPA_UC_POLL_SLEEP_USEC,
1410 IPA_UC_POLL_SLEEP_USEC * IPA_UC_POLL_SLEEP_USEC);
Skylar Changc71b3c02016-11-29 13:36:24 -08001411
Amir Levy9659e592016-10-27 18:08:27 +03001412 }
1413
1414 disable.params.ipa_pipe_number = clnt_hdl;
Amir Levy9659e592016-10-27 18:08:27 +03001415 result = ipa3_uc_send_cmd(disable.raw32b,
1416 IPA_CPU_2_HW_CMD_WDI_CH_DISABLE,
1417 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
1418 false, 10*HZ);
1419
1420 if (result) {
1421 result = -EFAULT;
1422 goto uc_timeout;
1423 }
1424
1425 /* Set the delay after disabling IPA Producer pipe */
1426 if (IPA_CLIENT_IS_PROD(ep->client)) {
1427 memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
1428 ep_cfg_ctrl.ipa_ep_delay = true;
1429 ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
1430 }
1431 IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
1432 ep->uc_offload_state &= ~IPA_WDI_ENABLED;
1433 IPADBG("client (ep: %d) disabled\n", clnt_hdl);
1434
Skylar Change834a9e612016-12-22 14:20:32 -08001435
Amir Levy9659e592016-10-27 18:08:27 +03001436uc_timeout:
1437 return result;
1438}
1439
1440/**
1441 * ipa3_resume_wdi_pipe() - WDI client resume
1442 * @clnt_hdl: [in] opaque client handle assigned by IPA to client
1443 *
1444 * Returns: 0 on success, negative on failure
1445 *
1446 * Note: Should not be called from atomic context
1447 */
1448int ipa3_resume_wdi_pipe(u32 clnt_hdl)
1449{
1450 int result = 0;
1451 struct ipa3_ep_context *ep;
1452 union IpaHwWdiCommonChCmdData_t resume;
1453 struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
1454
1455 if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
1456 ipa3_ctx->ep[clnt_hdl].valid == 0) {
1457 IPAERR("bad parm, %d\n", clnt_hdl);
1458 return -EINVAL;
1459 }
1460
1461 result = ipa3_uc_state_check();
1462 if (result)
1463 return result;
1464
1465 IPADBG("ep=%d\n", clnt_hdl);
1466
1467 ep = &ipa3_ctx->ep[clnt_hdl];
1468
1469 if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
1470 IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
1471 return -EFAULT;
1472 }
1473 IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
1474 resume.params.ipa_pipe_number = clnt_hdl;
1475
1476 result = ipa3_uc_send_cmd(resume.raw32b,
1477 IPA_CPU_2_HW_CMD_WDI_CH_RESUME,
1478 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
1479 false, 10*HZ);
1480
1481 if (result) {
1482 result = -EFAULT;
1483 goto uc_timeout;
1484 }
1485
1486 memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
1487 result = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
1488 if (result)
1489 IPAERR("client (ep: %d) fail un-susp/delay result=%d\n",
1490 clnt_hdl, result);
1491 else
1492 IPADBG("client (ep: %d) un-susp/delay\n", clnt_hdl);
1493
1494 ep->uc_offload_state |= IPA_WDI_RESUMED;
1495 IPADBG("client (ep: %d) resumed\n", clnt_hdl);
1496
1497uc_timeout:
1498 return result;
1499}
1500
Mohammed Javiddb111a32018-02-22 15:16:47 +05301501static void ipa3_cfg_holb_wdi_consumer(bool is_enable)
1502{
1503 u32 clnt_hdl;
1504 struct ipa_ep_cfg_holb holb_cfg;
1505
1506 clnt_hdl = ipa3_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
1507 if (clnt_hdl < ipa3_ctx->ipa_num_pipes &&
1508 ipa3_ctx->ep[clnt_hdl].valid == 1) {
1509 memset(&holb_cfg, 0, sizeof(holb_cfg));
1510 if (is_enable)
1511 holb_cfg.en = IPA_HOLB_TMR_EN;
1512 else
1513 holb_cfg.en = IPA_HOLB_TMR_DIS;
1514 holb_cfg.tmr_val = 0;
1515 ipa3_cfg_ep_holb(clnt_hdl, &holb_cfg);
1516 }
1517}
1518
Amir Levy9659e592016-10-27 18:08:27 +03001519/**
1520 * ipa3_suspend_wdi_pipe() - WDI client suspend
1521 * @clnt_hdl: [in] opaque client handle assigned by IPA to client
1522 *
1523 * Returns: 0 on success, negative on failure
1524 *
1525 * Note: Should not be called from atomic context
1526 */
1527int ipa3_suspend_wdi_pipe(u32 clnt_hdl)
1528{
1529 int result = 0;
1530 struct ipa3_ep_context *ep;
1531 union IpaHwWdiCommonChCmdData_t suspend;
1532 struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
Skylar Chang019df562017-03-08 10:45:45 -08001533 u32 source_pipe_bitmask = 0;
1534 bool disable_force_clear = false;
1535 struct ipahal_ep_cfg_ctrl_scnd ep_ctrl_scnd = { 0 };
Amir Levy9659e592016-10-27 18:08:27 +03001536
1537 if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
1538 ipa3_ctx->ep[clnt_hdl].valid == 0) {
1539 IPAERR("bad parm, %d\n", clnt_hdl);
1540 return -EINVAL;
1541 }
1542
1543 result = ipa3_uc_state_check();
1544 if (result)
1545 return result;
1546
1547 IPADBG("ep=%d\n", clnt_hdl);
1548
1549 ep = &ipa3_ctx->ep[clnt_hdl];
1550
1551 if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED |
1552 IPA_WDI_RESUMED)) {
1553 IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
1554 return -EFAULT;
1555 }
1556
1557 suspend.params.ipa_pipe_number = clnt_hdl;
1558
1559 if (IPA_CLIENT_IS_PROD(ep->client)) {
Skylar Chang019df562017-03-08 10:45:45 -08001560 /*
1561 * For WDI 2.0 need to ensure pipe will be empty before suspend
1562 * as IPA uC will fail to suspend the pipe otherwise.
1563 */
1564 if (ipa3_ctx->ipa_wdi2) {
1565 source_pipe_bitmask = 1 <<
1566 ipa3_get_ep_mapping(ep->client);
1567 result = ipa3_enable_force_clear(clnt_hdl,
1568 false, source_pipe_bitmask);
1569 if (result) {
1570 /*
1571 * assuming here modem SSR, AP can remove
1572 * the delay in this case
1573 */
1574 IPAERR("failed to force clear %d\n", result);
1575 IPAERR("remove delay from SCND reg\n");
1576 ep_ctrl_scnd.endp_delay = false;
1577 ipahal_write_reg_n_fields(
1578 IPA_ENDP_INIT_CTRL_SCND_n, clnt_hdl,
1579 &ep_ctrl_scnd);
1580 } else {
1581 disable_force_clear = true;
1582 }
1583 }
1584
Mohammed Javiddb111a32018-02-22 15:16:47 +05301585 /* Enabling HOLB on WDI consumer pipe */
1586 ipa3_cfg_holb_wdi_consumer(true);
1587
Amir Levy9659e592016-10-27 18:08:27 +03001588 IPADBG("Post suspend event first for IPA Producer\n");
1589 IPADBG("Client: %d clnt_hdl: %d\n", ep->client, clnt_hdl);
1590 result = ipa3_uc_send_cmd(suspend.raw32b,
1591 IPA_CPU_2_HW_CMD_WDI_CH_SUSPEND,
1592 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
1593 false, 10*HZ);
1594
1595 if (result) {
1596 result = -EFAULT;
Mohammed Javiddb111a32018-02-22 15:16:47 +05301597 /* Disabling HOLB on WDI consumer pipe */
1598 ipa3_cfg_holb_wdi_consumer(false);
Amir Levy9659e592016-10-27 18:08:27 +03001599 goto uc_timeout;
1600 }
Mohammed Javiddb111a32018-02-22 15:16:47 +05301601 /* Disabling HOLB on WDI consumer pipe */
1602 ipa3_cfg_holb_wdi_consumer(false);
Amir Levy9659e592016-10-27 18:08:27 +03001603 }
1604
1605 memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
1606 if (IPA_CLIENT_IS_CONS(ep->client)) {
Skylar Changa699afd2017-06-06 10:06:21 -07001607 if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
1608 ep_cfg_ctrl.ipa_ep_suspend = true;
1609 result = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
1610 if (result)
1611 IPAERR("(ep: %d) failed to suspend result=%d\n",
1612 clnt_hdl, result);
1613 else
1614 IPADBG("(ep: %d) suspended\n", clnt_hdl);
1615 }
Amir Levy9659e592016-10-27 18:08:27 +03001616 } else {
1617 ep_cfg_ctrl.ipa_ep_delay = true;
1618 result = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
1619 if (result)
1620 IPAERR("client (ep: %d) failed to delay result=%d\n",
1621 clnt_hdl, result);
1622 else
1623 IPADBG("client (ep: %d) delayed\n", clnt_hdl);
1624 }
1625
1626 if (IPA_CLIENT_IS_CONS(ep->client)) {
1627 result = ipa3_uc_send_cmd(suspend.raw32b,
1628 IPA_CPU_2_HW_CMD_WDI_CH_SUSPEND,
1629 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
1630 false, 10*HZ);
1631
1632 if (result) {
1633 result = -EFAULT;
1634 goto uc_timeout;
1635 }
1636 }
1637
Skylar Chang019df562017-03-08 10:45:45 -08001638 if (disable_force_clear)
1639 ipa3_disable_force_clear(clnt_hdl);
1640
Amir Levy9659e592016-10-27 18:08:27 +03001641 ipa3_ctx->tag_process_before_gating = true;
1642 IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
1643 ep->uc_offload_state &= ~IPA_WDI_RESUMED;
1644 IPADBG("client (ep: %d) suspended\n", clnt_hdl);
1645
1646uc_timeout:
1647 return result;
1648}
1649
Skylar Chang6b41f8d2016-11-01 12:50:11 -07001650/**
1651 * ipa_broadcast_wdi_quota_reach_ind() - quota reach
1652 * @uint32_t fid: [in] input netdev ID
1653 * @uint64_t num_bytes: [in] used bytes
1654 *
1655 * Returns: 0 on success, negative on failure
1656 */
1657int ipa3_broadcast_wdi_quota_reach_ind(uint32_t fid,
1658 uint64_t num_bytes)
1659{
1660 IPAERR("Quota reached indication on fid(%d) Mbytes(%lu)\n",
1661 fid,
1662 (unsigned long int) num_bytes);
1663 ipa3_broadcast_quota_reach_ind(0, IPA_UPSTEAM_WLAN);
1664 return 0;
1665}
1666
Amir Levy9659e592016-10-27 18:08:27 +03001667int ipa3_write_qmapid_wdi_pipe(u32 clnt_hdl, u8 qmap_id)
1668{
1669 int result = 0;
1670 struct ipa3_ep_context *ep;
1671 union IpaHwWdiRxExtCfgCmdData_t qmap;
1672
1673 if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
1674 ipa3_ctx->ep[clnt_hdl].valid == 0) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301675 IPAERR_RL("bad parm, %d\n", clnt_hdl);
Amir Levy9659e592016-10-27 18:08:27 +03001676 return -EINVAL;
1677 }
1678
1679 result = ipa3_uc_state_check();
1680 if (result)
1681 return result;
1682
1683 IPADBG("ep=%d\n", clnt_hdl);
1684
1685 ep = &ipa3_ctx->ep[clnt_hdl];
1686
1687 if (!(ep->uc_offload_state & IPA_WDI_CONNECTED)) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301688 IPAERR_RL("WDI channel bad state %d\n", ep->uc_offload_state);
Amir Levy9659e592016-10-27 18:08:27 +03001689 return -EFAULT;
1690 }
1691 IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
1692 qmap.params.ipa_pipe_number = clnt_hdl;
1693 qmap.params.qmap_id = qmap_id;
1694
1695 result = ipa3_uc_send_cmd(qmap.raw32b,
1696 IPA_CPU_2_HW_CMD_WDI_RX_EXT_CFG,
1697 IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
1698 false, 10*HZ);
1699
1700 if (result) {
1701 result = -EFAULT;
1702 goto uc_timeout;
1703 }
1704 IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
1705
1706 IPADBG("client (ep: %d) qmap_id %d updated\n", clnt_hdl, qmap_id);
1707
1708uc_timeout:
1709 return result;
1710}
1711
1712/**
1713 * ipa3_uc_reg_rdyCB() - To register uC
1714 * ready CB if uC not ready
1715 * @inout: [in/out] input/output parameters
1716 * from/to client
1717 *
1718 * Returns: 0 on success, negative on failure
1719 *
1720 */
1721int ipa3_uc_reg_rdyCB(
1722 struct ipa_wdi_uc_ready_params *inout)
1723{
1724 int result = 0;
1725
1726 if (inout == NULL) {
1727 IPAERR("bad parm. inout=%p ", inout);
1728 return -EINVAL;
1729 }
1730
1731 result = ipa3_uc_state_check();
1732 if (result) {
1733 inout->is_uC_ready = false;
1734 ipa3_ctx->uc_wdi_ctx.uc_ready_cb = inout->notify;
1735 ipa3_ctx->uc_wdi_ctx.priv = inout->priv;
1736 } else {
1737 inout->is_uC_ready = true;
1738 }
1739
1740 return 0;
1741}
1742
1743/**
1744 * ipa3_uc_dereg_rdyCB() - To de-register uC ready CB
1745 *
1746 * Returns: 0 on success, negative on failure
1747 *
1748 */
1749int ipa3_uc_dereg_rdyCB(void)
1750{
1751 ipa3_ctx->uc_wdi_ctx.uc_ready_cb = NULL;
1752 ipa3_ctx->uc_wdi_ctx.priv = NULL;
1753
1754 return 0;
1755}
1756
1757
1758/**
1759 * ipa3_uc_wdi_get_dbpa() - To retrieve
1760 * doorbell physical address of wlan pipes
1761 * @param: [in/out] input/output parameters
1762 * from/to client
1763 *
1764 * Returns: 0 on success, negative on failure
1765 *
1766 */
1767int ipa3_uc_wdi_get_dbpa(
1768 struct ipa_wdi_db_params *param)
1769{
1770 if (param == NULL || param->client >= IPA_CLIENT_MAX) {
1771 IPAERR("bad parm. param=%p ", param);
1772 if (param)
1773 IPAERR("client = %d\n", param->client);
1774 return -EINVAL;
1775 }
1776
1777 if (IPA_CLIENT_IS_CONS(param->client)) {
1778 param->uc_door_bell_pa = ipa3_ctx->ipa_wrapper_base +
1779 ipahal_get_reg_base() +
1780 ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n,
1781 IPA_HW_WDI_TX_MBOX_START_INDEX/32,
1782 IPA_HW_WDI_TX_MBOX_START_INDEX % 32);
1783 } else {
1784 param->uc_door_bell_pa = ipa3_ctx->ipa_wrapper_base +
1785 ipahal_get_reg_base() +
1786 ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n,
1787 IPA_HW_WDI_RX_MBOX_START_INDEX/32,
1788 IPA_HW_WDI_RX_MBOX_START_INDEX % 32);
1789 }
1790
1791 return 0;
1792}
1793
1794static void ipa3_uc_wdi_loaded_handler(void)
1795{
1796 if (!ipa3_ctx) {
1797 IPAERR("IPA ctx is null\n");
1798 return;
1799 }
1800
1801 if (ipa3_ctx->uc_wdi_ctx.uc_ready_cb) {
1802 ipa3_ctx->uc_wdi_ctx.uc_ready_cb(
1803 ipa3_ctx->uc_wdi_ctx.priv);
1804
1805 ipa3_ctx->uc_wdi_ctx.uc_ready_cb =
1806 NULL;
1807 ipa3_ctx->uc_wdi_ctx.priv = NULL;
1808 }
1809}
1810
1811int ipa3_create_wdi_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info)
1812{
Skylar Changefc0a0f2018-03-29 11:17:40 -07001813 struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_WLAN);
Amir Levy9659e592016-10-27 18:08:27 +03001814 int i;
1815 int ret = 0;
1816 int prot = IOMMU_READ | IOMMU_WRITE;
1817
1818 if (!info) {
1819 IPAERR("info = %p\n", info);
1820 return -EINVAL;
1821 }
1822
1823 if (!cb->valid) {
1824 IPAERR("No SMMU CB setup\n");
1825 return -EINVAL;
1826 }
1827
Michael Adisumarta93e97522017-10-06 15:49:46 -07001828 if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN]) {
1829 IPAERR("IPA SMMU not enabled\n");
1830 return -EINVAL;
1831 }
1832
Amir Levy9659e592016-10-27 18:08:27 +03001833 for (i = 0; i < num_buffers; i++) {
1834 IPADBG("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i,
1835 &info[i].pa, info[i].iova, info[i].size);
1836 info[i].result = ipa3_iommu_map(cb->iommu,
1837 rounddown(info[i].iova, PAGE_SIZE),
1838 rounddown(info[i].pa, PAGE_SIZE),
1839 roundup(info[i].size + info[i].pa -
1840 rounddown(info[i].pa, PAGE_SIZE), PAGE_SIZE),
1841 prot);
1842 }
1843
1844 return ret;
1845}
1846
1847int ipa3_release_wdi_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info)
1848{
Skylar Changefc0a0f2018-03-29 11:17:40 -07001849 struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_WLAN);
Amir Levy9659e592016-10-27 18:08:27 +03001850 int i;
1851 int ret = 0;
1852
1853 if (!info) {
1854 IPAERR("info = %p\n", info);
1855 return -EINVAL;
1856 }
1857
1858 if (!cb->valid) {
1859 IPAERR("No SMMU CB setup\n");
1860 return -EINVAL;
1861 }
1862
1863 for (i = 0; i < num_buffers; i++) {
1864 IPADBG("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i,
1865 &info[i].pa, info[i].iova, info[i].size);
1866 info[i].result = iommu_unmap(cb->iommu,
1867 rounddown(info[i].iova, PAGE_SIZE),
1868 roundup(info[i].size + info[i].pa -
1869 rounddown(info[i].pa, PAGE_SIZE), PAGE_SIZE));
1870 }
1871
1872 return ret;
1873}