Loading drivers/platform/msm/ipa/ipa_v2/Makefile +2 −1 Original line number Diff line number Diff line obj-$(CONFIG_IPA) += ipat.o ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \ ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \ ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \ ipa_wdi3_i.o obj-$(CONFIG_RMNET_IPA) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o drivers/platform/msm/ipa/ipa_v2/ipa_i.h +6 −0 Original line number Diff line number Diff line Loading @@ -1546,6 +1546,12 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl); int ipa2_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv); void ipa2_ntn_uc_dereg_rdyCB(void); int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out); int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); /* * To retrieve doorbell physical address of * wlan pipes Loading drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h +62 −2 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ #define IPA_NTN_TX_DIR 1 #define IPA_NTN_RX_DIR 2 #define IPA_WDI3_TX_DIR 1 #define IPA_WDI3_RX_DIR 2 /** * @brief Enum value determined based on the feature it * corresponds to Loading @@ -45,16 +48,20 @@ * enum ipa_hw_features - Values that represent the features supported in IPA HW * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW * @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW * @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW * @IPA_HW_FEATURE_WDI3 : Feature related to WDI operation in IPA HW */ enum ipa_hw_features { IPA_HW_FEATURE_COMMON = 0x0, IPA_HW_FEATURE_MHI = 0x1, IPA_HW_FEATURE_POWER_COLLAPSE = 0x2, IPA_HW_FEATURE_WDI = 0x3, IPA_HW_FEATURE_NTN = 0x4, IPA_HW_FEATURE_OFFLOAD = 0x5, IPA_HW_FEATURE_WDI3 = 0x6, IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES }; Loading Loading @@ -277,6 +284,33 @@ struct IpaHwNtnSetUpCmdData_t { } __packed; struct IpaHwWdi3SetUpCmdData_t { u32 transfer_ring_base_pa; u32 transfer_ring_base_pa_hi; u32 transfer_ring_size; u32 transfer_ring_doorbell_pa; u32 transfer_ring_doorbell_pa_hi; u32 event_ring_base_pa; u32 event_ring_base_pa_hi; u32 event_ring_size; u32 event_ring_doorbell_pa; u32 event_ring_doorbell_pa_hi; u16 num_pkt_buffers; u8 ipa_pipe_number; u8 dir; u16 pkt_offset; u16 reserved0; u32 desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE]; } __packed; /** * struct IpaHwNtnCommonChCmdData_t - Structure holding the * parameters for Ntn Tear down command data params Loading @@ -291,6 +325,13 @@ union IpaHwNtnCommonChCmdData_t { uint32_t raw32b; } __packed; union IpaHwWdi3CommonChCmdData_t { struct IpaHwWdi3CommonChCmdParams_t { u32 ipa_pipe_number :8; u32 reserved :24; } __packed params; u32 raw32b; } __packed; /** * struct IpaHwNTNErrorEventData_t - Structure holding the Loading Loading @@ -408,13 +449,30 @@ struct IpaHwStatsNTNInfoData_t { * the offload commands from CPU * @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up * Offload protocol's Tx/Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down * @IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN : Command to tear down * Offload protocol's Tx/ Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE : Command to enable * Offload protocol's Tx/Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE : Command to disable * Offload protocol's Tx/ Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND : Command to suspend * Offload protocol's Tx/Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_RESUME : Command to resume * Offload protocol's Tx/ Rx Path */ enum ipa_cpu_2_hw_offload_commands { IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2), IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3), IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4), IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5), IPA_CPU_2_HW_CMD_OFFLOAD_RESUME = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6), }; Loading Loading @@ -525,6 +583,7 @@ enum ipa_hw_2_cpu_cmd_resp_status { */ union IpaHwSetUpCmd { struct IpaHwNtnSetUpCmdData_t NtnSetupCh_params; struct IpaHwWdi3SetUpCmdData_t Wdi3SetupCh_params; } __packed; /** Loading @@ -545,6 +604,7 @@ struct IpaHwOffloadSetUpCmdData_t { */ union IpaHwCommonChCmd { union IpaHwNtnCommonChCmdData_t NtnCommonCh_params; union IpaHwWdi3CommonChCmdData_t Wdi3CommonCh_params; } __packed; struct IpaHwOffloadCommonChCmdData_t { Loading drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +4 −0 Original line number Diff line number Diff line Loading @@ -5088,6 +5088,10 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_get_pdev = ipa2_get_pdev; api_ctrl->ipa_ntn_uc_reg_rdyCB = ipa2_ntn_uc_reg_rdyCB; api_ctrl->ipa_ntn_uc_dereg_rdyCB = ipa2_ntn_uc_dereg_rdyCB; api_ctrl->ipa_conn_wdi3_pipes = ipa2_conn_wdi3_pipes; api_ctrl->ipa_disconn_wdi3_pipes = ipa2_disconn_wdi3_pipes; api_ctrl->ipa_enable_wdi3_pipes = ipa2_enable_wdi3_pipes; api_ctrl->ipa_disable_wdi3_pipes = ipa2_disable_wdi3_pipes; return 0; } Loading drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c 0 → 100644 +406 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "ipa_i.h" #include "ipa_uc_offload_i.h" #include <linux/ipa_wdi3.h> #define IPA_HW_WDI3_RX_MBOX_START_INDEX 48 #define IPA_HW_WDI3_TX_MBOX_START_INDEX 50 static int ipa_send_wdi3_setup_pipe_cmd( struct ipa_wdi3_setup_info *info, u8 dir) { int ipa_ep_idx; int result = 0; struct ipa_mem_buffer cmd; struct IpaHwWdi3SetUpCmdData_t *wdi3_params; struct IpaHwOffloadSetUpCmdData_t *cmd_data; if (info == NULL) { IPAERR("invalid input\n"); return -EINVAL; } ipa_ep_idx = ipa_get_ep_mapping(info->client); IPAERR("ep number: %d\n", ipa_ep_idx); if (ipa_ep_idx == -1) { IPAERR("fail to get ep idx.\n"); return -EFAULT; } IPAERR("client=%d ep=%d\n", info->client, ipa_ep_idx); IPAERR("ring_base_pa = 0x%pad\n", &info->transfer_ring_base_pa); IPAERR("ring_size = %hu\n", info->transfer_ring_size); IPAERR("ring_db_pa = 0x%pad\n", &info->transfer_ring_doorbell_pa); IPAERR("evt_ring_base_pa = 0x%pad\n", &info->event_ring_base_pa); IPAERR("evt_ring_size = %hu\n", info->event_ring_size); IPAERR("evt_ring_db_pa = 0x%pad\n", &info->event_ring_doorbell_pa); IPAERR("num_pkt_buffers = %hu\n", info->num_pkt_buffers); IPAERR("pkt_offset = %d.\n", info->pkt_offset); cmd.size = sizeof(*cmd_data); cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size, &cmd.phys_base, GFP_KERNEL); if (cmd.base == NULL) { IPAERR("fail to get DMA memory.\n"); return -ENOMEM; } IPAERR("suceeded in allocating memory.\n"); cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base; cmd_data->protocol = IPA_HW_FEATURE_WDI3; wdi3_params = &cmd_data->SetupCh_params.Wdi3SetupCh_params; wdi3_params->transfer_ring_base_pa = (u32)info->transfer_ring_base_pa; wdi3_params->transfer_ring_base_pa_hi = (u32)((u64)info->transfer_ring_base_pa >> 32); wdi3_params->transfer_ring_size = info->transfer_ring_size; wdi3_params->transfer_ring_doorbell_pa = (u32)info->transfer_ring_doorbell_pa; wdi3_params->transfer_ring_doorbell_pa_hi = (u32)((u64)info->transfer_ring_doorbell_pa >> 32); wdi3_params->event_ring_base_pa = (u32)info->event_ring_base_pa; wdi3_params->event_ring_base_pa_hi = (u32)((u64)info->event_ring_base_pa >> 32); wdi3_params->event_ring_size = info->event_ring_size; wdi3_params->event_ring_doorbell_pa = (u32)info->event_ring_doorbell_pa; wdi3_params->event_ring_doorbell_pa_hi = (u32)((u64)info->event_ring_doorbell_pa >> 32); wdi3_params->num_pkt_buffers = info->num_pkt_buffers; wdi3_params->ipa_pipe_number = ipa_ep_idx; wdi3_params->dir = dir; wdi3_params->pkt_offset = info->pkt_offset; memcpy(wdi3_params->desc_format_template, info->desc_format_template, sizeof(wdi3_params->desc_format_template)); IPAERR("suceeded in populating the command memory.\n"); result = ipa_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, false, 10*HZ); if (result) { IPAERR("uc setup channel cmd failed: %d\n", result); result = -EFAULT; } dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); IPAERR("suceeded in freeing memory.\n"); return result; } int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out) { struct ipa_ep_context *ep_rx; struct ipa_ep_context *ep_tx; int ipa_ep_idx_rx; int ipa_ep_idx_tx; int result = 0; if (in == NULL || out == NULL) { IPAERR("invalid input\n"); return -EINVAL; } ipa_ep_idx_rx = ipa_get_ep_mapping(in->rx.client); ipa_ep_idx_tx = ipa_get_ep_mapping(in->tx.client); if (ipa_ep_idx_rx == -1 || ipa_ep_idx_tx == -1) { IPAERR("fail to alloc EP.\n"); return -EFAULT; } ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; if (ep_rx->valid || ep_tx->valid) { IPAERR("EP already allocated.\n"); return -EFAULT; } memset(ep_rx, 0, offsetof(struct ipa_ep_context, sys)); memset(ep_tx, 0, offsetof(struct ipa_ep_context, sys)); IPA_ACTIVE_CLIENTS_INC_SIMPLE(); /* setup rx ep cfg */ ep_rx->valid = 1; ep_rx->client = in->rx.client; result = ipa_disable_data_path(ipa_ep_idx_rx); if (result) { IPAERR("disable data path failed res=%d clnt=%d.\n", result, ipa_ep_idx_rx); IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return -EFAULT; } ep_rx->client_notify = in->notify; ep_rx->priv = in->priv; memcpy(&ep_rx->cfg, &in->rx.ipa_ep_cfg, sizeof(ep_rx->cfg)); if (ipa_cfg_ep(ipa_ep_idx_rx, &ep_rx->cfg)) { IPAERR("fail to setup rx pipe cfg\n"); result = -EFAULT; goto fail; } IPAERR("configured RX EP.\n"); if (ipa_send_wdi3_setup_pipe_cmd(&in->rx, IPA_WDI3_RX_DIR)) { IPAERR("fail to send cmd to uc for rx pipe\n"); result = -EFAULT; goto fail; } IPAERR("rx pipe was setup.\n"); ipa_install_dflt_flt_rules(ipa_ep_idx_rx); out->rx_uc_db_pa = ipa_ctx->ipa_wrapper_base + IPA_REG_BASE_OFST_v2_5 + IPA_UC_MAILBOX_m_n_OFFS_v2_5( IPA_HW_WDI3_RX_MBOX_START_INDEX/32, IPA_HW_WDI3_RX_MBOX_START_INDEX % 32); IPADBG("client %d (ep: %d) connected\n", in->rx.client, ipa_ep_idx_rx); /* setup dl ep cfg */ ep_tx->valid = 1; ep_tx->client = in->tx.client; result = ipa_disable_data_path(ipa_ep_idx_tx); if (result) { IPAERR("disable data path failed res=%d clnt=%d.\n", result, ipa_ep_idx_tx); result = -EFAULT; goto fail; } memcpy(&ep_tx->cfg, &in->tx.ipa_ep_cfg, sizeof(ep_tx->cfg)); if (ipa_cfg_ep(ipa_ep_idx_tx, &ep_tx->cfg)) { IPAERR("fail to setup tx pipe cfg\n"); result = -EFAULT; goto fail; } IPAERR("configured TX EP in DMA mode.\n"); if (ipa_send_wdi3_setup_pipe_cmd(&in->tx, IPA_WDI3_TX_DIR)) { IPAERR("fail to send cmd to uc for tx pipe\n"); result = -EFAULT; goto fail; } IPAERR("tx pipe was setup.\n"); out->tx_uc_db_pa = ipa_ctx->ipa_wrapper_base + IPA_REG_BASE_OFST_v2_5 + IPA_UC_MAILBOX_m_n_OFFS_v2_5( IPA_HW_WDI3_TX_MBOX_START_INDEX/32, IPA_HW_WDI3_TX_MBOX_START_INDEX % 32); out->tx_uc_db_va = ioremap(out->tx_uc_db_pa, 4); IPADBG("client %d (ep: %d) connected\n", in->tx.client, ipa_ep_idx_tx); fail: IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return result; } static int ipa_send_wdi3_common_ch_cmd(int ipa_ep_idx, int command) { struct ipa_mem_buffer cmd; struct IpaHwOffloadCommonChCmdData_t *cmd_data; union IpaHwWdi3CommonChCmdData_t *wdi3; int result = 0; cmd.size = sizeof(*cmd_data); cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size, &cmd.phys_base, GFP_KERNEL); if (cmd.base == NULL) { IPAERR("fail to get DMA memory.\n"); return -ENOMEM; } IPA_ACTIVE_CLIENTS_INC_SIMPLE(); /* enable the TX pipe */ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base; cmd_data->protocol = IPA_HW_FEATURE_WDI3; wdi3 = &cmd_data->CommonCh_params.Wdi3CommonCh_params; wdi3->params.ipa_pipe_number = ipa_ep_idx; IPAERR("cmd: %d ep_idx: %d\n", command, ipa_ep_idx); result = ipa_uc_send_cmd((u32)(cmd.phys_base), command, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, false, 10*HZ); if (result) { result = -EFAULT; goto fail; } fail: dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return result; } int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { struct ipa_ep_context *ep_tx, *ep_rx; int result = 0; IPADBG("ep_tx = %d\n", ipa_ep_idx_tx); IPADBG("ep_rx = %d\n", ipa_ep_idx_rx); ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; /* tear down tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) { IPAERR("fail to tear down tx pipe\n"); result = -EFAULT; goto fail; } ipa_disable_data_path(ipa_ep_idx_tx); memset(ep_tx, 0, sizeof(struct ipa_ep_context)); IPADBG("tx client (ep: %d) disconnected\n", ipa_ep_idx_tx); /* tear down rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) { IPAERR("fail to tear down rx pipe\n"); result = -EFAULT; goto fail; } ipa_disable_data_path(ipa_ep_idx_rx); ipa_delete_dflt_flt_rules(ipa_ep_idx_rx); memset(ep_rx, 0, sizeof(struct ipa_ep_context)); IPADBG("rx client (ep: %d) disconnected\n", ipa_ep_idx_rx); fail: return result; } int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { struct ipa_ep_context *ep_tx, *ep_rx; int result = 0; IPAERR("ep_tx = %d\n", ipa_ep_idx_tx); IPAERR("ep_rx = %d\n", ipa_ep_idx_rx); ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; /* enable tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) { IPAERR("fail to enable tx pipe\n"); WARN_ON(1); result = -EFAULT; goto fail; } /* resume tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) { IPAERR("fail to resume tx pipe\n"); WARN_ON(1); result = -EFAULT; goto fail; } /* enable rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) { IPAERR("fail to enable rx pipe\n"); WARN_ON(1); result = -EFAULT; goto fail; } /* resume rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) { IPAERR("fail to resume rx pipe\n"); WARN_ON(1); result = -EFAULT; goto fail; } IPA_ACTIVE_CLIENTS_INC_SIMPLE(); /* enable data path */ result = ipa_enable_data_path(ipa_ep_idx_rx); if (result) { IPAERR("enable data path failed res=%d clnt=%d.\n", result, ipa_ep_idx_rx); IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return -EFAULT; } result = ipa_enable_data_path(ipa_ep_idx_tx); if (result) { IPAERR("enable data path failed res=%d clnt=%d.\n", result, ipa_ep_idx_tx); IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return -EFAULT; } IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); fail: return result; } int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { struct ipa_ep_context *ep_tx, *ep_rx; int result = 0; IPADBG("ep_tx = %d\n", ipa_ep_idx_tx); IPADBG("ep_rx = %d\n", ipa_ep_idx_rx); ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; /* suspend tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) { IPAERR("fail to suspend tx pipe\n"); result = -EFAULT; goto fail; } /* disable tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) { IPAERR("fail to disable tx pipe\n"); result = -EFAULT; goto fail; } /* suspend rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) { IPAERR("fail to suspend rx pipe\n"); result = -EFAULT; goto fail; } /* disable rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) { IPAERR("fail to disable rx pipe\n"); result = -EFAULT; goto fail; } fail: return result; } Loading
drivers/platform/msm/ipa/ipa_v2/Makefile +2 −1 Original line number Diff line number Diff line obj-$(CONFIG_IPA) += ipat.o ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \ ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \ ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \ ipa_wdi3_i.o obj-$(CONFIG_RMNET_IPA) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o
drivers/platform/msm/ipa/ipa_v2/ipa_i.h +6 −0 Original line number Diff line number Diff line Loading @@ -1546,6 +1546,12 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl); int ipa2_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv); void ipa2_ntn_uc_dereg_rdyCB(void); int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out); int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); /* * To retrieve doorbell physical address of * wlan pipes Loading
drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h +62 −2 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ #define IPA_NTN_TX_DIR 1 #define IPA_NTN_RX_DIR 2 #define IPA_WDI3_TX_DIR 1 #define IPA_WDI3_RX_DIR 2 /** * @brief Enum value determined based on the feature it * corresponds to Loading @@ -45,16 +48,20 @@ * enum ipa_hw_features - Values that represent the features supported in IPA HW * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW * @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW * @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW * @IPA_HW_FEATURE_WDI3 : Feature related to WDI operation in IPA HW */ enum ipa_hw_features { IPA_HW_FEATURE_COMMON = 0x0, IPA_HW_FEATURE_MHI = 0x1, IPA_HW_FEATURE_POWER_COLLAPSE = 0x2, IPA_HW_FEATURE_WDI = 0x3, IPA_HW_FEATURE_NTN = 0x4, IPA_HW_FEATURE_OFFLOAD = 0x5, IPA_HW_FEATURE_WDI3 = 0x6, IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES }; Loading Loading @@ -277,6 +284,33 @@ struct IpaHwNtnSetUpCmdData_t { } __packed; struct IpaHwWdi3SetUpCmdData_t { u32 transfer_ring_base_pa; u32 transfer_ring_base_pa_hi; u32 transfer_ring_size; u32 transfer_ring_doorbell_pa; u32 transfer_ring_doorbell_pa_hi; u32 event_ring_base_pa; u32 event_ring_base_pa_hi; u32 event_ring_size; u32 event_ring_doorbell_pa; u32 event_ring_doorbell_pa_hi; u16 num_pkt_buffers; u8 ipa_pipe_number; u8 dir; u16 pkt_offset; u16 reserved0; u32 desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE]; } __packed; /** * struct IpaHwNtnCommonChCmdData_t - Structure holding the * parameters for Ntn Tear down command data params Loading @@ -291,6 +325,13 @@ union IpaHwNtnCommonChCmdData_t { uint32_t raw32b; } __packed; union IpaHwWdi3CommonChCmdData_t { struct IpaHwWdi3CommonChCmdParams_t { u32 ipa_pipe_number :8; u32 reserved :24; } __packed params; u32 raw32b; } __packed; /** * struct IpaHwNTNErrorEventData_t - Structure holding the Loading Loading @@ -408,13 +449,30 @@ struct IpaHwStatsNTNInfoData_t { * the offload commands from CPU * @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up * Offload protocol's Tx/Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down * @IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN : Command to tear down * Offload protocol's Tx/ Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE : Command to enable * Offload protocol's Tx/Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE : Command to disable * Offload protocol's Tx/ Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND : Command to suspend * Offload protocol's Tx/Rx Path * @IPA_CPU_2_HW_CMD_OFFLOAD_RESUME : Command to resume * Offload protocol's Tx/ Rx Path */ enum ipa_cpu_2_hw_offload_commands { IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2), IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3), IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4), IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5), IPA_CPU_2_HW_CMD_OFFLOAD_RESUME = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6), }; Loading Loading @@ -525,6 +583,7 @@ enum ipa_hw_2_cpu_cmd_resp_status { */ union IpaHwSetUpCmd { struct IpaHwNtnSetUpCmdData_t NtnSetupCh_params; struct IpaHwWdi3SetUpCmdData_t Wdi3SetupCh_params; } __packed; /** Loading @@ -545,6 +604,7 @@ struct IpaHwOffloadSetUpCmdData_t { */ union IpaHwCommonChCmd { union IpaHwNtnCommonChCmdData_t NtnCommonCh_params; union IpaHwWdi3CommonChCmdData_t Wdi3CommonCh_params; } __packed; struct IpaHwOffloadCommonChCmdData_t { Loading
drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +4 −0 Original line number Diff line number Diff line Loading @@ -5088,6 +5088,10 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_get_pdev = ipa2_get_pdev; api_ctrl->ipa_ntn_uc_reg_rdyCB = ipa2_ntn_uc_reg_rdyCB; api_ctrl->ipa_ntn_uc_dereg_rdyCB = ipa2_ntn_uc_dereg_rdyCB; api_ctrl->ipa_conn_wdi3_pipes = ipa2_conn_wdi3_pipes; api_ctrl->ipa_disconn_wdi3_pipes = ipa2_disconn_wdi3_pipes; api_ctrl->ipa_enable_wdi3_pipes = ipa2_enable_wdi3_pipes; api_ctrl->ipa_disable_wdi3_pipes = ipa2_disable_wdi3_pipes; return 0; } Loading
drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c 0 → 100644 +406 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "ipa_i.h" #include "ipa_uc_offload_i.h" #include <linux/ipa_wdi3.h> #define IPA_HW_WDI3_RX_MBOX_START_INDEX 48 #define IPA_HW_WDI3_TX_MBOX_START_INDEX 50 static int ipa_send_wdi3_setup_pipe_cmd( struct ipa_wdi3_setup_info *info, u8 dir) { int ipa_ep_idx; int result = 0; struct ipa_mem_buffer cmd; struct IpaHwWdi3SetUpCmdData_t *wdi3_params; struct IpaHwOffloadSetUpCmdData_t *cmd_data; if (info == NULL) { IPAERR("invalid input\n"); return -EINVAL; } ipa_ep_idx = ipa_get_ep_mapping(info->client); IPAERR("ep number: %d\n", ipa_ep_idx); if (ipa_ep_idx == -1) { IPAERR("fail to get ep idx.\n"); return -EFAULT; } IPAERR("client=%d ep=%d\n", info->client, ipa_ep_idx); IPAERR("ring_base_pa = 0x%pad\n", &info->transfer_ring_base_pa); IPAERR("ring_size = %hu\n", info->transfer_ring_size); IPAERR("ring_db_pa = 0x%pad\n", &info->transfer_ring_doorbell_pa); IPAERR("evt_ring_base_pa = 0x%pad\n", &info->event_ring_base_pa); IPAERR("evt_ring_size = %hu\n", info->event_ring_size); IPAERR("evt_ring_db_pa = 0x%pad\n", &info->event_ring_doorbell_pa); IPAERR("num_pkt_buffers = %hu\n", info->num_pkt_buffers); IPAERR("pkt_offset = %d.\n", info->pkt_offset); cmd.size = sizeof(*cmd_data); cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size, &cmd.phys_base, GFP_KERNEL); if (cmd.base == NULL) { IPAERR("fail to get DMA memory.\n"); return -ENOMEM; } IPAERR("suceeded in allocating memory.\n"); cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base; cmd_data->protocol = IPA_HW_FEATURE_WDI3; wdi3_params = &cmd_data->SetupCh_params.Wdi3SetupCh_params; wdi3_params->transfer_ring_base_pa = (u32)info->transfer_ring_base_pa; wdi3_params->transfer_ring_base_pa_hi = (u32)((u64)info->transfer_ring_base_pa >> 32); wdi3_params->transfer_ring_size = info->transfer_ring_size; wdi3_params->transfer_ring_doorbell_pa = (u32)info->transfer_ring_doorbell_pa; wdi3_params->transfer_ring_doorbell_pa_hi = (u32)((u64)info->transfer_ring_doorbell_pa >> 32); wdi3_params->event_ring_base_pa = (u32)info->event_ring_base_pa; wdi3_params->event_ring_base_pa_hi = (u32)((u64)info->event_ring_base_pa >> 32); wdi3_params->event_ring_size = info->event_ring_size; wdi3_params->event_ring_doorbell_pa = (u32)info->event_ring_doorbell_pa; wdi3_params->event_ring_doorbell_pa_hi = (u32)((u64)info->event_ring_doorbell_pa >> 32); wdi3_params->num_pkt_buffers = info->num_pkt_buffers; wdi3_params->ipa_pipe_number = ipa_ep_idx; wdi3_params->dir = dir; wdi3_params->pkt_offset = info->pkt_offset; memcpy(wdi3_params->desc_format_template, info->desc_format_template, sizeof(wdi3_params->desc_format_template)); IPAERR("suceeded in populating the command memory.\n"); result = ipa_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, false, 10*HZ); if (result) { IPAERR("uc setup channel cmd failed: %d\n", result); result = -EFAULT; } dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); IPAERR("suceeded in freeing memory.\n"); return result; } int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out) { struct ipa_ep_context *ep_rx; struct ipa_ep_context *ep_tx; int ipa_ep_idx_rx; int ipa_ep_idx_tx; int result = 0; if (in == NULL || out == NULL) { IPAERR("invalid input\n"); return -EINVAL; } ipa_ep_idx_rx = ipa_get_ep_mapping(in->rx.client); ipa_ep_idx_tx = ipa_get_ep_mapping(in->tx.client); if (ipa_ep_idx_rx == -1 || ipa_ep_idx_tx == -1) { IPAERR("fail to alloc EP.\n"); return -EFAULT; } ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; if (ep_rx->valid || ep_tx->valid) { IPAERR("EP already allocated.\n"); return -EFAULT; } memset(ep_rx, 0, offsetof(struct ipa_ep_context, sys)); memset(ep_tx, 0, offsetof(struct ipa_ep_context, sys)); IPA_ACTIVE_CLIENTS_INC_SIMPLE(); /* setup rx ep cfg */ ep_rx->valid = 1; ep_rx->client = in->rx.client; result = ipa_disable_data_path(ipa_ep_idx_rx); if (result) { IPAERR("disable data path failed res=%d clnt=%d.\n", result, ipa_ep_idx_rx); IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return -EFAULT; } ep_rx->client_notify = in->notify; ep_rx->priv = in->priv; memcpy(&ep_rx->cfg, &in->rx.ipa_ep_cfg, sizeof(ep_rx->cfg)); if (ipa_cfg_ep(ipa_ep_idx_rx, &ep_rx->cfg)) { IPAERR("fail to setup rx pipe cfg\n"); result = -EFAULT; goto fail; } IPAERR("configured RX EP.\n"); if (ipa_send_wdi3_setup_pipe_cmd(&in->rx, IPA_WDI3_RX_DIR)) { IPAERR("fail to send cmd to uc for rx pipe\n"); result = -EFAULT; goto fail; } IPAERR("rx pipe was setup.\n"); ipa_install_dflt_flt_rules(ipa_ep_idx_rx); out->rx_uc_db_pa = ipa_ctx->ipa_wrapper_base + IPA_REG_BASE_OFST_v2_5 + IPA_UC_MAILBOX_m_n_OFFS_v2_5( IPA_HW_WDI3_RX_MBOX_START_INDEX/32, IPA_HW_WDI3_RX_MBOX_START_INDEX % 32); IPADBG("client %d (ep: %d) connected\n", in->rx.client, ipa_ep_idx_rx); /* setup dl ep cfg */ ep_tx->valid = 1; ep_tx->client = in->tx.client; result = ipa_disable_data_path(ipa_ep_idx_tx); if (result) { IPAERR("disable data path failed res=%d clnt=%d.\n", result, ipa_ep_idx_tx); result = -EFAULT; goto fail; } memcpy(&ep_tx->cfg, &in->tx.ipa_ep_cfg, sizeof(ep_tx->cfg)); if (ipa_cfg_ep(ipa_ep_idx_tx, &ep_tx->cfg)) { IPAERR("fail to setup tx pipe cfg\n"); result = -EFAULT; goto fail; } IPAERR("configured TX EP in DMA mode.\n"); if (ipa_send_wdi3_setup_pipe_cmd(&in->tx, IPA_WDI3_TX_DIR)) { IPAERR("fail to send cmd to uc for tx pipe\n"); result = -EFAULT; goto fail; } IPAERR("tx pipe was setup.\n"); out->tx_uc_db_pa = ipa_ctx->ipa_wrapper_base + IPA_REG_BASE_OFST_v2_5 + IPA_UC_MAILBOX_m_n_OFFS_v2_5( IPA_HW_WDI3_TX_MBOX_START_INDEX/32, IPA_HW_WDI3_TX_MBOX_START_INDEX % 32); out->tx_uc_db_va = ioremap(out->tx_uc_db_pa, 4); IPADBG("client %d (ep: %d) connected\n", in->tx.client, ipa_ep_idx_tx); fail: IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return result; } static int ipa_send_wdi3_common_ch_cmd(int ipa_ep_idx, int command) { struct ipa_mem_buffer cmd; struct IpaHwOffloadCommonChCmdData_t *cmd_data; union IpaHwWdi3CommonChCmdData_t *wdi3; int result = 0; cmd.size = sizeof(*cmd_data); cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size, &cmd.phys_base, GFP_KERNEL); if (cmd.base == NULL) { IPAERR("fail to get DMA memory.\n"); return -ENOMEM; } IPA_ACTIVE_CLIENTS_INC_SIMPLE(); /* enable the TX pipe */ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base; cmd_data->protocol = IPA_HW_FEATURE_WDI3; wdi3 = &cmd_data->CommonCh_params.Wdi3CommonCh_params; wdi3->params.ipa_pipe_number = ipa_ep_idx; IPAERR("cmd: %d ep_idx: %d\n", command, ipa_ep_idx); result = ipa_uc_send_cmd((u32)(cmd.phys_base), command, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, false, 10*HZ); if (result) { result = -EFAULT; goto fail; } fail: dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return result; } int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { struct ipa_ep_context *ep_tx, *ep_rx; int result = 0; IPADBG("ep_tx = %d\n", ipa_ep_idx_tx); IPADBG("ep_rx = %d\n", ipa_ep_idx_rx); ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; /* tear down tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) { IPAERR("fail to tear down tx pipe\n"); result = -EFAULT; goto fail; } ipa_disable_data_path(ipa_ep_idx_tx); memset(ep_tx, 0, sizeof(struct ipa_ep_context)); IPADBG("tx client (ep: %d) disconnected\n", ipa_ep_idx_tx); /* tear down rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) { IPAERR("fail to tear down rx pipe\n"); result = -EFAULT; goto fail; } ipa_disable_data_path(ipa_ep_idx_rx); ipa_delete_dflt_flt_rules(ipa_ep_idx_rx); memset(ep_rx, 0, sizeof(struct ipa_ep_context)); IPADBG("rx client (ep: %d) disconnected\n", ipa_ep_idx_rx); fail: return result; } int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { struct ipa_ep_context *ep_tx, *ep_rx; int result = 0; IPAERR("ep_tx = %d\n", ipa_ep_idx_tx); IPAERR("ep_rx = %d\n", ipa_ep_idx_rx); ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; /* enable tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) { IPAERR("fail to enable tx pipe\n"); WARN_ON(1); result = -EFAULT; goto fail; } /* resume tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) { IPAERR("fail to resume tx pipe\n"); WARN_ON(1); result = -EFAULT; goto fail; } /* enable rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) { IPAERR("fail to enable rx pipe\n"); WARN_ON(1); result = -EFAULT; goto fail; } /* resume rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) { IPAERR("fail to resume rx pipe\n"); WARN_ON(1); result = -EFAULT; goto fail; } IPA_ACTIVE_CLIENTS_INC_SIMPLE(); /* enable data path */ result = ipa_enable_data_path(ipa_ep_idx_rx); if (result) { IPAERR("enable data path failed res=%d clnt=%d.\n", result, ipa_ep_idx_rx); IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return -EFAULT; } result = ipa_enable_data_path(ipa_ep_idx_tx); if (result) { IPAERR("enable data path failed res=%d clnt=%d.\n", result, ipa_ep_idx_tx); IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return -EFAULT; } IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); fail: return result; } int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { struct ipa_ep_context *ep_tx, *ep_rx; int result = 0; IPADBG("ep_tx = %d\n", ipa_ep_idx_tx); IPADBG("ep_rx = %d\n", ipa_ep_idx_rx); ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; /* suspend tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) { IPAERR("fail to suspend tx pipe\n"); result = -EFAULT; goto fail; } /* disable tx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) { IPAERR("fail to disable tx pipe\n"); result = -EFAULT; goto fail; } /* suspend rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) { IPAERR("fail to suspend rx pipe\n"); result = -EFAULT; goto fail; } /* disable rx pipe */ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) { IPAERR("fail to disable rx pipe\n"); result = -EFAULT; goto fail; } fail: return result; }