blob: 5a41d641de4f7354caa70e8346fd486986a80e2c [file] [log] [blame]
/* Copyright (c) 2016, 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 <linux/delay.h>
#include <linux/ipa_mhi.h>
#include <linux/ipa.h>
#include "../ipa_v3/ipa_i.h"
#include "../../gsi/gsi.h"
#include "../../gsi/gsi_reg.h"
#include "ipa_ut_framework.h"
#define IPA_MHI_TEST_NUM_CHANNELS 8
#define IPA_MHI_TEST_NUM_EVENT_RINGS 8
#define IPA_MHI_TEST_FIRST_CHANNEL_ID 100
#define IPA_MHI_TEST_FIRST_EVENT_RING_ID 100
#define IPA_MHI_TEST_LAST_CHANNEL_ID \
(IPA_MHI_TEST_FIRST_CHANNEL_ID + IPA_MHI_TEST_NUM_CHANNELS - 1)
#define IPA_MHI_TEST_LAST_EVENT_RING_ID \
(IPA_MHI_TEST_FIRST_EVENT_RING_ID + IPA_MHI_TEST_NUM_EVENT_RINGS - 1)
#define IPA_MHI_TEST_MAX_DATA_BUF_SIZE 1500
#define IPA_MHI_TEST_SEQ_TYPE_DMA 0x00000000
#define IPA_MHI_TEST_LOOP_NUM 5
#define IPA_MHI_RUN_TEST_UNIT_IN_LOOP(test_unit, rc, args...) \
do { \
int __i; \
for (__i = 0; __i < IPA_MHI_TEST_LOOP_NUM; __i++) { \
IPA_UT_LOG(#test_unit " START iter %d\n", __i); \
rc = test_unit(args); \
if (!rc) \
continue; \
IPA_UT_LOG(#test_unit " failed %d\n", rc); \
break; \
} \
} while (0)
/**
* check for MSI interrupt for one or both channels:
* OUT channel MSI my be missed as it
* will be overwritten by the IN channel MSI
*/
#define IPA_MHI_TEST_CHECK_MSI_INTR(__both, __timeout) \
do { \
int i; \
for (i = 0; i < 20; i++) { \
if (*((u32 *)test_mhi_ctx->msi.base) == \
(0x10000000 | \
(IPA_MHI_TEST_FIRST_EVENT_RING_ID + 1))) { \
__timeout = false; \
break; \
} \
if (__both && (*((u32 *)test_mhi_ctx->msi.base) == \
(0x10000000 | \
(IPA_MHI_TEST_FIRST_EVENT_RING_ID)))) { \
/* sleep to be sure IN MSI is generated */ \
msleep(20); \
__timeout = false; \
break; \
} \
msleep(20); \
} \
} while (0)
static DECLARE_COMPLETION(mhi_test_ready_comp);
static DECLARE_COMPLETION(mhi_test_wakeup_comp);
/**
* enum ipa_mhi_ring_elements_type - MHI ring elements types.
*/
enum ipa_mhi_ring_elements_type {
IPA_MHI_RING_ELEMENT_NO_OP = 1,
IPA_MHI_RING_ELEMENT_TRANSFER = 2
};
/**
* enum ipa_mhi_channel_direction - MHI channel directions
*/
enum ipa_mhi_channel_direction {
IPA_MHI_OUT_CHAHNNEL = 1,
IPA_MHI_IN_CHAHNNEL = 2,
};
/**
* struct ipa_mhi_channel_context_array - MHI Channel context array entry
*
* mapping is taken from MHI spec
*/
struct ipa_mhi_channel_context_array {
u32 chstate:8; /*0-7*/
u32 brsmode:2; /*8-9*/
u32 pollcfg:6; /*10-15*/
u32 reserved:16; /*16-31*/
u32 chtype; /*channel type (inbound/outbound)*/
u32 erindex; /*event ring index*/
u64 rbase; /*ring base address in the host addr spc*/
u64 rlen; /*ring length in bytes*/
u64 rp; /*read pointer in the host system addr spc*/
u64 wp; /*write pointer in the host system addr spc*/
} __packed;
/**
* struct ipa_mhi_event_context_array - MGI event ring context array entry
*
* mapping is taken from MHI spec
*/
struct ipa_mhi_event_context_array {
u16 intmodc;
u16 intmodt;/* Interrupt moderation timer (in microseconds) */
u32 ertype;
u32 msivec; /* MSI vector for interrupt (MSI data)*/
u64 rbase; /* ring base address in host address space*/
u64 rlen; /* ring length in bytes*/
u64 rp; /* read pointer in the host system address space*/
u64 wp; /* write pointer in the host system address space*/
} __packed;
/**
*
* struct ipa_mhi_mmio_register_set - MHI configuration registers,
* control registers, status registers, pointers to doorbell arrays,
* pointers to channel and event context arrays.
*
* The structure is defined in mhi spec (register names are taken from there).
* Only values accessed by HWP or test are documented
*/
struct ipa_mhi_mmio_register_set {
u32 mhireglen;
u32 reserved_08_04;
u32 mhiver;
u32 reserved_10_0c;
struct mhicfg {
u8 nch;
u8 reserved_15_8;
u8 ner;
u8 reserved_31_23;
} __packed mhicfg;
u32 reserved_18_14;
u32 chdboff;
u32 reserved_20_1C;
u32 erdboff;
u32 reserved_28_24;
u32 bhioff;
u32 reserved_30_2C;
u32 debugoff;
u32 reserved_38_34;
struct mhictrl {
u32 rs : 1;
u32 reset : 1;
u32 reserved_7_2 : 6;
u32 mhistate : 8;
u32 reserved_31_16 : 16;
} __packed mhictrl;
u64 reserved_40_3c;
u32 reserved_44_40;
struct mhistatus {
u32 ready : 1;
u32 reserved_3_2 : 1;
u32 syserr : 1;
u32 reserved_7_3 : 5;
u32 mhistate : 8;
u32 reserved_31_16 : 16;
} __packed mhistatus;
/**
* Register is not accessed by HWP.
* In test register carries the handle for
* the buffer of channel context array
*/
u32 reserved_50_4c;
u32 mhierror;
/**
* Register is not accessed by HWP.
* In test register carries the handle for
* the buffer of event ring context array
*/
u32 reserved_58_54;
/**
* 64-bit pointer to the channel context array in the host memory space
* host sets the pointer to the channel context array during
* initialization.
*/
u64 ccabap;
/**
* 64-bit pointer to the event context array in the host memory space
* host sets the pointer to the event context array during
* initialization
*/
u64 ecabap;
/**
* Register is not accessed by HWP.
* In test register carries the pointer of virtual address
* for the buffer of channel context array
*/
u64 crcbap;
/**
* Register is not accessed by HWP.
* In test register carries the pointer of virtual address
* for the buffer of event ring context array
*/
u64 crdb;
u64 reserved_80_78;
struct mhiaddr {
/**
* Base address (64-bit) of the memory region in
* the host address space where the MHI control
* data structures are allocated by the host,
* including channel context array, event context array,
* and rings.
* The device uses this information to set up its internal
* address translation tables.
* value must be aligned to 4 Kbytes.
*/
u64 mhicrtlbase;
/**
* Upper limit address (64-bit) of the memory region in
* the host address space where the MHI control
* data structures are allocated by the host.
* The device uses this information to setup its internal
* address translation tables.
* The most significant 32 bits of MHICTRLBASE and
* MHICTRLLIMIT registers must be equal.
*/
u64 mhictrllimit;
u64 reserved_18_10;
/**
* Base address (64-bit) of the memory region in
* the host address space where the MHI data buffers
* are allocated by the host.
* The device uses this information to setup its
* internal address translation tables.
* value must be aligned to 4 Kbytes.
*/
u64 mhidatabase;
/**
* Upper limit address (64-bit) of the memory region in
* the host address space where the MHI data buffers
* are allocated by the host.
* The device uses this information to setup its
* internal address translation tables.
* The most significant 32 bits of MHIDATABASE and
* MHIDATALIMIT registers must be equal.
*/
u64 mhidatalimit;
u64 reserved_30_28;
} __packed mhiaddr;
} __packed;
/**
* struct ipa_mhi_event_ring_element - MHI Event ring element
*
* mapping is taken from MHI spec
*/
struct ipa_mhi_event_ring_element {
/**
* pointer to ring element that generated event in
* the host system address space
*/
u64 ptr;
union {
struct {
u32 len : 24;
u32 code : 8;
} __packed bits;
u32 dword;
} __packed dword_8;
u16 reserved;
u8 type;
u8 chid;
} __packed;
/**
* struct ipa_mhi_transfer_ring_element - MHI Transfer ring element
*
* mapping is taken from MHI spec
*/
struct ipa_mhi_transfer_ring_element {
u64 ptr; /*pointer to buffer in the host system address space*/
u16 len; /*transaction length in bytes*/
u16 reserved0;
union {
struct {
u16 chain : 1;
u16 reserved_7_1 : 7;
u16 ieob : 1;
u16 ieot : 1;
u16 bei : 1;
u16 reserved_15_11 : 5;
} __packed bits;
u16 word;
} __packed word_C;
u8 type;
u8 reserved1;
} __packed;
/**
* struct ipa_test_mhi_context - MHI test context
*/
struct ipa_test_mhi_context {
void __iomem *gsi_mmio;
struct ipa_mem_buffer msi;
struct ipa_mem_buffer ch_ctx_array;
struct ipa_mem_buffer ev_ctx_array;
struct ipa_mem_buffer mmio_buf;
struct ipa_mem_buffer xfer_ring_bufs[IPA_MHI_TEST_NUM_CHANNELS];
struct ipa_mem_buffer ev_ring_bufs[IPA_MHI_TEST_NUM_EVENT_RINGS];
struct ipa_mem_buffer in_buffer;
struct ipa_mem_buffer out_buffer;
u32 prod_hdl;
u32 cons_hdl;
};
static struct ipa_test_mhi_context *test_mhi_ctx;
static void ipa_mhi_test_cb(void *priv,
enum ipa_mhi_event_type event, unsigned long data)
{
IPA_UT_DBG("Entry\n");
if (event == IPA_MHI_EVENT_DATA_AVAILABLE)
complete_all(&mhi_test_wakeup_comp);
else if (event == IPA_MHI_EVENT_READY)
complete_all(&mhi_test_ready_comp);
else
WARN_ON(1);
}
static void ipa_test_mhi_free_mmio_space(void)
{
IPA_UT_DBG("Entry\n");
if (!test_mhi_ctx)
return;
dma_free_coherent(ipa3_ctx->pdev, test_mhi_ctx->mmio_buf.size,
test_mhi_ctx->mmio_buf.base,
test_mhi_ctx->mmio_buf.phys_base);
dma_free_coherent(ipa3_ctx->pdev, test_mhi_ctx->ev_ctx_array.size,
test_mhi_ctx->ev_ctx_array.base,
test_mhi_ctx->ev_ctx_array.phys_base);
dma_free_coherent(ipa3_ctx->pdev, test_mhi_ctx->ch_ctx_array.size,
test_mhi_ctx->ch_ctx_array.base,
test_mhi_ctx->ch_ctx_array.phys_base);
dma_free_coherent(ipa3_ctx->pdev, test_mhi_ctx->msi.size,
test_mhi_ctx->msi.base, test_mhi_ctx->msi.phys_base);
}
static int ipa_test_mhi_alloc_mmio_space(void)
{
int rc = 0;
struct ipa_mem_buffer *msi;
struct ipa_mem_buffer *ch_ctx_array;
struct ipa_mem_buffer *ev_ctx_array;
struct ipa_mem_buffer *mmio_buf;
struct ipa_mhi_mmio_register_set *p_mmio;
IPA_UT_DBG("Entry\n");
msi = &test_mhi_ctx->msi;
ch_ctx_array = &test_mhi_ctx->ch_ctx_array;
ev_ctx_array = &test_mhi_ctx->ev_ctx_array;
mmio_buf = &test_mhi_ctx->mmio_buf;
/* Allocate MSI */
msi->size = 4;
msi->base = dma_alloc_coherent(ipa3_ctx->pdev, msi->size,
&msi->phys_base, GFP_KERNEL);
if (!msi->base) {
IPA_UT_ERR("no mem for msi\n");
return -ENOMEM;
}
IPA_UT_DBG("msi: base 0x%pK phys_addr 0x%pad size %d\n",
msi->base, &msi->phys_base, msi->size);
/* allocate buffer for channel context */
ch_ctx_array->size = sizeof(struct ipa_mhi_channel_context_array) *
IPA_MHI_TEST_NUM_CHANNELS;
ch_ctx_array->base = dma_alloc_coherent(ipa3_ctx->pdev,
ch_ctx_array->size, &ch_ctx_array->phys_base, GFP_KERNEL);
if (!ch_ctx_array->base) {
IPA_UT_ERR("no mem for ch ctx array\n");
rc = -ENOMEM;
goto fail_free_msi;
}
IPA_UT_DBG("channel ctx array: base 0x%pK phys_addr %pad size %d\n",
ch_ctx_array->base, &ch_ctx_array->phys_base,
ch_ctx_array->size);
/* allocate buffer for event context */
ev_ctx_array->size = sizeof(struct ipa_mhi_event_context_array) *
IPA_MHI_TEST_NUM_EVENT_RINGS;
ev_ctx_array->base = dma_alloc_coherent(ipa3_ctx->pdev,
ev_ctx_array->size, &ev_ctx_array->phys_base, GFP_KERNEL);
if (!ev_ctx_array->base) {
IPA_UT_ERR("no mem for ev ctx array\n");
rc = -ENOMEM;
goto fail_free_ch_ctx_arr;
}
IPA_UT_DBG("event ctx array: base 0x%pK phys_addr %pad size %d\n",
ev_ctx_array->base, &ev_ctx_array->phys_base,
ev_ctx_array->size);
/* allocate buffer for mmio */
mmio_buf->size = sizeof(struct ipa_mhi_mmio_register_set);
mmio_buf->base = dma_alloc_coherent(ipa3_ctx->pdev, mmio_buf->size,
&mmio_buf->phys_base, GFP_KERNEL);
if (!mmio_buf->base) {
IPA_UT_ERR("no mem for mmio buf\n");
rc = -ENOMEM;
goto fail_free_ev_ctx_arr;
}
IPA_UT_DBG("mmio buffer: base 0x%pK phys_addr %pad size %d\n",
mmio_buf->base, &mmio_buf->phys_base, mmio_buf->size);
/* initlize table */
p_mmio = (struct ipa_mhi_mmio_register_set *)mmio_buf->base;
/**
* 64-bit pointer to the channel context array in the host memory space;
* Host sets the pointer to the channel context array
* during initialization.
*/
p_mmio->ccabap = (u32)ch_ctx_array->phys_base -
(IPA_MHI_TEST_FIRST_CHANNEL_ID *
sizeof(struct ipa_mhi_channel_context_array));
IPA_UT_DBG("pMmio->ccabap 0x%llx\n", p_mmio->ccabap);
/**
* 64-bit pointer to the event context array in the host memory space;
* Host sets the pointer to the event context array
* during initialization
*/
p_mmio->ecabap = (u32)ev_ctx_array->phys_base -
(IPA_MHI_TEST_FIRST_EVENT_RING_ID *
sizeof(struct ipa_mhi_event_context_array));
IPA_UT_DBG("pMmio->ecabap 0x%llx\n", p_mmio->ecabap);
/**
* Register is not accessed by HWP.
* In test register carries the pointer of
* virtual address for the buffer of channel context array
*/
p_mmio->crcbap = (unsigned long)ch_ctx_array->base;
/**
* Register is not accessed by HWP.
* In test register carries the pointer of
* virtual address for the buffer of channel context array
*/
p_mmio->crdb = (unsigned long)ev_ctx_array->base;
/* test is running only on device. no need to translate addresses */
p_mmio->mhiaddr.mhicrtlbase = 0x04;
p_mmio->mhiaddr.mhictrllimit = 0xFFFFFFFF;
p_mmio->mhiaddr.mhidatabase = 0x04;
p_mmio->mhiaddr.mhidatalimit = 0xFFFFFFFF;
return rc;
fail_free_ev_ctx_arr:
dma_free_coherent(ipa3_ctx->pdev, ev_ctx_array->size,
ev_ctx_array->base, ev_ctx_array->phys_base);
ev_ctx_array->base = NULL;
fail_free_ch_ctx_arr:
dma_free_coherent(ipa3_ctx->pdev, ch_ctx_array->size,
ch_ctx_array->base, ch_ctx_array->phys_base);
ch_ctx_array->base = NULL;
fail_free_msi:
dma_free_coherent(ipa3_ctx->pdev, msi->size, msi->base,
msi->phys_base);
msi->base = NULL;
return rc;
}
static void ipa_mhi_test_destroy_channel_context(
struct ipa_mem_buffer transfer_ring_bufs[],
struct ipa_mem_buffer event_ring_bufs[],
u8 channel_id,
u8 event_ring_id)
{
u32 ev_ring_idx;
u32 ch_idx;
IPA_UT_DBG("Entry\n");
if ((channel_id < IPA_MHI_TEST_FIRST_CHANNEL_ID) ||
(channel_id > IPA_MHI_TEST_LAST_CHANNEL_ID)) {
IPA_UT_ERR("channal_id invalid %d\n", channel_id);
return;
}
if ((event_ring_id < IPA_MHI_TEST_FIRST_EVENT_RING_ID) ||
(event_ring_id > IPA_MHI_TEST_LAST_EVENT_RING_ID)) {
IPA_UT_ERR("event_ring_id invalid %d\n", event_ring_id);
return;
}
ch_idx = channel_id - IPA_MHI_TEST_FIRST_CHANNEL_ID;
ev_ring_idx = event_ring_id - IPA_MHI_TEST_FIRST_EVENT_RING_ID;
if (transfer_ring_bufs[ch_idx].base) {
dma_free_coherent(ipa3_ctx->pdev,
transfer_ring_bufs[ch_idx].size,
transfer_ring_bufs[ch_idx].base,
transfer_ring_bufs[ch_idx].phys_base);
transfer_ring_bufs[ch_idx].base = NULL;
}
if (event_ring_bufs[ev_ring_idx].base) {
dma_free_coherent(ipa3_ctx->pdev,
event_ring_bufs[ev_ring_idx].size,
event_ring_bufs[ev_ring_idx].base,
event_ring_bufs[ev_ring_idx].phys_base);
event_ring_bufs[ev_ring_idx].base = NULL;
}
}
static int ipa_mhi_test_config_channel_context(
struct ipa_mem_buffer *mmio,
struct ipa_mem_buffer transfer_ring_bufs[],
struct ipa_mem_buffer event_ring_bufs[],
u8 channel_id,
u8 event_ring_id,
u16 transfer_ring_size,
u16 event_ring_size,
u8 ch_type)
{
struct ipa_mhi_mmio_register_set *p_mmio;
struct ipa_mhi_channel_context_array *p_channels;
struct ipa_mhi_event_context_array *p_events;
u32 ev_ring_idx;
u32 ch_idx;
IPA_UT_DBG("Entry\n");
if ((channel_id < IPA_MHI_TEST_FIRST_CHANNEL_ID) ||
(channel_id > IPA_MHI_TEST_LAST_CHANNEL_ID)) {
IPA_UT_DBG("channal_id invalid %d\n", channel_id);
return -EFAULT;
}
if ((event_ring_id < IPA_MHI_TEST_FIRST_EVENT_RING_ID) ||
(event_ring_id > IPA_MHI_TEST_LAST_EVENT_RING_ID)) {
IPA_UT_DBG("event_ring_id invalid %d\n", event_ring_id);
return -EFAULT;
}
p_mmio = (struct ipa_mhi_mmio_register_set *)mmio->base;
p_channels =
(struct ipa_mhi_channel_context_array *)
((unsigned long)p_mmio->crcbap);
p_events = (struct ipa_mhi_event_context_array *)
((unsigned long)p_mmio->crdb);
IPA_UT_DBG("p_mmio: %pK p_channels: %pK p_events: %pK\n",
p_mmio, p_channels, p_events);
ch_idx = channel_id - IPA_MHI_TEST_FIRST_CHANNEL_ID;
ev_ring_idx = event_ring_id - IPA_MHI_TEST_FIRST_EVENT_RING_ID;
IPA_UT_DBG("ch_idx: %u ev_ring_idx: %u\n", ch_idx, ev_ring_idx);
if (transfer_ring_bufs[ch_idx].base) {
IPA_UT_ERR("ChannelId %d is already allocated\n", channel_id);
return -EFAULT;
}
/* allocate and init event ring if needed */
if (!event_ring_bufs[ev_ring_idx].base) {
IPA_UT_LOG("Configuring event ring...\n");
event_ring_bufs[ev_ring_idx].size =
event_ring_size *
sizeof(struct ipa_mhi_event_ring_element);
event_ring_bufs[ev_ring_idx].base =
dma_alloc_coherent(ipa3_ctx->pdev,
event_ring_bufs[ev_ring_idx].size,
&event_ring_bufs[ev_ring_idx].phys_base,
GFP_KERNEL);
if (!event_ring_bufs[ev_ring_idx].base) {
IPA_UT_ERR("no mem for ev ring buf\n");
return -ENOMEM;
}
p_events[ev_ring_idx].intmodc = 1;
p_events[ev_ring_idx].intmodt = 0;
p_events[ev_ring_idx].msivec = event_ring_id;
p_events[ev_ring_idx].rbase =
(u32)event_ring_bufs[ev_ring_idx].phys_base;
p_events[ev_ring_idx].rlen =
event_ring_bufs[ev_ring_idx].size;
p_events[ev_ring_idx].rp =
(u32)event_ring_bufs[ev_ring_idx].phys_base;
p_events[ev_ring_idx].wp =
(u32)event_ring_bufs[ev_ring_idx].phys_base;
} else {
IPA_UT_LOG("Skip configuring event ring - already done\n");
}
transfer_ring_bufs[ch_idx].size =
transfer_ring_size *
sizeof(struct ipa_mhi_transfer_ring_element);
transfer_ring_bufs[ch_idx].base =
dma_alloc_coherent(ipa3_ctx->pdev,
transfer_ring_bufs[ch_idx].size,
&transfer_ring_bufs[ch_idx].phys_base,
GFP_KERNEL);
if (!transfer_ring_bufs[ch_idx].base) {
IPA_UT_ERR("no mem for xfer ring buf\n");
dma_free_coherent(ipa3_ctx->pdev,
event_ring_bufs[ev_ring_idx].size,
event_ring_bufs[ev_ring_idx].base,
event_ring_bufs[ev_ring_idx].phys_base);
event_ring_bufs[ev_ring_idx].base = NULL;
return -ENOMEM;
}
p_channels[ch_idx].erindex = event_ring_id;
p_channels[ch_idx].rbase = (u32)transfer_ring_bufs[ch_idx].phys_base;
p_channels[ch_idx].rlen = transfer_ring_bufs[ch_idx].size;
p_channels[ch_idx].rp = (u32)transfer_ring_bufs[ch_idx].phys_base;
p_channels[ch_idx].wp = (u32)transfer_ring_bufs[ch_idx].phys_base;
p_channels[ch_idx].chtype = ch_type;
p_channels[ch_idx].brsmode = IPA_MHI_BURST_MODE_DEFAULT;
p_channels[ch_idx].pollcfg = 0;
return 0;
}
static void ipa_mhi_test_destroy_data_structures(void)
{
IPA_UT_DBG("Entry\n");
/* Destroy OUT data buffer */
if (test_mhi_ctx->out_buffer.base) {
dma_free_coherent(ipa3_ctx->pdev,
test_mhi_ctx->out_buffer.size,
test_mhi_ctx->out_buffer.base,
test_mhi_ctx->out_buffer.phys_base);
test_mhi_ctx->out_buffer.base = NULL;
}
/* Destroy IN data buffer */
if (test_mhi_ctx->in_buffer.base) {
dma_free_coherent(ipa3_ctx->pdev,
test_mhi_ctx->in_buffer.size,
test_mhi_ctx->in_buffer.base,
test_mhi_ctx->in_buffer.phys_base);
test_mhi_ctx->in_buffer.base = NULL;
}
/* Destroy IN channel ctx */
ipa_mhi_test_destroy_channel_context(
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
IPA_MHI_TEST_FIRST_EVENT_RING_ID + 1);
/* Destroy OUT channel ctx */
ipa_mhi_test_destroy_channel_context(
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID,
IPA_MHI_TEST_FIRST_EVENT_RING_ID);
}
static int ipa_mhi_test_setup_data_structures(void)
{
int rc = 0;
IPA_UT_DBG("Entry\n");
/* Config OUT Channel Context */
rc = ipa_mhi_test_config_channel_context(
&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID,
IPA_MHI_TEST_FIRST_EVENT_RING_ID,
0x100,
0x80,
IPA_MHI_OUT_CHAHNNEL);
if (rc) {
IPA_UT_ERR("Fail to config OUT ch ctx - err %d", rc);
return rc;
}
/* Config IN Channel Context */
rc = ipa_mhi_test_config_channel_context(
&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
IPA_MHI_TEST_FIRST_EVENT_RING_ID + 1,
0x100,
0x80,
IPA_MHI_IN_CHAHNNEL);
if (rc) {
IPA_UT_ERR("Fail to config IN ch ctx - err %d", rc);
goto fail_destroy_out_ch_ctx;
}
/* allocate IN data buffer */
test_mhi_ctx->in_buffer.size = IPA_MHI_TEST_MAX_DATA_BUF_SIZE;
test_mhi_ctx->in_buffer.base = dma_alloc_coherent(
ipa3_ctx->pdev, test_mhi_ctx->in_buffer.size,
&test_mhi_ctx->in_buffer.phys_base, GFP_KERNEL);
if (!test_mhi_ctx->in_buffer.base) {
IPA_UT_ERR("no mem for In data buffer\n");
rc = -ENOMEM;
goto fail_destroy_in_ch_ctx;
}
memset(test_mhi_ctx->in_buffer.base, 0,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
/* allocate OUT data buffer */
test_mhi_ctx->out_buffer.size = IPA_MHI_TEST_MAX_DATA_BUF_SIZE;
test_mhi_ctx->out_buffer.base = dma_alloc_coherent(
ipa3_ctx->pdev, test_mhi_ctx->out_buffer.size,
&test_mhi_ctx->out_buffer.phys_base, GFP_KERNEL);
if (!test_mhi_ctx->out_buffer.base) {
IPA_UT_ERR("no mem for Out data buffer\n");
rc = -EFAULT;
goto fail_destroy_in_data_buf;
}
memset(test_mhi_ctx->out_buffer.base, 0,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
return 0;
fail_destroy_in_data_buf:
dma_free_coherent(ipa3_ctx->pdev,
test_mhi_ctx->in_buffer.size,
test_mhi_ctx->in_buffer.base,
test_mhi_ctx->in_buffer.phys_base);
test_mhi_ctx->in_buffer.base = NULL;
fail_destroy_in_ch_ctx:
ipa_mhi_test_destroy_channel_context(
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
IPA_MHI_TEST_FIRST_EVENT_RING_ID + 1);
fail_destroy_out_ch_ctx:
ipa_mhi_test_destroy_channel_context(
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID,
IPA_MHI_TEST_FIRST_EVENT_RING_ID);
return 0;
}
/**
* ipa_test_mhi_suite_setup() - Suite setup function
*/
static int ipa_test_mhi_suite_setup(void **ppriv)
{
int rc = 0;
IPA_UT_DBG("Start Setup\n");
if (!gsi_ctx) {
IPA_UT_ERR("No GSI ctx\n");
return -EINVAL;
}
if (!ipa3_ctx) {
IPA_UT_ERR("No IPA ctx\n");
return -EINVAL;
}
test_mhi_ctx = kzalloc(sizeof(struct ipa_test_mhi_context),
GFP_KERNEL);
if (!test_mhi_ctx) {
IPA_UT_ERR("failed allocated ctx\n");
return -ENOMEM;
}
test_mhi_ctx->gsi_mmio = ioremap_nocache(gsi_ctx->per.phys_addr,
gsi_ctx->per.size);
if (!test_mhi_ctx) {
IPA_UT_ERR("failed to remap GSI HW size=%lu\n",
gsi_ctx->per.size);
rc = -EFAULT;
goto fail_free_ctx;
}
rc = ipa_test_mhi_alloc_mmio_space();
if (rc) {
IPA_UT_ERR("failed to alloc mmio space");
goto fail_iounmap;
}
rc = ipa_mhi_test_setup_data_structures();
if (rc) {
IPA_UT_ERR("failed to setup data structures");
goto fail_free_mmio_spc;
}
*ppriv = test_mhi_ctx;
return 0;
fail_free_mmio_spc:
ipa_test_mhi_free_mmio_space();
fail_iounmap:
iounmap(test_mhi_ctx->gsi_mmio);
fail_free_ctx:
kfree(test_mhi_ctx);
test_mhi_ctx = NULL;
return rc;
}
/**
* ipa_test_mhi_suite_teardown() - Suite teardown function
*/
static int ipa_test_mhi_suite_teardown(void *priv)
{
IPA_UT_DBG("Start Teardown\n");
if (!test_mhi_ctx)
return 0;
ipa_mhi_test_destroy_data_structures();
ipa_test_mhi_free_mmio_space();
iounmap(test_mhi_ctx->gsi_mmio);
kfree(test_mhi_ctx);
test_mhi_ctx = NULL;
return 0;
}
/**
* ipa_mhi_test_initialize_driver() - MHI init and possibly start and connect
*
* To be run during tests
* 1. MHI init (Ready state)
* 2. Conditional MHO start and connect (M0 state)
*/
static int ipa_mhi_test_initialize_driver(bool skip_start_and_conn)
{
int rc = 0;
struct ipa_mhi_init_params init_params;
struct ipa_mhi_start_params start_params;
struct ipa_mhi_connect_params prod_params;
struct ipa_mhi_connect_params cons_params;
struct ipa_mhi_mmio_register_set *p_mmio;
struct ipa_mhi_channel_context_array *p_ch_ctx_array;
bool is_dma;
u64 phys_addr;
IPA_UT_LOG("Entry\n");
p_mmio = test_mhi_ctx->mmio_buf.base;
/* start IPA MHI */
memset(&init_params, 0, sizeof(init_params));
init_params.msi.addr_low = test_mhi_ctx->msi.phys_base;
init_params.msi.data = 0x10000000;
init_params.msi.mask = ~0x10000000;
/* MMIO not needed for GSI */
init_params.first_ch_idx = IPA_MHI_TEST_FIRST_CHANNEL_ID;
init_params.first_er_idx = IPA_MHI_TEST_FIRST_EVENT_RING_ID;
init_params.assert_bit40 = false;
init_params.notify = ipa_mhi_test_cb;
init_params.priv = NULL;
init_params.test_mode = true;
rc = ipa_mhi_init(&init_params);
if (rc) {
IPA_UT_LOG("ipa_mhi_init failed %d\n", rc);
return rc;
}
IPA_UT_LOG("Wait async ready event\n");
if (wait_for_completion_timeout(&mhi_test_ready_comp, 10 * HZ) == 0) {
IPA_UT_LOG("timeout waiting for READY event");
IPA_UT_TEST_FAIL_REPORT("failed waiting for state ready");
return -ETIME;
}
if (ipa_mhi_is_using_dma(&is_dma)) {
IPA_UT_LOG("is_dma checkign failed. Is MHI loaded?\n");
IPA_UT_TEST_FAIL_REPORT("failed checking using dma");
return -EPERM;
}
if (is_dma) {
IPA_UT_LOG("init ipa_dma\n");
rc = ipa_dma_init();
if (rc && rc != -EFAULT) {
IPA_UT_LOG("ipa_dma_init failed, %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("failed init dma");
return rc;
}
IPA_UT_LOG("enable ipa_dma\n");
rc = ipa_dma_enable();
if (rc && rc != -EPERM) {
IPA_UT_LOG("ipa_dma_enable failed, %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("failed enable dma");
return rc;
}
}
if (!skip_start_and_conn) {
memset(&start_params, 0, sizeof(start_params));
start_params.channel_context_array_addr = p_mmio->ccabap;
start_params.event_context_array_addr = p_mmio->ecabap;
IPA_UT_LOG("BEFORE mhi_start\n");
rc = ipa_mhi_start(&start_params);
if (rc) {
IPA_UT_LOG("mhi_start failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail start mhi");
return rc;
}
IPA_UT_LOG("AFTER mhi_start\n");
phys_addr = p_mmio->ccabap + (IPA_MHI_TEST_FIRST_CHANNEL_ID *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
IPA_UT_LOG("ch: %d base: 0x%pK phys_addr 0x%llx chstate: %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID,
p_ch_ctx_array, phys_addr,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
memset(&prod_params, 0, sizeof(prod_params));
prod_params.sys.client = IPA_CLIENT_MHI_PROD;
prod_params.sys.ipa_ep_cfg.mode.mode = IPA_DMA;
prod_params.sys.ipa_ep_cfg.mode.dst = IPA_CLIENT_MHI_CONS;
prod_params.sys.ipa_ep_cfg.seq.seq_type =
IPA_MHI_TEST_SEQ_TYPE_DMA;
prod_params.sys.ipa_ep_cfg.seq.set_dynamic = true;
prod_params.channel_id = IPA_MHI_TEST_FIRST_CHANNEL_ID;
IPA_UT_LOG("BEFORE connect_pipe (PROD): client:%d ch_id:%u\n",
prod_params.sys.client, prod_params.channel_id);
rc = ipa_mhi_connect_pipe(&prod_params,
&test_mhi_ctx->prod_hdl);
if (rc) {
IPA_UT_LOG("mhi_connect_pipe failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail connect PROD pipe");
return rc;
}
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_RUN) {
IPA_UT_LOG("MHI_PROD: chstate is not RUN chstate:%s\n",
ipa_mhi_get_state_str(
p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("PROD pipe state is not run");
return -EFAULT;
}
phys_addr = p_mmio->ccabap +
((IPA_MHI_TEST_FIRST_CHANNEL_ID + 1) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
IPA_UT_LOG("ch: %d base: 0x%pK phys_addr 0x%llx chstate: %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
p_ch_ctx_array, phys_addr,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
memset(&cons_params, 0, sizeof(cons_params));
cons_params.sys.client = IPA_CLIENT_MHI_CONS;
cons_params.sys.skip_ep_cfg = true;
cons_params.channel_id = IPA_MHI_TEST_FIRST_CHANNEL_ID + 1;
IPA_UT_LOG("BEFORE connect_pipe (CONS): client:%d ch_id:%u\n",
cons_params.sys.client, cons_params.channel_id);
rc = ipa_mhi_connect_pipe(&cons_params,
&test_mhi_ctx->cons_hdl);
if (rc) {
IPA_UT_LOG("mhi_connect_pipe failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail connect CONS pipe");
return rc;
}
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_RUN) {
IPA_UT_LOG("MHI_CONS: chstate is not RUN chstate:%s\n",
ipa_mhi_get_state_str(
p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("CONS pipe state is not run");
return -EFAULT;
}
}
return 0;
}
/**
* To be run during test
* 1. MHI destroy
* 2. re-configure the channels
*/
static int ipa_mhi_test_destroy(struct ipa_test_mhi_context *ctx)
{
struct ipa_mhi_mmio_register_set *p_mmio;
u64 phys_addr;
struct ipa_mhi_channel_context_array *p_ch_ctx_array;
int rc;
IPA_UT_LOG("Entry\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("Input err invalid ctx\n");
return -EINVAL;
}
p_mmio = ctx->mmio_buf.base;
phys_addr = p_mmio->ccabap +
((IPA_MHI_TEST_FIRST_CHANNEL_ID + 1) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = ctx->ch_ctx_array.base +
(phys_addr - ctx->ch_ctx_array.phys_base);
IPA_UT_LOG("channel id %d (CONS): chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
phys_addr = p_mmio->ccabap +
((IPA_MHI_TEST_FIRST_CHANNEL_ID) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = ctx->ch_ctx_array.base +
(phys_addr - ctx->ch_ctx_array.phys_base);
IPA_UT_LOG("channel id %d (PROD): chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_LOG("MHI Destroy\n");
ipa_mhi_destroy();
IPA_UT_LOG("Post MHI Destroy\n");
ctx->prod_hdl = 0;
ctx->cons_hdl = 0;
dma_free_coherent(ipa3_ctx->pdev, ctx->xfer_ring_bufs[1].size,
ctx->xfer_ring_bufs[1].base, ctx->xfer_ring_bufs[1].phys_base);
ctx->xfer_ring_bufs[1].base = NULL;
IPA_UT_LOG("config channel context for channel %d (MHI CONS)\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1);
rc = ipa_mhi_test_config_channel_context(
&ctx->mmio_buf,
ctx->xfer_ring_bufs,
ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
IPA_MHI_TEST_FIRST_EVENT_RING_ID + 1,
0x100,
0x80,
IPA_MHI_IN_CHAHNNEL);
if (rc) {
IPA_UT_LOG("config channel context failed %d, channel %d\n",
rc, IPA_MHI_TEST_FIRST_CHANNEL_ID + 1);
IPA_UT_TEST_FAIL_REPORT("fail config CONS channel ctx");
return -EFAULT;
}
dma_free_coherent(ipa3_ctx->pdev, ctx->xfer_ring_bufs[0].size,
ctx->xfer_ring_bufs[0].base, ctx->xfer_ring_bufs[0].phys_base);
ctx->xfer_ring_bufs[0].base = NULL;
IPA_UT_LOG("config channel context for channel %d (MHI PROD)\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID);
rc = ipa_mhi_test_config_channel_context(
&ctx->mmio_buf,
ctx->xfer_ring_bufs,
ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID,
IPA_MHI_TEST_FIRST_EVENT_RING_ID,
0x100,
0x80,
IPA_MHI_OUT_CHAHNNEL);
if (rc) {
IPA_UT_LOG("config channel context failed %d, channel %d\n",
rc, IPA_MHI_TEST_FIRST_CHANNEL_ID);
IPA_UT_TEST_FAIL_REPORT("fail config PROD channel ctx");
return -EFAULT;
}
return 0;
}
/**
* To be run during test
* 1. Destroy
* 2. Initialize (to Ready or M0 states)
*/
static int ipa_mhi_test_reset(struct ipa_test_mhi_context *ctx,
bool skip_start_and_conn)
{
int rc;
IPA_UT_LOG("Entry\n");
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("destroy fail");
return rc;
}
rc = ipa_mhi_test_initialize_driver(skip_start_and_conn);
if (rc) {
IPA_UT_LOG("driver init failed skip_start_and_con=%d rc=%d\n",
skip_start_and_conn, rc);
IPA_UT_TEST_FAIL_REPORT("init fail");
return rc;
}
return 0;
}
/**
* To be run during test
* 1. disconnect cons channel
* 2. config cons channel
* 3. disconnect prod channel
* 4. config prod channel
* 5. connect prod
* 6. connect cons
*/
static int ipa_mhi_test_channel_reset(void)
{
int rc;
struct ipa_mhi_connect_params prod_params;
struct ipa_mhi_connect_params cons_params;
struct ipa_mhi_mmio_register_set *p_mmio;
struct ipa_mhi_channel_context_array *p_ch_ctx_array;
u64 phys_addr;
p_mmio = test_mhi_ctx->mmio_buf.base;
IPA_UT_LOG("Before pipe disconnect (CONS) client hdl=%u=\n",
test_mhi_ctx->cons_hdl);
rc = ipa_mhi_disconnect_pipe(test_mhi_ctx->cons_hdl);
if (rc) {
IPA_UT_LOG("disconnect_pipe failed (CONS) %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("CONS pipe disconnect fail");
return -EFAULT;
}
test_mhi_ctx->cons_hdl = 0;
phys_addr = p_mmio->ccabap +
((IPA_MHI_TEST_FIRST_CHANNEL_ID + 1) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
IPA_UT_LOG("chstate is not disabled! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("CONS pipe state is not disabled");
return -EFAULT;
}
dma_free_coherent(ipa3_ctx->pdev,
test_mhi_ctx->xfer_ring_bufs[1].size,
test_mhi_ctx->xfer_ring_bufs[1].base,
test_mhi_ctx->xfer_ring_bufs[1].phys_base);
test_mhi_ctx->xfer_ring_bufs[1].base = NULL;
rc = ipa_mhi_test_config_channel_context(
&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
IPA_MHI_TEST_FIRST_EVENT_RING_ID + 1,
0x100,
0x80,
IPA_MHI_IN_CHAHNNEL);
if (rc) {
IPA_UT_LOG("config_channel_context IN failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail config CONS channel context");
return -EFAULT;
}
IPA_UT_LOG("Before pipe disconnect (CONS) client hdl=%u=\n",
test_mhi_ctx->prod_hdl);
rc = ipa_mhi_disconnect_pipe(test_mhi_ctx->prod_hdl);
if (rc) {
IPA_UT_LOG("disconnect_pipe failed (PROD) %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("PROD pipe disconnect fail");
return -EFAULT;
}
test_mhi_ctx->prod_hdl = 0;
phys_addr = p_mmio->ccabap + ((IPA_MHI_TEST_FIRST_CHANNEL_ID) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
IPA_UT_LOG("chstate is not disabled! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("PROD pipe state is not disabled");
return -EFAULT;
}
dma_free_coherent(ipa3_ctx->pdev, test_mhi_ctx->xfer_ring_bufs[0].size,
test_mhi_ctx->xfer_ring_bufs[0].base,
test_mhi_ctx->xfer_ring_bufs[0].phys_base);
test_mhi_ctx->xfer_ring_bufs[0].base = NULL;
rc = ipa_mhi_test_config_channel_context(
&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID,
IPA_MHI_TEST_FIRST_EVENT_RING_ID,
0x100,
0x80,
IPA_MHI_OUT_CHAHNNEL);
if (rc) {
IPA_UT_LOG("config_channel_context OUT failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("PROD pipe state is not disabled");
return -EFAULT;
}
memset(&prod_params, 0, sizeof(prod_params));
prod_params.sys.client = IPA_CLIENT_MHI_PROD;
prod_params.sys.ipa_ep_cfg.mode.mode = IPA_DMA;
prod_params.sys.ipa_ep_cfg.mode.dst = IPA_CLIENT_MHI_CONS;
prod_params.sys.ipa_ep_cfg.seq.seq_type = IPA_MHI_TEST_SEQ_TYPE_DMA;
prod_params.sys.ipa_ep_cfg.seq.set_dynamic = true;
prod_params.channel_id = IPA_MHI_TEST_FIRST_CHANNEL_ID;
IPA_UT_LOG("BEFORE connect PROD\n");
rc = ipa_mhi_connect_pipe(&prod_params, &test_mhi_ctx->prod_hdl);
if (rc) {
IPA_UT_LOG("connect_pipe failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail connect PROD pipe");
return rc;
}
phys_addr = p_mmio->ccabap + ((IPA_MHI_TEST_FIRST_CHANNEL_ID) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_RUN) {
IPA_UT_LOG("chstate is not run! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("PROD pipe state is not run");
return -EFAULT;
}
memset(&cons_params, 0, sizeof(cons_params));
cons_params.sys.client = IPA_CLIENT_MHI_CONS;
cons_params.sys.skip_ep_cfg = true;
cons_params.channel_id = IPA_MHI_TEST_FIRST_CHANNEL_ID + 1;
IPA_UT_LOG("BEFORE connect CONS\n");
rc = ipa_mhi_connect_pipe(&cons_params, &test_mhi_ctx->cons_hdl);
if (rc) {
IPA_UT_LOG("ipa_mhi_connect_pipe failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail connect CONS pipe");
return rc;
}
phys_addr = p_mmio->ccabap +
((IPA_MHI_TEST_FIRST_CHANNEL_ID + 1) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_RUN) {
IPA_UT_LOG("chstate is not run! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("CONS pipe state is not run");
return -EFAULT;
}
return 0;
}
/**
* To be run during test
* Send data
*/
static int ipa_mhi_test_q_transfer_re(struct ipa_mem_buffer *mmio,
struct ipa_mem_buffer xfer_ring_bufs[],
struct ipa_mem_buffer ev_ring_bufs[],
u8 channel_id,
struct ipa_mem_buffer buf_array[],
int buf_array_size,
bool ieob,
bool ieot,
bool bei,
bool trigger_db)
{
struct ipa_mhi_transfer_ring_element *curr_re;
struct ipa_mhi_mmio_register_set *p_mmio;
struct ipa_mhi_channel_context_array *p_channels;
struct ipa_mhi_event_context_array *p_events;
u32 channel_idx;
u32 event_ring_index;
u32 wp_ofst;
u32 rp_ofst;
u32 next_wp_ofst;
int i;
u32 num_of_ed_to_queue;
IPA_UT_LOG("Entry\n");
p_mmio = (struct ipa_mhi_mmio_register_set *)mmio->base;
p_channels = (struct ipa_mhi_channel_context_array *)
((unsigned long)p_mmio->crcbap);
p_events = (struct ipa_mhi_event_context_array *)
((unsigned long)p_mmio->crdb);
if (ieob)
num_of_ed_to_queue = buf_array_size;
else
num_of_ed_to_queue = ieot ? 1 : 0;
if (channel_id >=
(IPA_MHI_TEST_FIRST_CHANNEL_ID + IPA_MHI_TEST_NUM_CHANNELS) ||
channel_id < IPA_MHI_TEST_FIRST_CHANNEL_ID) {
IPA_UT_LOG("Invalud Channel ID %d\n", channel_id);
return -EFAULT;
}
channel_idx = channel_id - IPA_MHI_TEST_FIRST_CHANNEL_ID;
if (!xfer_ring_bufs[channel_idx].base) {
IPA_UT_LOG("Channel is not allocated\n");
return -EFAULT;
}
if (p_channels[channel_idx].brsmode == IPA_MHI_BURST_MODE_DEFAULT ||
p_channels[channel_idx].brsmode == IPA_MHI_BURST_MODE_ENABLE)
num_of_ed_to_queue += 1; /* for OOB/DB mode event */
/* First queue EDs */
event_ring_index = p_channels[channel_idx].erindex -
IPA_MHI_TEST_FIRST_EVENT_RING_ID;
wp_ofst = (u32)(p_events[event_ring_index].wp -
p_events[event_ring_index].rbase);
if (p_events[event_ring_index].rlen & 0xFFFFFFFF00000000) {
IPA_UT_LOG("invalid ev rlen %llu\n",
p_events[event_ring_index].rlen);
return -EFAULT;
}
next_wp_ofst = (wp_ofst + num_of_ed_to_queue *
sizeof(struct ipa_mhi_event_ring_element)) %
(u32)p_events[event_ring_index].rlen;
/* set next WP */
p_events[event_ring_index].wp =
(u32)p_events[event_ring_index].rbase + next_wp_ofst;
/* write value to event ring doorbell */
IPA_UT_LOG("DB to event 0x%llx: base %pa ofst 0x%x\n",
p_events[event_ring_index].wp,
&(gsi_ctx->per.phys_addr), GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS(
event_ring_index + IPA_MHI_GSI_ER_START, 0));
iowrite32(p_events[event_ring_index].wp,
test_mhi_ctx->gsi_mmio +
GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS(
event_ring_index + IPA_MHI_GSI_ER_START, 0));
for (i = 0; i < buf_array_size; i++) {
/* calculate virtual pointer for current WP and RP */
wp_ofst = (u32)(p_channels[channel_idx].wp -
p_channels[channel_idx].rbase);
rp_ofst = (u32)(p_channels[channel_idx].rp -
p_channels[channel_idx].rbase);
(void)rp_ofst;
curr_re = (struct ipa_mhi_transfer_ring_element *)
((unsigned long)xfer_ring_bufs[channel_idx].base +
wp_ofst);
if (p_channels[channel_idx].rlen & 0xFFFFFFFF00000000) {
IPA_UT_LOG("invalid ch rlen %llu\n",
p_channels[channel_idx].rlen);
return -EFAULT;
}
next_wp_ofst = (wp_ofst +
sizeof(struct ipa_mhi_transfer_ring_element)) %
(u32)p_channels[channel_idx].rlen;
/* write current RE */
curr_re->type = IPA_MHI_RING_ELEMENT_TRANSFER;
curr_re->len = (u16)buf_array[i].size;
curr_re->ptr = (u32)buf_array[i].phys_base;
curr_re->word_C.bits.bei = bei;
curr_re->word_C.bits.ieob = ieob;
curr_re->word_C.bits.ieot = ieot;
/* set next WP */
p_channels[channel_idx].wp =
p_channels[channel_idx].rbase + next_wp_ofst;
if (i == (buf_array_size - 1)) {
/* last buffer */
curr_re->word_C.bits.chain = 0;
if (trigger_db) {
IPA_UT_LOG(
"DB to channel 0x%llx: base %pa ofst 0x%x\n"
, p_channels[channel_idx].wp
, &(gsi_ctx->per.phys_addr)
, GSI_EE_n_GSI_CH_k_DOORBELL_0_OFFS(
channel_idx, 0));
iowrite32(p_channels[channel_idx].wp,
test_mhi_ctx->gsi_mmio +
GSI_EE_n_GSI_CH_k_DOORBELL_0_OFFS(
channel_idx, 0));
}
} else {
curr_re->word_C.bits.chain = 1;
}
}
return 0;
}
/**
* To be run during test
* Send data in loopback (from In to OUT) and compare
*/
static int ipa_mhi_test_loopback_data_transfer(void)
{
struct ipa_mem_buffer *p_mmio;
int i;
int rc;
static int val;
bool timeout = true;
IPA_UT_LOG("Entry\n");
p_mmio = &test_mhi_ctx->mmio_buf;
/* invalidate spare register value (for msi) */
memset(test_mhi_ctx->msi.base, 0xFF, test_mhi_ctx->msi.size);
val++;
memset(test_mhi_ctx->in_buffer.base, 0,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
for (i = 0; i < IPA_MHI_TEST_MAX_DATA_BUF_SIZE; i++)
memset(test_mhi_ctx->out_buffer.base + i, (val + i) & 0xFF, 1);
/* queue RE for IN side and trigger doorbell */
rc = ipa_mhi_test_q_transfer_re(p_mmio,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
&test_mhi_ctx->in_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("q_transfer_re failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail IN q xfer re");
return rc;
}
/* queue REs for OUT side and trigger doorbell */
rc = ipa_mhi_test_q_transfer_re(p_mmio,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID,
&test_mhi_ctx->out_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("q_transfer_re failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail OUT q xfer re");
return rc;
}
IPA_MHI_TEST_CHECK_MSI_INTR(true, timeout);
if (timeout) {
IPA_UT_LOG("transfer timeout. MSI = 0x%x\n",
*((u32 *)test_mhi_ctx->msi.base));
IPA_UT_TEST_FAIL_REPORT("xfter timeout");
return -EFAULT;
}
/* compare the two buffers */
if (memcmp(test_mhi_ctx->in_buffer.base, test_mhi_ctx->out_buffer.base,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE)) {
IPA_UT_LOG("buffer are not equal\n");
IPA_UT_TEST_FAIL_REPORT("non-equal buffers after xfer");
return -EFAULT;
}
return 0;
}
/**
* To be run during test
* Do suspend and check channel states to be suspend if should success
*/
static int ipa_mhi_test_suspend(bool force, bool should_success)
{
int rc;
struct ipa_mhi_mmio_register_set *p_mmio;
struct ipa_mhi_channel_context_array *p_ch_ctx_array;
u64 phys_addr;
IPA_UT_LOG("Entry\n");
rc = ipa_mhi_suspend(force);
if (should_success && rc != 0) {
IPA_UT_LOG("ipa_mhi_suspend failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("suspend failed");
return -EFAULT;
}
if (!should_success && rc != -EAGAIN) {
IPA_UT_LOG("ipa_mhi_suspenddid not return -EAGAIN fail %d\n",
rc);
IPA_UT_TEST_FAIL_REPORT("suspend succeeded unexpectedly");
return -EFAULT;
}
p_mmio = test_mhi_ctx->mmio_buf.base;
phys_addr = p_mmio->ccabap + ((IPA_MHI_TEST_FIRST_CHANNEL_ID + 1) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
if (should_success) {
if (p_ch_ctx_array->chstate !=
IPA_HW_MHI_CHANNEL_STATE_SUSPEND) {
IPA_UT_LOG("chstate is not suspend. ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("channel state not suspend");
return -EFAULT;
}
if (!force && p_ch_ctx_array->rp != p_ch_ctx_array->wp) {
IPA_UT_LOG("rp not updated ch %d rp 0x%llx wp 0x%llx\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
p_ch_ctx_array->rp, p_ch_ctx_array->wp);
IPA_UT_TEST_FAIL_REPORT("rp was not updated");
return -EFAULT;
}
} else {
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_RUN) {
IPA_UT_LOG("chstate is not running! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("channel state not run");
return -EFAULT;
}
}
phys_addr = p_mmio->ccabap + ((IPA_MHI_TEST_FIRST_CHANNEL_ID) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
if (should_success) {
if (p_ch_ctx_array->chstate !=
IPA_HW_MHI_CHANNEL_STATE_SUSPEND) {
IPA_UT_LOG("chstate is not running! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("channel state not suspend");
return -EFAULT;
}
if (!force && p_ch_ctx_array->rp != p_ch_ctx_array->wp) {
IPA_UT_LOG("rp not updated ch %d rp 0x%llx wp 0x%llx\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID,
p_ch_ctx_array->rp, p_ch_ctx_array->wp);
IPA_UT_TEST_FAIL_REPORT("rp was not updated");
return -EFAULT;
}
} else {
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_RUN) {
IPA_UT_LOG("chstate is not running! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("channel state not run");
return -EFAULT;
}
}
return 0;
}
/**
* To be run during test
* Do resume and check channel state to be running
*/
static int ipa_test_mhi_resume(void)
{
int rc;
struct ipa_mhi_mmio_register_set *p_mmio;
struct ipa_mhi_channel_context_array *p_ch_ctx_array;
u64 phys_addr;
rc = ipa_mhi_resume();
if (rc) {
IPA_UT_LOG("resume failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("resume failed");
return -EFAULT;
}
p_mmio = test_mhi_ctx->mmio_buf.base;
phys_addr = p_mmio->ccabap + ((IPA_MHI_TEST_FIRST_CHANNEL_ID + 1) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_RUN) {
IPA_UT_LOG("chstate is not running! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("channel state not run");
return -EFAULT;
}
phys_addr = p_mmio->ccabap + ((IPA_MHI_TEST_FIRST_CHANNEL_ID) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
if (p_ch_ctx_array->chstate != IPA_HW_MHI_CHANNEL_STATE_RUN) {
IPA_UT_LOG("chstate is not running! ch %d chstate %s\n",
IPA_MHI_TEST_FIRST_CHANNEL_ID,
ipa_mhi_get_state_str(p_ch_ctx_array->chstate));
IPA_UT_TEST_FAIL_REPORT("channel state not run");
return -EFAULT;
}
return 0;
}
/**
* To be run during test
* 1. suspend
* 2. queue RE for IN and OUT and send data
* 3. should get MSI timeout due to suspend
* 4. resume
* 5. should get the MSIs now
* 6. comapre the IN and OUT buffers
*/
static int ipa_mhi_test_suspend_resume(void)
{
int rc;
int i;
bool timeout = true;
IPA_UT_LOG("Entry\n");
IPA_UT_LOG("BEFORE suspend\n");
rc = ipa_mhi_test_suspend(false, true);
if (rc) {
IPA_UT_LOG("suspend failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("suspend failed");
return rc;
}
IPA_UT_LOG("AFTER suspend\n");
/* invalidate spare register value (for msi) */
memset(test_mhi_ctx->msi.base, 0xFF, test_mhi_ctx->msi.size);
memset(test_mhi_ctx->in_buffer.base, 0, IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
for (i = 0; i < IPA_MHI_TEST_MAX_DATA_BUF_SIZE; i++)
memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1);
/* queue RE for IN side and trigger doorbell */
rc = ipa_mhi_test_q_transfer_re(&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
&test_mhi_ctx->in_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("ipa_mhi_test_q_transfer_re failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail IN q xfer re");
return rc;
}
/* queue REs for OUT side and trigger doorbell */
rc = ipa_mhi_test_q_transfer_re(&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID,
&test_mhi_ctx->out_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("ipa_mhi_test_q_transfer_re failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail OUT q xfer re");
return rc;
}
IPA_MHI_TEST_CHECK_MSI_INTR(true, timeout);
if (!timeout) {
IPA_UT_LOG("Error: transfer success on suspend\n");
IPA_UT_TEST_FAIL_REPORT("xfer suceeded unexpectedly");
return -EFAULT;
}
IPA_UT_LOG("BEFORE resume\n");
rc = ipa_test_mhi_resume();
if (rc) {
IPA_UT_LOG("ipa_mhi_resume failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("resume fail");
return rc;
}
IPA_UT_LOG("AFTER resume\n");
IPA_MHI_TEST_CHECK_MSI_INTR(true, timeout);
if (timeout) {
IPA_UT_LOG("Error: transfer timeout\n");
IPA_UT_TEST_FAIL_REPORT("xfer timeout");
return -EFAULT;
}
/* compare the two buffers */
if (memcmp(test_mhi_ctx->in_buffer.base,
test_mhi_ctx->out_buffer.base,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE)) {
IPA_UT_LOG("Error: buffers are not equal\n");
IPA_UT_TEST_FAIL_REPORT("non-equal buffers after xfer");
return -EFAULT;
}
return 0;
}
/**
* To be run during test
* 1. enable aggregation
* 2. queue IN RE (ring element)
* 3. allocate skb with data
* 4. send it (this will create open aggr frame)
*/
static int ipa_mhi_test_create_aggr_open_frame(void)
{
struct ipa_ep_cfg_aggr ep_aggr;
struct sk_buff *skb;
int rc;
int i;
u32 aggr_state_active;
IPA_UT_LOG("Entry\n");
memset(&ep_aggr, 0, sizeof(ep_aggr));
ep_aggr.aggr_en = IPA_ENABLE_AGGR;
ep_aggr.aggr = IPA_GENERIC;
ep_aggr.aggr_pkt_limit = 2;
rc = ipa3_cfg_ep_aggr(test_mhi_ctx->cons_hdl, &ep_aggr);
if (rc) {
IPA_UT_LOG("failed to configure aggr");
IPA_UT_TEST_FAIL_REPORT("failed to configure aggr");
return rc;
}
/* invalidate spare register value (for msi) */
memset(test_mhi_ctx->msi.base, 0xFF, test_mhi_ctx->msi.size);
/* queue RE for IN side and trigger doorbell */
rc = ipa_mhi_test_q_transfer_re(&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
&test_mhi_ctx->in_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("ipa_mhi_test_q_transfer_re failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail IN q xfer re");
return rc;
}
skb = dev_alloc_skb(IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
if (!skb) {
IPA_UT_LOG("non mem for skb\n");
IPA_UT_TEST_FAIL_REPORT("fail alloc skb");
return -ENOMEM;
}
skb_put(skb, IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
for (i = 0; i < IPA_MHI_TEST_MAX_DATA_BUF_SIZE; i++) {
memset(skb->data + i, i & 0xFF, 1);
memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1);
}
rc = ipa_tx_dp(IPA_CLIENT_MHI_CONS, skb, NULL);
if (rc) {
IPA_UT_LOG("ipa_tx_dp failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("ipa tx dp fail");
return rc;
}
msleep(20);
aggr_state_active = ipahal_read_reg(IPA_STATE_AGGR_ACTIVE);
IPA_UT_LOG("IPA_STATE_AGGR_ACTIVE 0x%x\n", aggr_state_active);
if (aggr_state_active == 0) {
IPA_UT_LOG("No aggregation frame open!\n");
IPA_UT_TEST_FAIL_REPORT("No aggregation frame open");
return -EFAULT;
}
return 0;
}
/**
* To be run during test
* 1. create open aggr by sending data
* 2. suspend - if force it should succeed, otherwize it fails
* 3. if force - wait for wakeup event - it should arrive
* 4. if force - resume
* 5. force close the aggr.
* 6. wait for MSI - it should arrive
* 7. compare IN and OUT buffers
* 8. disable aggr.
*/
static int ipa_mhi_test_suspend_aggr_open(bool force)
{
int rc;
struct ipa_ep_cfg_aggr ep_aggr;
bool timeout = true;
IPA_UT_LOG("Entry\n");
rc = ipa_mhi_test_create_aggr_open_frame();
if (rc) {
IPA_UT_LOG("failed create open aggr\n");
IPA_UT_TEST_FAIL_REPORT("fail create open aggr");
return rc;
}
if (force)
reinit_completion(&mhi_test_wakeup_comp);
IPA_UT_LOG("BEFORE suspend\n");
/**
* if suspend force, then suspend should succeed.
* otherwize it should fail due to open aggr.
*/
rc = ipa_mhi_test_suspend(force, force);
if (rc) {
IPA_UT_LOG("suspend failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("suspend fail");
return rc;
}
IPA_UT_LOG("AFTER suspend\n");
if (force) {
if (!wait_for_completion_timeout(&mhi_test_wakeup_comp, HZ)) {
IPA_UT_LOG("timeout waiting for wakeup event\n");
IPA_UT_TEST_FAIL_REPORT("timeout waitinf wakeup event");
return -ETIME;
}
IPA_UT_LOG("BEFORE resume\n");
rc = ipa_test_mhi_resume();
if (rc) {
IPA_UT_LOG("resume failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("resume failed");
return rc;
}
IPA_UT_LOG("AFTER resume\n");
}
ipahal_write_reg(IPA_AGGR_FORCE_CLOSE, (1 << test_mhi_ctx->cons_hdl));
IPA_MHI_TEST_CHECK_MSI_INTR(false, timeout);
if (timeout) {
IPA_UT_LOG("fail: transfer not completed\n");
IPA_UT_TEST_FAIL_REPORT("timeout on transferring data");
return -EFAULT;
}
/* compare the two buffers */
if (memcmp(test_mhi_ctx->in_buffer.base,
test_mhi_ctx->out_buffer.base,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE)) {
IPA_UT_LOG("fail: buffer are not equal\n");
IPA_UT_TEST_FAIL_REPORT("non-equal buffers after xfer");
return -EFAULT;
}
memset(&ep_aggr, 0, sizeof(ep_aggr));
rc = ipa3_cfg_ep_aggr(test_mhi_ctx->cons_hdl, &ep_aggr);
if (rc) {
IPA_UT_LOG("failed to configure aggr");
IPA_UT_TEST_FAIL_REPORT("fail to disable aggr");
return rc;
}
return 0;
}
/**
* To be run during test
* 1. suspend
* 2. queue IN RE (ring element)
* 3. allocate skb with data
* 4. send it (this will create open aggr frame)
* 5. wait for wakeup event - it should arrive
* 6. resume
* 7. wait for MSI - it should arrive
* 8. compare IN and OUT buffers
*/
static int ipa_mhi_test_suspend_host_wakeup(void)
{
int rc;
int i;
bool timeout = true;
struct sk_buff *skb;
reinit_completion(&mhi_test_wakeup_comp);
IPA_UT_LOG("BEFORE suspend\n");
rc = ipa_mhi_test_suspend(false, true);
if (rc) {
IPA_UT_LOG("suspend failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("suspend fail");
return rc;
}
IPA_UT_LOG("AFTER suspend\n");
/* invalidate spare register value (for msi) */
memset(test_mhi_ctx->msi.base, 0xFF, test_mhi_ctx->msi.size);
memset(test_mhi_ctx->in_buffer.base, 0, IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
/* queue RE for IN side and trigger doorbell*/
rc = ipa_mhi_test_q_transfer_re(&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
&test_mhi_ctx->in_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("ipa_mhi_test_q_transfer_re failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail IN q xfer re");
return rc;
}
skb = dev_alloc_skb(IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
if (!skb) {
IPA_UT_LOG("non mem for skb\n");
IPA_UT_TEST_FAIL_REPORT("no mem for skb");
return -ENOMEM;
}
skb_put(skb, IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
for (i = 0; i < IPA_MHI_TEST_MAX_DATA_BUF_SIZE; i++) {
memset(skb->data + i, i & 0xFF, 1);
memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1);
}
rc = ipa_tx_dp(IPA_CLIENT_MHI_CONS, skb, NULL);
if (rc) {
IPA_UT_LOG("ipa_tx_dp failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("ipa tx dp fail");
return rc;
}
if (wait_for_completion_timeout(&mhi_test_wakeup_comp, HZ) == 0) {
IPA_UT_LOG("timeout waiting for wakeup event\n");
IPA_UT_TEST_FAIL_REPORT("timeout waiting for wakeup event");
return -ETIME;
}
IPA_UT_LOG("BEFORE resume\n");
rc = ipa_test_mhi_resume();
if (rc) {
IPA_UT_LOG("resume failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("resume fail");
return rc;
}
IPA_UT_LOG("AFTER resume\n");
/* check for MSI interrupt one channels */
IPA_MHI_TEST_CHECK_MSI_INTR(false, timeout);
if (timeout) {
IPA_UT_LOG("fail: transfer timeout\n");
IPA_UT_TEST_FAIL_REPORT("timeout on xfer");
return -EFAULT;
}
/* compare the two buffers */
if (memcmp(test_mhi_ctx->in_buffer.base,
test_mhi_ctx->out_buffer.base,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE)) {
IPA_UT_LOG("fail: buffer are not equal\n");
IPA_UT_TEST_FAIL_REPORT("non-equal buffers after xfer");
return -EFAULT;
}
return 0;
}
/**
* To be run during test
* 1. queue OUT RE/buffer
* 2. wait for MSI on OUT
* 3. Do 1. and 2. till got MSI wait timeout (ch full / holb)
*/
static int ipa_mhi_test_create_full_channel(int *submitted_packets)
{
int i;
bool timeout = true;
int rc;
if (!submitted_packets) {
IPA_UT_LOG("Input error\n");
return -EINVAL;
}
*submitted_packets = 0;
for (i = 0; i < IPA_MHI_TEST_MAX_DATA_BUF_SIZE; i++)
memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1);
do {
/* invalidate spare register value (for msi) */
memset(test_mhi_ctx->msi.base, 0xFF, test_mhi_ctx->msi.size);
IPA_UT_LOG("submitting OUT buffer\n");
timeout = true;
/* queue REs for OUT side and trigger doorbell */
rc = ipa_mhi_test_q_transfer_re(&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID,
&test_mhi_ctx->out_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("ipa_mhi_test_q_transfer_re failed %d\n",
rc);
IPA_UT_TEST_FAIL_REPORT("fail OUT q re");
return rc;
}
(*submitted_packets)++;
IPA_UT_LOG("waiting for MSI\n");
for (i = 0; i < 10; i++) {
if (*((u32 *)test_mhi_ctx->msi.base) ==
(0x10000000 |
(IPA_MHI_TEST_FIRST_EVENT_RING_ID))) {
IPA_UT_LOG("got MSI\n");
timeout = false;
break;
}
msleep(20);
}
} while (!timeout);
return 0;
}
/**
* To be run during test
* 1. queue OUT RE/buffer
* 2. wait for MSI on OUT
* 3. Do 1. and 2. till got MSI wait timeout (ch full)
* 4. suspend - it should fail with -EAGAIN - M1 is rejected
* 5. foreach submitted pkt, do the next steps
* 6. queue IN RE/buffer
* 7. wait for MSI
* 8. compare IN and OUT buffers
*/
static int ipa_mhi_test_suspend_full_channel(bool force)
{
int rc;
bool timeout;
int submitted_packets = 0;
rc = ipa_mhi_test_create_full_channel(&submitted_packets);
if (rc) {
IPA_UT_LOG("fail create full channel\n");
IPA_UT_TEST_FAIL_REPORT("fail create full channel");
return rc;
}
IPA_UT_LOG("BEFORE suspend\n");
rc = ipa_mhi_test_suspend(force, false);
if (rc) {
IPA_UT_LOG("ipa_mhi_suspend did not returned -EAGAIN. rc %d\n",
rc);
IPA_UT_TEST_FAIL_REPORT("test suspend fail");
return -EFAULT;
}
IPA_UT_LOG("AFTER suspend\n");
while (submitted_packets) {
memset(test_mhi_ctx->in_buffer.base, 0,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE);
/* invalidate spare register value (for msi) */
memset(test_mhi_ctx->msi.base, 0xFF, test_mhi_ctx->msi.size);
timeout = true;
/* queue RE for IN side and trigger doorbell */
rc = ipa_mhi_test_q_transfer_re(&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
&test_mhi_ctx->in_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("ipa_mhi_test_q_transfer_re failed %d\n",
rc);
IPA_UT_TEST_FAIL_REPORT("fail IN q re");
return rc;
}
IPA_MHI_TEST_CHECK_MSI_INTR(true, timeout);
if (timeout) {
IPA_UT_LOG("transfer failed - timeout\n");
IPA_UT_TEST_FAIL_REPORT("timeout on xfer");
return -EFAULT;
}
/* compare the two buffers */
if (memcmp(test_mhi_ctx->in_buffer.base,
test_mhi_ctx->out_buffer.base,
IPA_MHI_TEST_MAX_DATA_BUF_SIZE)) {
IPA_UT_LOG("buffer are not equal\n");
IPA_UT_TEST_FAIL_REPORT("non-equal buffers after xfer");
return -EFAULT;
}
submitted_packets--;
}
return 0;
}
/**
* To be called from test
* 1. suspend
* 2. reset to M0 state
*/
static int ipa_mhi_test_suspend_and_reset(struct ipa_test_mhi_context *ctx)
{
int rc;
IPA_UT_LOG("BEFORE suspend\n");
rc = ipa_mhi_test_suspend(false, true);
if (rc) {
IPA_UT_LOG("suspend failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("suspend fail");
return rc;
}
IPA_UT_LOG("AFTER suspend\n");
rc = ipa_mhi_test_reset(ctx, false);
if (rc) {
IPA_UT_LOG("reset failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("reset fail");
return rc;
}
return 0;
}
/**
* To be run during test
* 1. manualy update wp
* 2. suspend - should succeed
* 3. restore wp value
*/
static int ipa_mhi_test_suspend_wp_update(void)
{
int rc;
struct ipa_mhi_mmio_register_set *p_mmio;
struct ipa_mhi_channel_context_array *p_ch_ctx_array;
u64 old_wp;
u64 phys_addr;
/* simulate a write by updating the wp */
p_mmio = test_mhi_ctx->mmio_buf.base;
phys_addr = p_mmio->ccabap + ((IPA_MHI_TEST_FIRST_CHANNEL_ID) *
sizeof(struct ipa_mhi_channel_context_array));
p_ch_ctx_array = test_mhi_ctx->ch_ctx_array.base +
(phys_addr - test_mhi_ctx->ch_ctx_array.phys_base);
old_wp = p_ch_ctx_array->wp;
p_ch_ctx_array->wp += 16;
IPA_UT_LOG("BEFORE suspend\n");
rc = ipa_mhi_test_suspend(false, false);
if (rc) {
IPA_UT_LOG("suspend failed rc %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("suspend fail");
p_ch_ctx_array->wp = old_wp;
return rc;
}
IPA_UT_LOG("AFTER suspend\n");
p_ch_ctx_array->wp = old_wp;
return 0;
}
/**
* To be run during test
* 1. create open aggr by sending data
* 2. channel reset (disconnect/connet)
* 3. validate no aggr. open after reset
* 4. disable aggr.
*/
static int ipa_mhi_test_channel_reset_aggr_open(void)
{
int rc;
u32 aggr_state_active;
struct ipa_ep_cfg_aggr ep_aggr;
IPA_UT_LOG("Entry\n");
rc = ipa_mhi_test_create_aggr_open_frame();
if (rc) {
IPA_UT_LOG("failed create open aggr rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail creare open aggr frame");
return rc;
}
rc = ipa_mhi_test_channel_reset();
if (rc) {
IPA_UT_LOG("channel reset failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("channel reset fail");
return rc;
}
aggr_state_active = ipahal_read_reg(IPA_STATE_AGGR_ACTIVE);
IPADBG("IPA_STATE_AGGR_ACTIVE 0x%x\n", aggr_state_active);
if (aggr_state_active != 0) {
IPA_UT_LOG("aggregation frame open after reset!\n");
IPA_UT_LOG("IPA_STATE_AGGR_ACTIVE 0x%x\n", aggr_state_active);
IPA_UT_TEST_FAIL_REPORT("open aggr after reset");
return -EFAULT;
}
memset(&ep_aggr, 0, sizeof(ep_aggr));
rc = ipa3_cfg_ep_aggr(test_mhi_ctx->cons_hdl, &ep_aggr);
if (rc) {
IPA_UT_LOG("failed to configure aggr");
IPA_UT_TEST_FAIL_REPORT("fail to disable aggr");
return rc;
}
return rc;
}
/**
* To be run during test
* 1. queue OUT RE/buffer
* 2. wait for MSI on OUT
* 3. Do 1. and 2. till got MSI wait timeout (ch full)
* 4. channel reset
* disconnect and reconnect the prod and cons
* 5. queue IN RE/buffer and ring DB
* 6. wait for MSI - should get timeout as channels were reset
* 7. reset again
*/
static int ipa_mhi_test_channel_reset_ipa_holb(void)
{
int rc;
int submitted_packets = 0;
bool timeout;
IPA_UT_LOG("Entry\n");
rc = ipa_mhi_test_create_full_channel(&submitted_packets);
if (rc) {
IPA_UT_LOG("fail create full channel rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail create full channel");
return rc;
}
rc = ipa_mhi_test_channel_reset();
if (rc) {
IPA_UT_LOG("channel reset failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("channel reset fail");
return rc;
}
/* invalidate spare register value (for msi) */
memset(test_mhi_ctx->msi.base, 0xFF, test_mhi_ctx->msi.size);
timeout = true;
/* queue RE for IN side and trigger doorbell */
rc = ipa_mhi_test_q_transfer_re(&test_mhi_ctx->mmio_buf,
test_mhi_ctx->xfer_ring_bufs,
test_mhi_ctx->ev_ring_bufs,
IPA_MHI_TEST_FIRST_CHANNEL_ID + 1,
&test_mhi_ctx->in_buffer,
1,
true,
true,
false,
true);
if (rc) {
IPA_UT_LOG("ipa_mhi_test_q_transfer_re failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail IN q re");
return rc;
}
submitted_packets--;
IPA_MHI_TEST_CHECK_MSI_INTR(true, timeout);
if (!timeout) {
IPA_UT_LOG("transfer succeed although we had reset\n");
IPA_UT_TEST_FAIL_REPORT("xfer succeed although we had reset");
return -EFAULT;
}
rc = ipa_mhi_test_channel_reset();
if (rc) {
IPA_UT_LOG("channel reset failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("channel reset fail");
return rc;
}
return rc;
}
/**
* TEST: mhi reset in READY state
* 1. init to ready state (without start and connect)
* 2. reset (destroy and re-init)
* 2. destroy
*/
static int ipa_mhi_test_reset_ready_state(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(true);
if (rc) {
IPA_UT_LOG("init to Ready state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("fail to init to ready state");
return rc;
}
rc = ipa_mhi_test_reset(ctx, true);
if (rc) {
IPA_UT_LOG("reset failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("reset (destroy/re-init) failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi reset in M0 state
* 1. init to M0 state (with start and connect)
* 2. reset (destroy and re-init)
* 2. destroy
*/
static int ipa_mhi_test_reset_m0_state(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT
("fail to init to M0 state (w/ start and connect)");
return rc;
}
rc = ipa_mhi_test_reset(ctx, false);
if (rc) {
IPA_UT_LOG("reset failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("reset (destroy/re-init) failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi in-loop reset in M0 state
* 1. init to M0 state (with start and connect)
* 2. reset (destroy and re-init) in-loop
* 3. destroy
*/
static int ipa_mhi_test_inloop_reset_m0_state(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT
("fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_reset, rc, ctx, false);
if (rc) {
IPA_UT_LOG("in-loop reset failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT(
"reset (destroy/re-init) in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data with reset
* 1. init to M0 state (with start and connect)
* 2. reset (destroy and re-init)
* 3. loopback data
* 4. reset (destroy and re-init)
* 5. loopback data again
* 6. destroy
*/
static int ipa_mhi_test_loopback_data_with_reset(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
rc = ipa_mhi_test_reset(ctx, false);
if (rc) {
IPA_UT_LOG("reset failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("reset (destroy/re-init) failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_reset(ctx, false);
if (rc) {
IPA_UT_LOG("reset failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("reset (destroy/re-init) failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi reset in suspend state
* 1. init to M0 state (with start and connect)
* 2. suspend
* 3. reset (destroy and re-init)
* 4. destroy
*/
static int ipa_mhi_test_reset_on_suspend(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return -EFAULT;
}
rc = ipa_mhi_test_suspend_and_reset(ctx);
if (rc) {
IPA_UT_LOG("suspend and reset failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("suspend and then reset failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed %d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return -EFAULT;
}
return 0;
}
/**
* TEST: mhi in-loop reset in suspend state
* 1. init to M0 state (with start and connect)
* 2. suspend
* 3. reset (destroy and re-init)
* 4. Do 2 and 3 in loop
* 3. destroy
*/
static int ipa_mhi_test_inloop_reset_on_suspend(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_suspend_and_reset, rc, ctx);
if (rc) {
IPA_UT_LOG("in-loop reset in suspend failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("fail to in-loop reset while suspend");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data with reset
* 1. init to M0 state (with start and connect)
* 2. suspend
* 3. reset (destroy and re-init)
* 4. loopback data
* 5. suspend
* 5. reset (destroy and re-init)
* 6. destroy
*/
static int ipa_mhi_test_loopback_data_with_reset_on_suspend(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
rc = ipa_mhi_test_suspend_and_reset(ctx);
if (rc) {
IPA_UT_LOG("suspend and reset failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("fail to suspend and then reset");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_suspend_and_reset(ctx);
if (rc) {
IPA_UT_LOG("suspend and reset failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("fail to suspend and then reset");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop suspend/resume
* 1. init to M0 state (with start and connect)
* 2. in loop suspend/resume
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_suspend_resume(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_suspend_resume, rc);
if (rc) {
IPA_UT_LOG("suspend resume failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT("in loop suspend/resume failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop suspend/resume with aggr open
* 1. init to M0 state (with start and connect)
* 2. in loop suspend/resume with open aggr.
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_suspend_resume_aggr_open(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_suspend_aggr_open,
rc, false);
if (rc) {
IPA_UT_LOG("suspend resume with aggr open failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT(
"in loop suspend/resume with open aggr failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop force suspend/resume with aggr open
* 1. init to M0 state (with start and connect)
* 2. in loop force suspend/resume with open aggr.
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_force_suspend_resume_aggr_open(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_suspend_aggr_open,
rc, true);
if (rc) {
IPA_UT_LOG("force suspend resume with aggr open failed rc=%d",
rc);
IPA_UT_TEST_FAIL_REPORT(
"in loop force suspend/resume with open aggr failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop suspend/host wakeup resume
* 1. init to M0 state (with start and connect)
* 2. in loop suspend/resume with host wakeup
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_suspend_host_wakeup(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_suspend_host_wakeup, rc);
if (rc) {
IPA_UT_LOG("suspend host wakeup resume failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT(
"in loop suspend/resume with hsot wakeup failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop rejected suspend as full channel
* 1. init to M0 state (with start and connect)
* 2. in loop rejrected suspend
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_reject_suspend_full_channel(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_suspend_full_channel,
rc, false);
if (rc) {
IPA_UT_LOG("full channel rejected suspend failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT(
"in loop rejected suspend due to full channel failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop rejected force suspend as full channel
* 1. init to M0 state (with start and connect)
* 2. in loop force rejected suspend
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_reject_force_suspend_full_channel(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_suspend_full_channel,
rc, true);
if (rc) {
IPA_UT_LOG("full channel rejected force suspend failed rc=%d",
rc);
IPA_UT_TEST_FAIL_REPORT(
"in loop force rejected suspend as full ch failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop suspend after wp manual update
* 1. init to M0 state (with start and connect)
* 2. in loop suspend after wp update
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_suspend_resume_wp_update(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_suspend_wp_update, rc);
if (rc) {
IPA_UT_LOG("suspend after wp update failed rc=%d", rc);
IPA_UT_TEST_FAIL_REPORT(
"in loop suspend after wp update failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop channel reset (disconnect/connect)
* 1. init to M0 state (with start and connect)
* 2. in loop channel reset (disconnect/connect)
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_channel_reset(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_channel_reset, rc);
if (rc) {
IPA_UT_LOG("channel reset (disconnect/connect) failed rc=%d",
rc);
IPA_UT_TEST_FAIL_REPORT("in loop channel reset failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop channel reset (disconnect/connect)
* 1. init to M0 state (with start and connect)
* 2. in loop channel reset (disconnect/connect) with open aggr
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_channel_reset_aggr_open(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_channel_reset_aggr_open, rc);
if (rc) {
IPA_UT_LOG("channel reset (disconnect/connect) failed rc=%d",
rc);
IPA_UT_TEST_FAIL_REPORT(
"in loop channel reset with open aggr failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/**
* TEST: mhi loopback data after in loop channel reset (disconnect/connect)
* 1. init to M0 state (with start and connect)
* 2. in loop channel reset (disconnect/connect) with channel in HOLB
* 3. loopback data
* 4. destroy
*/
static int ipa_mhi_test_in_loop_channel_reset_ipa_holb(void *priv)
{
int rc;
struct ipa_test_mhi_context *ctx = (struct ipa_test_mhi_context *)priv;
IPA_UT_LOG("Test Start\n");
if (unlikely(!ctx)) {
IPA_UT_LOG("No context");
return -EFAULT;
}
rc = ipa_mhi_test_initialize_driver(false);
if (rc) {
IPA_UT_LOG("init to M0 state failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT(
"fail to init to M0 state (w/ start and connect)");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_channel_reset_ipa_holb, rc);
if (rc) {
IPA_UT_LOG("channel reset (disconnect/connect) failed rc=%d",
rc);
IPA_UT_TEST_FAIL_REPORT(
"in loop channel reset with channel HOLB failed");
return rc;
}
IPA_MHI_RUN_TEST_UNIT_IN_LOOP(ipa_mhi_test_loopback_data_transfer, rc);
if (rc) {
IPA_UT_LOG("data loopback failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("loopback data in loop failed");
return rc;
}
rc = ipa_mhi_test_destroy(ctx);
if (rc) {
IPA_UT_LOG("destroy failed rc=%d\n", rc);
IPA_UT_TEST_FAIL_REPORT("destroy failed");
return rc;
}
return 0;
}
/* Suite definition block */
IPA_UT_DEFINE_SUITE_START(mhi, "MHI for GSI",
ipa_test_mhi_suite_setup, ipa_test_mhi_suite_teardown)
{
IPA_UT_ADD_TEST(reset_ready_state,
"reset test in Ready state",
ipa_mhi_test_reset_ready_state,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(reset_m0_state,
"reset test in M0 state",
ipa_mhi_test_reset_m0_state,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(inloop_reset_m0_state,
"several reset iterations in M0 state",
ipa_mhi_test_inloop_reset_m0_state,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(loopback_data_with_reset_on_m0,
"reset before and after loopback data in M0 state",
ipa_mhi_test_loopback_data_with_reset,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(reset_on_suspend,
"reset test in suspend state",
ipa_mhi_test_reset_on_suspend,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(inloop_reset_on_suspend,
"several reset iterations in suspend state",
ipa_mhi_test_inloop_reset_on_suspend,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(loopback_data_with_reset_on_suspend,
"reset before and after loopback data in suspend state",
ipa_mhi_test_loopback_data_with_reset_on_suspend,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(suspend_resume,
"several suspend/resume iterations",
ipa_mhi_test_in_loop_suspend_resume,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(suspend_resume_with_open_aggr,
"several suspend/resume iterations with open aggregation frame",
ipa_mhi_test_in_loop_suspend_resume_aggr_open,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(force_suspend_resume_with_open_aggr,
"several force suspend/resume iterations with open aggregation frame",
ipa_mhi_test_in_loop_force_suspend_resume_aggr_open,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(suspend_resume_with_host_wakeup,
"several suspend and host wakeup resume iterations",
ipa_mhi_test_in_loop_suspend_host_wakeup,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(reject_suspend_channel_full,
"several rejected suspend iterations due to full channel",
ipa_mhi_test_in_loop_reject_suspend_full_channel,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(reject_force_suspend_channel_full,
"several rejected force suspend iterations due to full channel",
ipa_mhi_test_in_loop_reject_force_suspend_full_channel,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(suspend_resume_manual_wp_update,
"several suspend/resume iterations with after simulating writing by wp manual update",
ipa_mhi_test_in_loop_suspend_resume_wp_update,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(channel_reset,
"several channel reset (disconnect/connect) iterations",
ipa_mhi_test_in_loop_channel_reset,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(channel_reset_aggr_open,
"several channel reset (disconnect/connect) iterations with open aggregation frame",
ipa_mhi_test_in_loop_channel_reset_aggr_open,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(channel_reset_ipa_holb,
"several channel reset (disconnect/connect) iterations with channel in HOLB state",
ipa_mhi_test_in_loop_channel_reset_ipa_holb,
true, IPA_HW_v3_0, IPA_HW_MAX),
} IPA_UT_DEFINE_SUITE_END(mhi);