blob: a2c33a1d8cd04795a29ef657a7fa1c7ab628c9f6 [file] [log] [blame]
/* 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;
}