1386 lines
33 KiB
C
1386 lines
33 KiB
C
|
/* Copyright (c) 2012, 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/types.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/list.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/memory.h>
|
||
|
#include <linux/fs.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/dma-mapping.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/miscdevice.h>
|
||
|
#include <asm/uaccess.h>
|
||
|
#include <mach/irqs.h>
|
||
|
#include <mach/msm_iomap.h>
|
||
|
#include <mach/sps.h>
|
||
|
#include "msm_sps_test.h"
|
||
|
|
||
|
/** Module name string */
|
||
|
#define DRV_MAME "sps_test"
|
||
|
#define DRV_VERSION "2.1"
|
||
|
#define BAM_PHYS_ADDR 0x12244000
|
||
|
#define BAM_PHYS_SIZE 0x4000
|
||
|
#define TEST_SIGNATURE 0xfacecafe
|
||
|
#define BAMDMA_MAX_CHANS 10
|
||
|
#define P_EVNT_DEST_ADDR(n) (0x102c + 64 * (n))
|
||
|
#define TEST_FAIL -2000
|
||
|
|
||
|
#define SPS_INFO(msg, args...) do { \
|
||
|
if (debug_mode) \
|
||
|
pr_info(msg, ##args); \
|
||
|
else \
|
||
|
pr_debug(msg, ##args); \
|
||
|
} while (0)
|
||
|
|
||
|
struct test_context {
|
||
|
dev_t dev_num;
|
||
|
struct miscdevice *misc_dev;
|
||
|
long testcase;
|
||
|
void *bam;
|
||
|
int is_bam_registered;
|
||
|
void *dma;
|
||
|
u32 signature;
|
||
|
};
|
||
|
|
||
|
struct test_endpoint {
|
||
|
struct sps_pipe *sps;
|
||
|
struct sps_connect connect;
|
||
|
struct sps_register_event reg_event;
|
||
|
int is_bam2bam;
|
||
|
struct sps_iovec IOVec;
|
||
|
struct completion xfer_done;
|
||
|
u32 signature;
|
||
|
};
|
||
|
|
||
|
static struct test_context *sps_test;
|
||
|
int sps_test_num = 0;
|
||
|
int test_type = 0;
|
||
|
int debug_mode = 0;
|
||
|
|
||
|
/**
|
||
|
* Allocate memory from pipe-memory or system memory.
|
||
|
*
|
||
|
* @param use_pipe_mem
|
||
|
* @param mem
|
||
|
*/
|
||
|
static void test_alloc_mem(int use_pipe_mem, struct sps_mem_buffer *mem)
|
||
|
{
|
||
|
if (use_pipe_mem) {
|
||
|
sps_alloc_mem(NULL, SPS_MEM_LOCAL, mem);
|
||
|
} else {
|
||
|
dma_addr_t dma_addr;
|
||
|
|
||
|
mem->base = dma_alloc_coherent(NULL, mem->size, &dma_addr, 0);
|
||
|
mem->phys_base = dma_addr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Free memory from pipe-memory or system memory.
|
||
|
*
|
||
|
* @param use_pipe_mem
|
||
|
* @param mem
|
||
|
*/
|
||
|
static void test_free_mem(int use_pipe_mem, struct sps_mem_buffer *mem)
|
||
|
{
|
||
|
if (use_pipe_mem) {
|
||
|
sps_free_mem(NULL, mem);
|
||
|
} else {
|
||
|
dma_addr_t dma_addr = mem->phys_base;
|
||
|
|
||
|
if (dma_addr)
|
||
|
dma_free_coherent(NULL, mem->size, mem->base, dma_addr);
|
||
|
|
||
|
mem->phys_base = 0;
|
||
|
mem->base = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* print BAM registers' content
|
||
|
*
|
||
|
* @virt_addr
|
||
|
* @pipe
|
||
|
*/
|
||
|
static void print_bam_reg(void *virt_addr, int pipe)
|
||
|
{
|
||
|
int i;
|
||
|
u32 *bam = (u32 *) virt_addr;
|
||
|
u32 ctrl;
|
||
|
u32 ver;
|
||
|
u32 pipes;
|
||
|
|
||
|
if (bam == NULL)
|
||
|
return;
|
||
|
|
||
|
ctrl = bam[0xf80 / 4];
|
||
|
ver = bam[0xf84 / 4];
|
||
|
pipes = bam[0xfbc / 4];
|
||
|
|
||
|
SPS_INFO("-- BAM Controls Registers --\n");
|
||
|
|
||
|
SPS_INFO("BAM_CTRL: 0x%x.\n", ctrl);
|
||
|
SPS_INFO("BAM_REVISION: 0x%x.\n", ver);
|
||
|
SPS_INFO("NUM_PIPES: 0x%x.\n", pipes);
|
||
|
|
||
|
for (i = 0xf80; i < 0x1000; i += 0x10)
|
||
|
SPS_INFO("bam 0x%x: 0x%x,0x%x,0x%x,0x%x.\n",
|
||
|
i,
|
||
|
bam[i / 4],
|
||
|
bam[(i / 4) + 1], bam[(i / 4) + 2], bam[(i / 4) + 3]);
|
||
|
|
||
|
SPS_INFO("-- BAM PIPE %d Management Registers --\n", pipe);
|
||
|
|
||
|
for (i = 0x0000 + 0x80 * pipe; i < 0x0000 + 0x80 * (pipe + 1);
|
||
|
i += 0x10)
|
||
|
SPS_INFO("bam 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i, bam[i / 4],
|
||
|
bam[(i / 4) + 1], bam[(i / 4) + 2], bam[(i / 4) + 3]);
|
||
|
|
||
|
SPS_INFO("-- BAM PIPE %d Configuration Registers --\n", pipe);
|
||
|
|
||
|
for (i = 0x1000 + 0x40 * pipe; i < 0x1000 + 0x40 * (pipe + 1);
|
||
|
i += 0x10)
|
||
|
SPS_INFO("bam 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i, bam[i / 4],
|
||
|
bam[(i / 4) + 1], bam[(i / 4) + 2], bam[(i / 4) + 3]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* print BAM DMA registers
|
||
|
*
|
||
|
* @virt_addr
|
||
|
*/
|
||
|
void print_bamdma_reg(void *virt_addr)
|
||
|
{
|
||
|
int ch; /* channel */
|
||
|
u32 *dma = (u32 *) virt_addr;
|
||
|
|
||
|
if (dma == NULL)
|
||
|
return;
|
||
|
|
||
|
SPS_INFO("-- DMA Registers --\n");
|
||
|
SPS_INFO("dma_enable=%d.\n", dma[0]);
|
||
|
|
||
|
for (ch = 0; ch < BAMDMA_MAX_CHANS; ch++)
|
||
|
SPS_INFO("ch#%d config=0x%x.\n", ch, dma[1 + ch]);
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* register a BAM device
|
||
|
*
|
||
|
* @bam
|
||
|
* @use_irq
|
||
|
*
|
||
|
* @return void*
|
||
|
*/
|
||
|
static void *register_bam(u32 *h, int use_irq)
|
||
|
{
|
||
|
int res = 0;
|
||
|
struct sps_bam_props bam_props = {0};
|
||
|
u32 bam_handle;
|
||
|
|
||
|
bam_props.phys_addr = BAM_PHYS_ADDR;
|
||
|
bam_props.virt_addr = ioremap(BAM_PHYS_ADDR, BAM_PHYS_SIZE);
|
||
|
bam_props.event_threshold = 0x10; /* Pipe event threshold */
|
||
|
bam_props.summing_threshold = 0x10; /* BAM event threshold */
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":bam physical base=0x%x\n",
|
||
|
(u32) bam_props.phys_addr);
|
||
|
SPS_INFO(DRV_MAME ":bam virtual base=0x%x\n",
|
||
|
(u32) bam_props.virt_addr);
|
||
|
|
||
|
if (use_irq)
|
||
|
bam_props.irq = SPS_BAM_DMA_IRQ;
|
||
|
else
|
||
|
bam_props.irq = 0;
|
||
|
|
||
|
res = sps_register_bam_device(&bam_props, &bam_handle);
|
||
|
|
||
|
if (res == 0) {
|
||
|
*h = bam_handle;
|
||
|
sps_test->is_bam_registered = true;
|
||
|
} else if (res) {
|
||
|
/* fall back if BAM-DMA already registered */
|
||
|
SPS_INFO("sps_test:fallback.use sps_dma_get_bam_handle().\n");
|
||
|
bam_handle = sps_dma_get_bam_handle();
|
||
|
if (bam_handle == 0)
|
||
|
return NULL;
|
||
|
*h = bam_handle;
|
||
|
} else
|
||
|
return NULL;
|
||
|
|
||
|
sps_test->bam = bam_props.virt_addr;
|
||
|
|
||
|
return sps_test->bam;
|
||
|
}
|
||
|
|
||
|
static void unregister_bam(void *bam, u32 h)
|
||
|
{
|
||
|
if (bam != NULL)
|
||
|
iounmap(bam);
|
||
|
|
||
|
if (sps_test->is_bam_registered) {
|
||
|
sps_deregister_bam_device(h);
|
||
|
sps_test->is_bam_registered = false;
|
||
|
}
|
||
|
|
||
|
sps_test->bam = NULL;
|
||
|
}
|
||
|
|
||
|
static int wait_xfer_completion(int use_irq,
|
||
|
struct completion *xfer_done,
|
||
|
struct sps_pipe *sps,
|
||
|
int rx_xfers,
|
||
|
u32 max_retry,
|
||
|
u32 delay_ms)
|
||
|
{
|
||
|
struct sps_event_notify dummy_event = {0};
|
||
|
/* Note: sps_get_event() is polling the pipe and trigger the event. */
|
||
|
int i;
|
||
|
|
||
|
if (use_irq) {
|
||
|
wait_for_completion(xfer_done);
|
||
|
} else {
|
||
|
for (i = 0; i < rx_xfers; i++) {
|
||
|
u32 retry = 0;
|
||
|
while (!try_wait_for_completion(xfer_done)) {
|
||
|
sps_get_event(sps, &dummy_event);
|
||
|
|
||
|
if (retry++ >= max_retry)
|
||
|
return -EBUSY;
|
||
|
|
||
|
if (delay_ms)
|
||
|
msleep(delay_ms);
|
||
|
} /* end-of-while */
|
||
|
} /* end-of-for */
|
||
|
} /* end-of-if */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sps_test_sys2bam(int use_irq,
|
||
|
int use_pipe_mem,
|
||
|
int use_cache_mem_data_buf,
|
||
|
int loop_test)
|
||
|
{
|
||
|
int res = 0;
|
||
|
|
||
|
struct test_endpoint test_ep[2];
|
||
|
struct test_endpoint *tx_ep = &test_ep[0];
|
||
|
struct test_endpoint *rx_ep = &test_ep[1];
|
||
|
|
||
|
void *bam = NULL;
|
||
|
u32 h = 0;
|
||
|
struct sps_connect tx_connect = { 0};
|
||
|
struct sps_connect rx_connect = { 0};
|
||
|
|
||
|
struct sps_mem_buffer tx_desc_fifo = { 0};
|
||
|
struct sps_mem_buffer rx_desc_fifo = { 0};
|
||
|
|
||
|
struct sps_mem_buffer tx_mem = { 0};
|
||
|
struct sps_mem_buffer rx_mem = { 0};
|
||
|
|
||
|
struct sps_pipe *tx_h_sps = NULL;
|
||
|
struct sps_pipe *rx_h_sps = NULL;
|
||
|
|
||
|
struct sps_iovec tx_iovec = { 0};
|
||
|
struct sps_iovec rx_iovec = { 0};
|
||
|
u32 *desc = NULL;
|
||
|
u32 *tx_buf = NULL;
|
||
|
u32 *rx_buf = NULL;
|
||
|
|
||
|
u32 tx_pipe = 4;
|
||
|
u32 rx_pipe = 5;
|
||
|
|
||
|
u32 tx_xfer_flags = 0;
|
||
|
u32 rx_xfer_flags = 0;
|
||
|
|
||
|
struct sps_register_event tx_event = { 0};
|
||
|
struct sps_register_event rx_event = { 0};
|
||
|
|
||
|
u32 tx_size = 0x400;
|
||
|
u32 rx_size = 0x800; /* This the max size and not the actual size */
|
||
|
|
||
|
u32 tx_connect_options = 0;
|
||
|
u32 rx_connect_options = 0;
|
||
|
char *irq_mode = use_irq ? "IRQ mode" : "Polling mode";
|
||
|
char *mem_type = use_pipe_mem ? "Pipe-Mem" : "Sys-Mem";
|
||
|
int loop = 0;
|
||
|
int max_loops = 1;
|
||
|
|
||
|
memset(test_ep, 0, sizeof(test_ep));
|
||
|
tx_ep->signature = TEST_SIGNATURE;
|
||
|
rx_ep->signature = TEST_SIGNATURE;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":----Register BAM ----\n");
|
||
|
|
||
|
bam = register_bam(&h, use_irq);
|
||
|
if (bam == NULL) {
|
||
|
res = -1;
|
||
|
goto register_err;
|
||
|
}
|
||
|
|
||
|
tx_ep->sps = sps_alloc_endpoint();
|
||
|
rx_ep->sps = sps_alloc_endpoint();
|
||
|
tx_h_sps = tx_ep->sps;
|
||
|
rx_h_sps = rx_ep->sps;
|
||
|
|
||
|
res = sps_get_config(tx_h_sps, &tx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
res = sps_get_config(rx_h_sps, &rx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":----Connect TX = MEM->BAM = Consumer ----\n");
|
||
|
tx_connect_options =
|
||
|
SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS;
|
||
|
if (!use_irq)
|
||
|
tx_connect_options |= SPS_O_POLL;
|
||
|
|
||
|
tx_connect.source = SPS_DEV_HANDLE_MEM; /* Memory */
|
||
|
tx_connect.destination = h;
|
||
|
tx_connect.mode = SPS_MODE_DEST; /* Consumer pipe */
|
||
|
tx_connect.src_pipe_index = rx_pipe;
|
||
|
tx_connect.dest_pipe_index = tx_pipe;
|
||
|
tx_connect.event_thresh = 0x10;
|
||
|
tx_connect.options = tx_connect_options;
|
||
|
|
||
|
tx_desc_fifo.size = 0x40; /* Use small fifo to force wrap-around */
|
||
|
test_alloc_mem(use_pipe_mem, &tx_desc_fifo);
|
||
|
memset(tx_desc_fifo.base, 0x00, tx_desc_fifo.size);
|
||
|
tx_connect.desc = tx_desc_fifo;
|
||
|
|
||
|
res = sps_connect(tx_h_sps, &tx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":----Connect RX = BAM->MEM = Producer----\n");
|
||
|
rx_connect_options =
|
||
|
SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS;
|
||
|
if (!use_irq)
|
||
|
rx_connect_options |= SPS_O_POLL;
|
||
|
|
||
|
rx_connect.source = h;
|
||
|
rx_connect.destination = SPS_DEV_HANDLE_MEM; /* Memory */
|
||
|
rx_connect.mode = SPS_MODE_SRC; /* Producer pipe */
|
||
|
rx_connect.src_pipe_index = rx_pipe;
|
||
|
rx_connect.dest_pipe_index = tx_pipe;
|
||
|
rx_connect.event_thresh = 0x10;
|
||
|
rx_connect.options = rx_connect_options;
|
||
|
|
||
|
rx_desc_fifo.size = 0x40; /* Use small fifo to force wrap-around */
|
||
|
test_alloc_mem(use_pipe_mem, &rx_desc_fifo);
|
||
|
memset(rx_desc_fifo.base, 0x00, rx_desc_fifo.size);
|
||
|
rx_connect.desc = rx_desc_fifo;
|
||
|
|
||
|
res = sps_connect(rx_h_sps, &rx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Allocate Buffers----\n");
|
||
|
tx_mem.size = tx_size;
|
||
|
rx_mem.size = rx_size;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":Allocating Data Buffers from %s.\n", mem_type);
|
||
|
if (use_cache_mem_data_buf) {
|
||
|
struct sps_mem_buffer *mem;
|
||
|
|
||
|
mem = &tx_mem;
|
||
|
mem->base = kzalloc(mem->size, GFP_KERNEL);
|
||
|
mem->phys_base = 0xdeadbeef; /* map later */
|
||
|
|
||
|
mem = &rx_mem;
|
||
|
mem->base = kzalloc(mem->size, GFP_KERNEL);
|
||
|
mem->phys_base = 0xdeadbeef; /* map later */
|
||
|
} else {
|
||
|
test_alloc_mem(use_pipe_mem, &tx_mem);
|
||
|
test_alloc_mem(use_pipe_mem, &rx_mem);
|
||
|
}
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":%s.tx mem phys=0x%x.virt=0x%x.\n",
|
||
|
__func__, tx_mem.phys_base, (u32) tx_mem.base);
|
||
|
SPS_INFO(DRV_MAME ":%s.rx mem phys=0x%x.virt=0x%x.\n",
|
||
|
__func__, rx_mem.phys_base, (u32) rx_mem.base);
|
||
|
|
||
|
tx_buf = tx_mem.base;
|
||
|
rx_buf = rx_mem.base;
|
||
|
|
||
|
tx_event.mode = SPS_TRIGGER_CALLBACK;
|
||
|
tx_event.options = SPS_O_EOT;
|
||
|
tx_event.user = (void *)tx_ep;
|
||
|
init_completion(&tx_ep->xfer_done);
|
||
|
tx_event.xfer_done = &tx_ep->xfer_done;
|
||
|
SPS_INFO(DRV_MAME ":tx_event.event=0x%x.\n", (u32) tx_event.xfer_done);
|
||
|
|
||
|
rx_event.mode = SPS_TRIGGER_CALLBACK;
|
||
|
rx_event.options = SPS_O_EOT;
|
||
|
rx_event.user = (void *)rx_ep;
|
||
|
init_completion(&rx_ep->xfer_done);
|
||
|
rx_event.xfer_done = &rx_ep->xfer_done;
|
||
|
SPS_INFO(DRV_MAME ":rx_event.event=0x%x.\n", (u32) rx_event.xfer_done);
|
||
|
|
||
|
/* WARNING: MUST register reg_event to enable interrupt
|
||
|
on the pipe level. */
|
||
|
res = sps_register_event(tx_h_sps, &tx_event);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
res = sps_register_event(rx_h_sps, &rx_event);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
if (loop_test)
|
||
|
/* Each loop the tx_size is decremented by 4 */
|
||
|
max_loops = (tx_size / 4) - 4;
|
||
|
|
||
|
for (loop = 0; loop < max_loops; loop++) {
|
||
|
init_completion(&tx_ep->xfer_done);
|
||
|
init_completion(&rx_ep->xfer_done);
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Prepare Buffers----\n");
|
||
|
|
||
|
memset(tx_buf, 0xaa, tx_mem.size);
|
||
|
memset(rx_buf, 0xbb, rx_mem.size);
|
||
|
|
||
|
if (use_cache_mem_data_buf) {
|
||
|
struct sps_mem_buffer *mem;
|
||
|
|
||
|
mem = &tx_mem;
|
||
|
mem->phys_base = dma_map_single(NULL, mem->base,
|
||
|
mem->size, 0);
|
||
|
mem = &rx_mem;
|
||
|
mem->phys_base = dma_map_single(NULL, mem->base,
|
||
|
mem->size, 0);
|
||
|
}
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Start Transfers----\n");
|
||
|
tx_xfer_flags = SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOB |
|
||
|
SPS_IOVEC_FLAG_EOT;
|
||
|
res = sps_transfer_one(tx_h_sps, tx_mem.phys_base, tx_size,
|
||
|
tx_h_sps, tx_xfer_flags);
|
||
|
|
||
|
SPS_INFO("sps_transfer_one tx res = %d.\n", res);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
rx_xfer_flags = SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOB;
|
||
|
res = sps_transfer_one(rx_h_sps, rx_mem.phys_base, rx_size,
|
||
|
rx_h_sps, rx_xfer_flags);
|
||
|
SPS_INFO("sps_transfer_one rx res = %d.\n", res);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
/* wait until transfer completes, instead of polling */
|
||
|
SPS_INFO(DRV_MAME ":-----Wait Transfers Complete----\n");
|
||
|
|
||
|
msleep(10); /* allow transfer to complete */
|
||
|
|
||
|
print_bam_reg(bam, tx_pipe);
|
||
|
print_bam_reg(bam, rx_pipe);
|
||
|
|
||
|
desc = tx_desc_fifo.base;
|
||
|
SPS_INFO("tx desc-fifo at 0x%x = 0x%x,0x%x,0x%x,0x%x.\n",
|
||
|
tx_desc_fifo.phys_base, desc[0], desc[1], desc[2],
|
||
|
desc[3]);
|
||
|
desc = rx_desc_fifo.base;
|
||
|
SPS_INFO("rx desc-fifo at 0x%x = 0x%x,0x%x,0x%x,0x%x.\n",
|
||
|
rx_desc_fifo.phys_base, desc[0], desc[1], desc[2],
|
||
|
desc[3]);
|
||
|
|
||
|
res = wait_xfer_completion(use_irq, &rx_ep->xfer_done,
|
||
|
rx_h_sps, 1, 3, 0);
|
||
|
|
||
|
if (res) {
|
||
|
pr_err("sps_test:%s.transfer not completed.\n",
|
||
|
__func__);
|
||
|
goto exit_err;
|
||
|
}
|
||
|
|
||
|
sps_get_iovec(tx_ep->sps, &tx_ep->IOVec);
|
||
|
sps_get_iovec(rx_ep->sps, &rx_ep->IOVec);
|
||
|
|
||
|
if (use_cache_mem_data_buf) {
|
||
|
struct sps_mem_buffer *mem;
|
||
|
|
||
|
mem = &tx_mem;
|
||
|
dma_unmap_single(NULL, mem->phys_base, mem->size, 0);
|
||
|
mem = &rx_mem;
|
||
|
dma_unmap_single(NULL, mem->phys_base, mem->size, 0);
|
||
|
}
|
||
|
|
||
|
tx_iovec = tx_ep->IOVec;
|
||
|
rx_iovec = rx_ep->IOVec;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Check Pass/Fail----\n");
|
||
|
|
||
|
SPS_INFO("sps_get_iovec tx size = %d.\n", tx_iovec.size);
|
||
|
SPS_INFO("sps_get_iovec rx size = %d.\n", rx_iovec.size);
|
||
|
|
||
|
SPS_INFO("tx buf[0] = 0x%x.\n", tx_buf[0]);
|
||
|
SPS_INFO("rx buf[0] = 0x%x.\n", rx_buf[0]);
|
||
|
SPS_INFO("tx buf[tx_size/4-1] = 0x%x.\n",
|
||
|
tx_buf[tx_size / 4 - 1]);
|
||
|
SPS_INFO("rx buf[tx_size/4-1] = 0x%x.\n",
|
||
|
rx_buf[tx_size / 4 - 1]);
|
||
|
|
||
|
/* NOTE: Rx according to Tx size with EOT. */
|
||
|
if ((tx_iovec.size == tx_size) &&
|
||
|
(rx_iovec.size == tx_size) &&
|
||
|
(tx_buf[0] == rx_buf[0]) &&
|
||
|
(tx_buf[tx_size / 4 - 1] == rx_buf[tx_size / 4 - 1])
|
||
|
) {
|
||
|
res = 0;
|
||
|
if (loop_test)
|
||
|
SPS_INFO("sps:loop#%d PASS.\n", loop);
|
||
|
} else {
|
||
|
res = -1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
tx_size -= 4;
|
||
|
}
|
||
|
|
||
|
exit_err:
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Tear Down----\n");
|
||
|
|
||
|
if (use_cache_mem_data_buf) {
|
||
|
struct sps_mem_buffer *mem;
|
||
|
|
||
|
mem = &tx_mem;
|
||
|
kfree(mem->base);
|
||
|
mem = &rx_mem;
|
||
|
kfree(mem->base);
|
||
|
} else {
|
||
|
test_free_mem(use_pipe_mem, &tx_mem);
|
||
|
test_free_mem(use_pipe_mem, &rx_mem);
|
||
|
}
|
||
|
test_free_mem(use_pipe_mem, &tx_desc_fifo);
|
||
|
test_free_mem(use_pipe_mem, &rx_desc_fifo);
|
||
|
|
||
|
sps_disconnect(tx_h_sps);
|
||
|
sps_disconnect(rx_h_sps);
|
||
|
|
||
|
unregister_bam(bam, h);
|
||
|
|
||
|
register_err:
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
if (res == 0)
|
||
|
SPS_INFO("SPS #%d BAM-DMA SYS-BAM Test %s DESC %s - PASS.\n",
|
||
|
sps_test_num?sps_test_num:test_type, irq_mode, mem_type);
|
||
|
else
|
||
|
pr_info("SPS #%d BAM-DMA SYS-BAM Test %s DESC %s - FAIL.\n",
|
||
|
sps_test_num?sps_test_num:test_type, irq_mode, mem_type);
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
static int sps_test_allloc(void)
|
||
|
{
|
||
|
int res = -1;
|
||
|
|
||
|
struct sps_pipe *sps;
|
||
|
struct sps_mem_buffer mem[10];
|
||
|
enum sps_mem mem_id = SPS_MEM_LOCAL;
|
||
|
void *buf1 = NULL;
|
||
|
void *buf2 = NULL;
|
||
|
int i;
|
||
|
|
||
|
sps = NULL;
|
||
|
|
||
|
memset(mem, 0, sizeof(mem));
|
||
|
|
||
|
for (i = 1; i < (sizeof(mem) / sizeof(mem[0])); i++) {
|
||
|
mem[i].size = i * 0x100;
|
||
|
sps_alloc_mem(sps, mem_id, &mem[i]);
|
||
|
}
|
||
|
buf1 = mem[0].base;
|
||
|
for (i = 1; i < (sizeof(mem) / sizeof(mem[0])); i++) {
|
||
|
mem[i].size = i * 0x100;
|
||
|
sps_free_mem(sps, &mem[i]);
|
||
|
}
|
||
|
for (i = 1; i < (sizeof(mem) / sizeof(mem[0])); i++) {
|
||
|
mem[i].size = i * 0x100;
|
||
|
sps_alloc_mem(sps, mem_id, &mem[i]);
|
||
|
}
|
||
|
buf2 = mem[0].base;
|
||
|
for (i = 1; i < (sizeof(mem) / sizeof(mem[0])); i++) {
|
||
|
mem[i].size = i * 0x100;
|
||
|
sps_free_mem(sps, &mem[i]);
|
||
|
}
|
||
|
|
||
|
if (buf1 == buf2) /* All allocation were properly free. */
|
||
|
res = 0;
|
||
|
else
|
||
|
res = -1;
|
||
|
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
if (res == 0)
|
||
|
SPS_INFO("SPS #%d PIPE-MEM-ALLOC Test - PASS.\n",
|
||
|
sps_test_num?sps_test_num:test_type);
|
||
|
else
|
||
|
pr_info("SPS #%d PIPE-MEM-ALLOC Test - FAIL.\n",
|
||
|
sps_test_num?sps_test_num:test_type);
|
||
|
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
static int sps_test_bamdma_alloc_free_chan(void)
|
||
|
{
|
||
|
int res = -1;
|
||
|
|
||
|
struct sps_alloc_dma_chan alloc[BAMDMA_MAX_CHANS];
|
||
|
struct sps_dma_chan chan[BAMDMA_MAX_CHANS];
|
||
|
u32 h;
|
||
|
u32 tx_pipe; /* consumer = dest */
|
||
|
u32 rx_pipe; /* producer = src */
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 10; i++) {
|
||
|
memset(&alloc[i], 0 , sizeof(alloc[i]));
|
||
|
memset(&chan[i], 0 , sizeof(chan[i]));
|
||
|
|
||
|
alloc[i].threshold = (1024 / 10) * i;
|
||
|
alloc[i].priority = i % 8;
|
||
|
res = sps_alloc_dma_chan(&alloc[i], &chan[i]);
|
||
|
if (res) {
|
||
|
pr_err("sps_alloc_dma_chan.res=%d.\n", res);
|
||
|
goto exit_err;
|
||
|
}
|
||
|
|
||
|
h = chan[i].dev;
|
||
|
rx_pipe = chan[i].src_pipe_index;
|
||
|
tx_pipe = chan[i].dest_pipe_index;
|
||
|
SPS_INFO("bamdma.handle=0x%x.tx_pipe=%d.rx_pipe=%d.\n",
|
||
|
h, tx_pipe, rx_pipe);
|
||
|
|
||
|
if ((tx_pipe != i*2) && (rx_pipe != (i*2+1))) {
|
||
|
pr_err("Pipes index not as expected.\n");
|
||
|
res = -1;
|
||
|
goto exit_err;
|
||
|
}
|
||
|
|
||
|
} /* for() */
|
||
|
|
||
|
for (i = 0; i < 10; i++) {
|
||
|
res = sps_free_dma_chan(&chan[i]);
|
||
|
if (res) {
|
||
|
pr_err("sps_free_dma_chan.res=%d.\n", res);
|
||
|
goto exit_err;
|
||
|
}
|
||
|
} /* for() */
|
||
|
|
||
|
exit_err:
|
||
|
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
if (res)
|
||
|
pr_info("SPS #%d BAM-DMA-CONFIG Test - FAIL.\n",
|
||
|
sps_test_num?sps_test_num:test_type);
|
||
|
else
|
||
|
SPS_INFO("SPS #%d BAM-DMA-CONFIG Test - PASS.\n",
|
||
|
sps_test_num?sps_test_num:test_type);
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/* MULTIPLE DESCRIPTORS PER TRANSFER or MULTIPLE TRANSFERS */
|
||
|
static int sps_test_multi(int tx_eot_per_xfer)
|
||
|
{
|
||
|
int res = 0;
|
||
|
|
||
|
int use_irq = false;
|
||
|
int use_pipe_mem = false;
|
||
|
int use_cache_mem_data_buf = false;
|
||
|
int loop_test = false;
|
||
|
|
||
|
struct test_endpoint test_ep[2];
|
||
|
struct test_endpoint *tx_ep = &test_ep[0];
|
||
|
struct test_endpoint *rx_ep = &test_ep[1];
|
||
|
|
||
|
void *bam = NULL;
|
||
|
u32 h = 0;
|
||
|
struct sps_connect tx_connect = { 0};
|
||
|
struct sps_connect rx_connect = { 0};
|
||
|
|
||
|
struct sps_mem_buffer tx_desc_fifo = { 0};
|
||
|
struct sps_mem_buffer rx_desc_fifo = { 0};
|
||
|
|
||
|
struct sps_mem_buffer tx_mem = { 0};
|
||
|
struct sps_mem_buffer rx_mem = { 0};
|
||
|
|
||
|
struct sps_pipe *tx_h_sps = NULL;
|
||
|
struct sps_pipe *rx_h_sps = NULL;
|
||
|
|
||
|
struct sps_iovec tx_iovec = { 0};
|
||
|
struct sps_iovec rx_iovec = { 0};
|
||
|
u32 *desc = NULL;
|
||
|
u32 *tx_buf = NULL;
|
||
|
u32 *rx_buf = NULL;
|
||
|
|
||
|
u32 tx_pipe = 4;
|
||
|
u32 rx_pipe = 5;
|
||
|
|
||
|
u32 tx_xfer_flags = 0;
|
||
|
u32 rx_xfer_flags = 0;
|
||
|
|
||
|
struct sps_register_event tx_event = { 0};
|
||
|
struct sps_register_event rx_event = { 0};
|
||
|
|
||
|
u32 tx_size = 0x400;
|
||
|
u32 rx_size = 0x800; /* This the max size and not the actual size */
|
||
|
|
||
|
u32 tx_connect_options = 0;
|
||
|
u32 rx_connect_options = 0;
|
||
|
char *irq_mode = use_irq ? "IRQ mode" : "Polling mode";
|
||
|
char *mem_type = use_pipe_mem ? "Pipe-Mem" : "Sys-Mem";
|
||
|
int loop = 0;
|
||
|
int max_loops = 1;
|
||
|
int tx_multi = 1;
|
||
|
int rx_multi = 1;
|
||
|
u32 total_tx_size = 0;
|
||
|
u32 total_rx_size = 0;
|
||
|
int i;
|
||
|
|
||
|
memset(test_ep, 0, sizeof(test_ep));
|
||
|
tx_ep->signature = TEST_SIGNATURE;
|
||
|
rx_ep->signature = TEST_SIGNATURE;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":----Register BAM ----\n");
|
||
|
bam = register_bam(&h, use_irq);
|
||
|
if (bam == NULL) {
|
||
|
res = -1;
|
||
|
goto register_err;
|
||
|
}
|
||
|
|
||
|
tx_ep->sps = sps_alloc_endpoint();
|
||
|
rx_ep->sps = sps_alloc_endpoint();
|
||
|
tx_h_sps = tx_ep->sps;
|
||
|
rx_h_sps = rx_ep->sps;
|
||
|
|
||
|
res = sps_get_config(tx_h_sps, &tx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
res = sps_get_config(rx_h_sps, &rx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":----Connect TX = MEM->BAM = Consumer ----\n");
|
||
|
tx_connect_options =
|
||
|
SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS;
|
||
|
if (!use_irq)
|
||
|
tx_connect_options |= SPS_O_POLL;
|
||
|
|
||
|
tx_connect.source = SPS_DEV_HANDLE_MEM; /* Memory */
|
||
|
tx_connect.destination = h;
|
||
|
tx_connect.mode = SPS_MODE_DEST; /* Consumer pipe */
|
||
|
tx_connect.src_pipe_index = rx_pipe;
|
||
|
tx_connect.dest_pipe_index = tx_pipe;
|
||
|
tx_connect.event_thresh = 0x10;
|
||
|
tx_connect.options = tx_connect_options;
|
||
|
|
||
|
tx_desc_fifo.size = 0x40 + 8; /* Use small fifo to force wrap-around */
|
||
|
test_alloc_mem(use_pipe_mem, &tx_desc_fifo);
|
||
|
memset(tx_desc_fifo.base, 0x00, tx_desc_fifo.size);
|
||
|
tx_connect.desc = tx_desc_fifo;
|
||
|
|
||
|
res = sps_connect(tx_h_sps, &tx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":----Connect RX = BAM->MEM = Producer----\n");
|
||
|
rx_connect_options =
|
||
|
SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS;
|
||
|
if (!use_irq)
|
||
|
rx_connect_options |= SPS_O_POLL;
|
||
|
|
||
|
rx_connect.source = h;
|
||
|
rx_connect.destination = SPS_DEV_HANDLE_MEM; /* Memory */
|
||
|
rx_connect.mode = SPS_MODE_SRC; /* Producer pipe */
|
||
|
rx_connect.src_pipe_index = rx_pipe;
|
||
|
rx_connect.dest_pipe_index = tx_pipe;
|
||
|
rx_connect.event_thresh = 0x10;
|
||
|
rx_connect.options = rx_connect_options;
|
||
|
|
||
|
rx_desc_fifo.size = 0x40 + 8; /* Use small fifo to force wrap-around */
|
||
|
test_alloc_mem(use_pipe_mem, &rx_desc_fifo);
|
||
|
memset(rx_desc_fifo.base, 0x00, rx_desc_fifo.size);
|
||
|
rx_connect.desc = rx_desc_fifo;
|
||
|
|
||
|
res = sps_connect(rx_h_sps, &rx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Allocate Buffers----\n");
|
||
|
tx_mem.size = tx_size;
|
||
|
rx_mem.size = rx_size;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":Allocating Data Buffers from %s.\n", mem_type);
|
||
|
if (use_cache_mem_data_buf) {
|
||
|
struct sps_mem_buffer *mem;
|
||
|
|
||
|
mem = &tx_mem;
|
||
|
mem->base = kzalloc(mem->size, GFP_KERNEL);
|
||
|
mem->phys_base = 0xdeadbeef; /* map later */
|
||
|
|
||
|
mem = &rx_mem;
|
||
|
mem->base = kzalloc(mem->size, GFP_KERNEL);
|
||
|
mem->phys_base = 0xdeadbeef; /* map later */
|
||
|
} else {
|
||
|
test_alloc_mem(use_pipe_mem, &tx_mem);
|
||
|
test_alloc_mem(use_pipe_mem, &rx_mem);
|
||
|
}
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":%s.tx mem phys=0x%x.virt=0x%x.\n",
|
||
|
__func__, tx_mem.phys_base, (u32) tx_mem.base);
|
||
|
SPS_INFO(DRV_MAME ":%s.rx mem phys=0x%x.virt=0x%x.\n",
|
||
|
__func__, rx_mem.phys_base, (u32) rx_mem.base);
|
||
|
|
||
|
tx_buf = tx_mem.base;
|
||
|
rx_buf = rx_mem.base;
|
||
|
|
||
|
tx_event.mode = SPS_TRIGGER_CALLBACK;
|
||
|
tx_event.options = SPS_O_EOT;
|
||
|
tx_event.user = (void *)tx_ep;
|
||
|
init_completion(&tx_ep->xfer_done);
|
||
|
tx_event.xfer_done = &tx_ep->xfer_done;
|
||
|
SPS_INFO(DRV_MAME ":tx_event.event=0x%x.\n", (u32) tx_event.xfer_done);
|
||
|
|
||
|
rx_event.mode = SPS_TRIGGER_CALLBACK;
|
||
|
rx_event.options = SPS_O_EOT;
|
||
|
rx_event.user = (void *)rx_ep;
|
||
|
init_completion(&rx_ep->xfer_done);
|
||
|
rx_event.xfer_done = &rx_ep->xfer_done;
|
||
|
SPS_INFO(DRV_MAME ":rx_event.event=0x%x.\n", (u32) rx_event.xfer_done);
|
||
|
|
||
|
/*
|
||
|
* WARNING: MUST register reg_event to enable interrupt on the
|
||
|
* pipe level.
|
||
|
*/
|
||
|
res = sps_register_event(tx_h_sps, &tx_event);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
res = sps_register_event(rx_h_sps, &rx_event);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
if (loop_test)
|
||
|
/* Each loop the tx_size is decremented by 4 */
|
||
|
max_loops = (tx_size / 0x40) - 4;
|
||
|
|
||
|
for (loop = 0; loop < max_loops; loop++) {
|
||
|
init_completion(&tx_ep->xfer_done);
|
||
|
init_completion(&rx_ep->xfer_done);
|
||
|
|
||
|
if (loop_test)
|
||
|
tx_size -= 0x40;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Prepare Buffers----\n");
|
||
|
|
||
|
memset(tx_buf, 0xaa, tx_mem.size);
|
||
|
memset(rx_buf, 0xbb, rx_mem.size);
|
||
|
|
||
|
if (use_cache_mem_data_buf) {
|
||
|
struct sps_mem_buffer *mem;
|
||
|
|
||
|
mem = &tx_mem;
|
||
|
mem->phys_base =
|
||
|
dma_map_single(NULL, mem->base, mem->size, 0);
|
||
|
mem = &rx_mem;
|
||
|
mem->phys_base =
|
||
|
dma_map_single(NULL, mem->base, mem->size, 0);
|
||
|
}
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Start Transfers----\n");
|
||
|
tx_xfer_flags =
|
||
|
SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOB |
|
||
|
SPS_IOVEC_FLAG_EOT;
|
||
|
tx_multi = (tx_desc_fifo.size / 8) - 1;
|
||
|
for (i = 0; i < tx_multi; i++) {
|
||
|
u32 size = tx_size / tx_multi;
|
||
|
u32 addr = tx_mem.phys_base + i * size;
|
||
|
u32 flags = 0;
|
||
|
|
||
|
if (tx_eot_per_xfer)
|
||
|
/* every desc has EOT */
|
||
|
flags = tx_xfer_flags;
|
||
|
else
|
||
|
/* only the last has EOT */
|
||
|
flags = (i < tx_multi - 1) ? 0 : tx_xfer_flags;
|
||
|
res =
|
||
|
sps_transfer_one(tx_h_sps, addr, size, tx_h_sps,
|
||
|
flags);
|
||
|
SPS_INFO("sps_transfer_one tx res = %d.\n", res);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
}
|
||
|
|
||
|
rx_xfer_flags = SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOB;
|
||
|
rx_multi = tx_eot_per_xfer ? tx_multi : 1;
|
||
|
for (i = 0; i < rx_multi; i++) {
|
||
|
u32 size = rx_size;
|
||
|
u32 addr = 0;
|
||
|
u32 flags = rx_xfer_flags;
|
||
|
|
||
|
if (tx_eot_per_xfer) {
|
||
|
/* for continues data in rx-buf
|
||
|
* the size *must* match the tx transfers
|
||
|
*/
|
||
|
|
||
|
size = tx_size / tx_multi;
|
||
|
}
|
||
|
addr = rx_mem.phys_base + i * size;
|
||
|
|
||
|
res =
|
||
|
sps_transfer_one(rx_h_sps, addr, size, rx_h_sps,
|
||
|
flags);
|
||
|
SPS_INFO("sps_transfer_one rx res = %d.\n", res);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
}
|
||
|
|
||
|
/* wait until transfer completes, instead of polling */
|
||
|
SPS_INFO(DRV_MAME ":-----Wait Transfers Complete----\n");
|
||
|
|
||
|
msleep(10); /* allow transfer to complete */
|
||
|
|
||
|
print_bam_reg(bam, tx_pipe);
|
||
|
print_bam_reg(bam, rx_pipe);
|
||
|
|
||
|
desc = tx_desc_fifo.base;
|
||
|
SPS_INFO("tx desc-fifo at 0x%x:\n", tx_desc_fifo.phys_base);
|
||
|
for (i = 0; i < (tx_desc_fifo.size / 8); i++)
|
||
|
SPS_INFO("%d:0x%x,0x%x.\n", i, desc[i * 2],
|
||
|
desc[i * 2 + 1]);
|
||
|
|
||
|
desc = rx_desc_fifo.base;
|
||
|
SPS_INFO("rx desc-fifo at 0x%x:\n", rx_desc_fifo.phys_base);
|
||
|
for (i = 0; i < (rx_desc_fifo.size / 8); i++)
|
||
|
SPS_INFO("%d:0x%x,0x%x.\n", i, desc[i * 2],
|
||
|
desc[i * 2 + 1]);
|
||
|
|
||
|
res = wait_xfer_completion(use_irq, &rx_ep->xfer_done,
|
||
|
rx_h_sps, rx_multi, 3, 0);
|
||
|
|
||
|
if (res) {
|
||
|
pr_err("sps_test:%s.transfer not completed.\n",
|
||
|
__func__);
|
||
|
goto exit_err;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < tx_multi; i++) {
|
||
|
sps_get_iovec(tx_ep->sps, &tx_ep->IOVec);
|
||
|
total_tx_size += tx_ep->IOVec.size;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < rx_multi; i++) {
|
||
|
sps_get_iovec(rx_ep->sps, &rx_ep->IOVec);
|
||
|
total_rx_size += rx_ep->IOVec.size;
|
||
|
}
|
||
|
|
||
|
if (use_cache_mem_data_buf) {
|
||
|
struct sps_mem_buffer *mem;
|
||
|
|
||
|
mem = &tx_mem;
|
||
|
dma_unmap_single(NULL, mem->phys_base, mem->size, 0);
|
||
|
mem = &rx_mem;
|
||
|
dma_unmap_single(NULL, mem->phys_base, mem->size, 0);
|
||
|
}
|
||
|
|
||
|
tx_iovec = tx_ep->IOVec;
|
||
|
rx_iovec = rx_ep->IOVec;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Check Pass/Fail----\n");
|
||
|
|
||
|
SPS_INFO("sps_get_iovec total_tx_size = %d.\n", total_tx_size);
|
||
|
SPS_INFO("sps_get_iovec total_rx_size = %d.\n", total_rx_size);
|
||
|
|
||
|
SPS_INFO("tx buf[0] = 0x%x.\n", tx_buf[0]);
|
||
|
SPS_INFO("rx buf[0] = 0x%x.\n", rx_buf[0]);
|
||
|
SPS_INFO("tx buf[tx_size/4-1] = 0x%x.\n",
|
||
|
tx_buf[tx_size / 4 - 1]);
|
||
|
SPS_INFO("rx buf[tx_size/4-1] = 0x%x.\n",
|
||
|
rx_buf[tx_size / 4 - 1]);
|
||
|
|
||
|
/* NOTE: Rx according to Tx size with EOT. */
|
||
|
if ((total_tx_size == tx_size) &&
|
||
|
(total_rx_size == tx_size) &&
|
||
|
(tx_buf[0] == rx_buf[0]) &&
|
||
|
(tx_buf[tx_size / 4 - 1] == rx_buf[tx_size / 4 - 1])
|
||
|
) {
|
||
|
res = 0;
|
||
|
if (loop_test)
|
||
|
SPS_INFO("sps:loop#%d PASS.\n", loop);
|
||
|
} else {
|
||
|
res = -1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exit_err:
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":-----Tear Down----\n");
|
||
|
|
||
|
if (use_cache_mem_data_buf) {
|
||
|
struct sps_mem_buffer *mem;
|
||
|
|
||
|
mem = &tx_mem;
|
||
|
kfree(mem->base);
|
||
|
mem = &rx_mem;
|
||
|
kfree(mem->base);
|
||
|
} else {
|
||
|
test_free_mem(use_pipe_mem, &tx_mem);
|
||
|
test_free_mem(use_pipe_mem, &rx_mem);
|
||
|
}
|
||
|
test_free_mem(use_pipe_mem, &tx_desc_fifo);
|
||
|
test_free_mem(use_pipe_mem, &rx_desc_fifo);
|
||
|
|
||
|
sps_disconnect(tx_h_sps);
|
||
|
sps_disconnect(rx_h_sps);
|
||
|
|
||
|
unregister_bam(bam, h);
|
||
|
|
||
|
register_err:
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
if (res == 0)
|
||
|
SPS_INFO("SPS #%d BAM-DMA MULTI Test %s DESC %s - PASS.\n",
|
||
|
sps_test_num?sps_test_num:test_type, irq_mode, mem_type);
|
||
|
else
|
||
|
pr_info("SPS #%d BAM-DMA MULTI Test %s DESC %s - FAIL.\n",
|
||
|
sps_test_num?sps_test_num:test_type, irq_mode, mem_type);
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
static int adversarial(void)
|
||
|
{
|
||
|
int ret = TEST_FAIL;
|
||
|
int res = 0;
|
||
|
|
||
|
struct test_endpoint test_ep[2];
|
||
|
struct test_endpoint *tx_ep = &test_ep[0];
|
||
|
struct test_endpoint *rx_ep = &test_ep[1];
|
||
|
|
||
|
void *bam = NULL;
|
||
|
u32 h = 0;
|
||
|
struct sps_connect tx_connect = { 0};
|
||
|
struct sps_connect rx_connect = { 0};
|
||
|
|
||
|
struct sps_mem_buffer tx_desc_fifo = { 0};
|
||
|
struct sps_mem_buffer rx_desc_fifo = { 0};
|
||
|
|
||
|
struct sps_mem_buffer tx_mem = { 0};
|
||
|
struct sps_mem_buffer rx_mem = { 0};
|
||
|
|
||
|
struct sps_pipe *tx_h_sps = NULL;
|
||
|
struct sps_pipe *rx_h_sps = NULL;
|
||
|
|
||
|
u32 tx_pipe = 1;
|
||
|
u32 rx_pipe = 2;
|
||
|
|
||
|
u32 tx_connect_options = 0;
|
||
|
|
||
|
memset(test_ep, 0, sizeof(test_ep));
|
||
|
tx_ep->signature = TEST_SIGNATURE;
|
||
|
rx_ep->signature = TEST_SIGNATURE;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":----Register BAM ----\n");
|
||
|
|
||
|
bam = register_bam(&h, 0);
|
||
|
if (bam == NULL) {
|
||
|
res = -1;
|
||
|
goto register_err;
|
||
|
}
|
||
|
|
||
|
tx_ep->sps = sps_alloc_endpoint();
|
||
|
rx_ep->sps = sps_alloc_endpoint();
|
||
|
tx_h_sps = tx_ep->sps;
|
||
|
rx_h_sps = rx_ep->sps;
|
||
|
|
||
|
res = sps_get_config(tx_h_sps, &tx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
res = sps_get_config(rx_h_sps, &rx_connect);
|
||
|
if (res)
|
||
|
goto exit_err;
|
||
|
|
||
|
SPS_INFO(DRV_MAME ":----Connect TX = MEM->BAM = Consumer ----\n");
|
||
|
tx_connect_options =
|
||
|
SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS;
|
||
|
tx_connect_options |= SPS_O_POLL;
|
||
|
|
||
|
tx_connect.source = SPS_DEV_HANDLE_MEM; /* Memory */
|
||
|
tx_connect.destination = h;
|
||
|
tx_connect.mode = SPS_MODE_DEST; /* Consumer pipe */
|
||
|
tx_connect.src_pipe_index = rx_pipe;
|
||
|
tx_connect.dest_pipe_index = tx_pipe;
|
||
|
tx_connect.event_thresh = 0x10;
|
||
|
tx_connect.options = tx_connect_options;
|
||
|
|
||
|
tx_desc_fifo.size = 0x40;
|
||
|
test_alloc_mem(1, &tx_desc_fifo);
|
||
|
memset(tx_desc_fifo.base, 0x00, tx_desc_fifo.size);
|
||
|
tx_connect.desc = tx_desc_fifo;
|
||
|
|
||
|
res = sps_connect(tx_h_sps, &tx_connect);
|
||
|
if (res)
|
||
|
ret = 0;
|
||
|
|
||
|
exit_err:
|
||
|
SPS_INFO(DRV_MAME ":-----Tear Down----\n");
|
||
|
test_free_mem(1, &tx_mem);
|
||
|
test_free_mem(1, &rx_mem);
|
||
|
test_free_mem(1, &tx_desc_fifo);
|
||
|
test_free_mem(1, &rx_desc_fifo);
|
||
|
sps_disconnect(tx_h_sps);
|
||
|
unregister_bam(bam, h);
|
||
|
|
||
|
register_err:
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
if (ret)
|
||
|
pr_info("SPS Adversarial Test - FAIL.\n");
|
||
|
else
|
||
|
SPS_INFO("SPS Adversarial Test - PASS.\n");
|
||
|
SPS_INFO("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write File.
|
||
|
*
|
||
|
* @note Trigger the test from user space by:
|
||
|
* echo n > /dev/sps_test
|
||
|
*
|
||
|
*/
|
||
|
ssize_t sps_test_write(struct file *filp, const char __user *buf,
|
||
|
size_t size, loff_t *f_pos)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
unsigned long missing;
|
||
|
static char str[10];
|
||
|
int i, n = 0;
|
||
|
|
||
|
memset(str, 0, sizeof(str));
|
||
|
missing = copy_from_user(str, buf, sizeof(str));
|
||
|
if (missing)
|
||
|
return -EFAULT;
|
||
|
|
||
|
/* Do string to int conversion up-to newline or NULL */
|
||
|
for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
|
||
|
n = (n * 10) + (str[i] - '0');
|
||
|
|
||
|
sps_test->testcase = n;
|
||
|
|
||
|
pr_info(DRV_MAME ":sps_test testcase=%d.\n", (u32) sps_test->testcase);
|
||
|
|
||
|
sps_test_num = (int) sps_test->testcase;
|
||
|
|
||
|
switch (sps_test->testcase) {
|
||
|
case 0:
|
||
|
/* adversarial testing */
|
||
|
ret = adversarial();
|
||
|
break;
|
||
|
case 1:
|
||
|
ret = sps_test_allloc();
|
||
|
break;
|
||
|
case 2:
|
||
|
ret = sps_test_bamdma_alloc_free_chan();
|
||
|
break;
|
||
|
case 3:
|
||
|
/* sys2bam , IRQ , sys-mem */
|
||
|
ret = sps_test_sys2bam(true, false, false, false);
|
||
|
break;
|
||
|
case 4:
|
||
|
/* sys2bam , polling , sys-mem */
|
||
|
ret = sps_test_sys2bam(false, false, false, false);
|
||
|
break;
|
||
|
case 5:
|
||
|
/* EOT per Transfer = Multiple Transfers */
|
||
|
ret = sps_test_multi(true);
|
||
|
break;
|
||
|
case 6:
|
||
|
/*
|
||
|
* EOT on last descriptor only,
|
||
|
* Multiple Descriptors per Transfer.
|
||
|
*/
|
||
|
ret = sps_test_multi(false);
|
||
|
break;
|
||
|
default:
|
||
|
pr_info(DRV_MAME ":Invalid Test Number.\n");
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
static long sps_test_ioctl(struct file *file, unsigned cmd, unsigned long arg)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int err = 0;
|
||
|
|
||
|
/* Verify user arguments. */
|
||
|
if (_IOC_TYPE(cmd) != MSM_SPS_TEST_IOC_MAGIC)
|
||
|
return -ENOTTY;
|
||
|
|
||
|
if (_IOC_DIR(cmd) & _IOC_READ)
|
||
|
err = !access_ok(VERIFY_WRITE, (void __user *)arg,
|
||
|
_IOC_SIZE(cmd));
|
||
|
else if (_IOC_DIR(cmd) & _IOC_WRITE)
|
||
|
err = !access_ok(VERIFY_READ, (void __user *)arg,
|
||
|
_IOC_SIZE(cmd));
|
||
|
if (err)
|
||
|
return -EFAULT;
|
||
|
|
||
|
switch (cmd) {
|
||
|
case MSM_SPS_TEST_IGNORE_FAILURE:
|
||
|
if (__get_user(debug_mode, (int __user *)arg))
|
||
|
return -EINVAL;
|
||
|
if (debug_mode) {
|
||
|
pr_info(DRV_MAME ":Enter debug mode.\n");
|
||
|
pr_info(DRV_MAME ":Continue testing all "
|
||
|
"testcases in case of failure.\n");
|
||
|
}
|
||
|
break;
|
||
|
case MSM_SPS_TEST_TYPE:
|
||
|
if (__get_user(test_type, (int __user *)arg))
|
||
|
return -EINVAL;
|
||
|
|
||
|
switch (test_type) {
|
||
|
case 1:
|
||
|
/* nominal testing */
|
||
|
|
||
|
/* sys2bam , IRQ , sys-mem */
|
||
|
if (sps_test_sys2bam(true, false, false, false))
|
||
|
ret = TEST_FAIL;
|
||
|
|
||
|
/* sys2bam , polling , sys-mem */
|
||
|
if (!ret || debug_mode) {
|
||
|
if (sps_test_sys2bam(false, false, false, false))
|
||
|
ret = TEST_FAIL;
|
||
|
}
|
||
|
|
||
|
/* EOT per Transfer = Multiple Transfers */
|
||
|
if (!ret || debug_mode) {
|
||
|
if (sps_test_multi(true))
|
||
|
ret = TEST_FAIL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* EOT on last descriptor only,
|
||
|
* Multiple Descriptors per Transfer.
|
||
|
*/
|
||
|
if (!ret || debug_mode) {
|
||
|
if (sps_test_multi(false))
|
||
|
ret = TEST_FAIL;
|
||
|
}
|
||
|
|
||
|
if (!ret || debug_mode) {
|
||
|
if (sps_test_allloc())
|
||
|
ret = TEST_FAIL;
|
||
|
}
|
||
|
|
||
|
if (!ret || debug_mode) {
|
||
|
if (sps_test_bamdma_alloc_free_chan())
|
||
|
ret = TEST_FAIL;
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
/* adversarial testing */
|
||
|
if (adversarial())
|
||
|
ret = TEST_FAIL;
|
||
|
break;
|
||
|
default:
|
||
|
pr_info(DRV_MAME ":Invalid test type.\n");
|
||
|
return -ENOTTY;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
default:
|
||
|
return -ENOTTY;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct file_operations sps_test_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.write = sps_test_write,
|
||
|
.unlocked_ioctl = sps_test_ioctl,
|
||
|
};
|
||
|
|
||
|
static struct miscdevice sps_test_dev = {
|
||
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
.name = "sps_test",
|
||
|
.fops = &sps_test_fops,
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Module Init.
|
||
|
*/
|
||
|
static int __init sps_test_init(void)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
pr_info(DRV_MAME ":sps_test_init.\n");
|
||
|
|
||
|
pr_info(DRV_MAME ":SW Version = %s.\n", DRV_VERSION);
|
||
|
|
||
|
sps_test = kzalloc(sizeof(*sps_test), GFP_KERNEL);
|
||
|
if (sps_test == NULL) {
|
||
|
pr_err(DRV_MAME ":kzalloc err.\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
sps_test->signature = TEST_SIGNATURE;
|
||
|
|
||
|
ret = misc_register(&sps_test_dev);
|
||
|
if (ret) {
|
||
|
pr_err(DRV_MAME "register sps_test_dev err.\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
sps_test->misc_dev = &sps_test_dev;
|
||
|
|
||
|
pr_info(DRV_MAME ":SPS-Test init OK.\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Module Exit.
|
||
|
*/
|
||
|
static void __exit sps_test_exit(void)
|
||
|
{
|
||
|
pr_info(DRV_MAME ":sps_test_exit.\n");
|
||
|
|
||
|
msleep(100); /* allow gracefull exit of the worker thread */
|
||
|
|
||
|
misc_deregister(&sps_test_dev);
|
||
|
kfree(sps_test);
|
||
|
|
||
|
pr_info(DRV_MAME ":sps_test_exit complete.\n");
|
||
|
}
|
||
|
|
||
|
module_init(sps_test_init);
|
||
|
module_exit(sps_test_exit);
|
||
|
|
||
|
MODULE_LICENSE("GPL v2");
|
||
|
MODULE_DESCRIPTION("SPS Unit-Test");
|
||
|
|