You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
kernel_samsung_sm7125/drivers/platform/msm/sps/sps.c

3091 lines
75 KiB

/* Copyright (c) 2011-2019, 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.
*/
/* Smart-Peripheral-Switch (SPS) Module. */
#include <linux/types.h> /* u32 */
#include <linux/kernel.h> /* pr_info() */
#include <linux/module.h> /* module_init() */
#include <linux/slab.h> /* kzalloc() */
#include <linux/mutex.h> /* mutex */
#include <linux/device.h> /* device */
#include <linux/fs.h> /* alloc_chrdev_region() */
#include <linux/list.h> /* list_head */
#include <linux/memory.h> /* memset */
#include <linux/io.h> /* ioremap() */
#include <linux/clk.h> /* clk_enable() */
#include <linux/platform_device.h> /* platform_get_resource_byname() */
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "sps_bam.h"
#include "spsi.h"
#include "sps_core.h"
#define SPS_DRV_NAME "msm_sps" /* must match the platform_device name */
/**
* SPS driver state
*/
struct sps_drv *sps;
u32 d_type;
bool enhd_pipe;
bool imem;
enum sps_bam_type bam_type;
enum sps_bam_type bam_types[] = {SPS_BAM_LEGACY, SPS_BAM_NDP, SPS_BAM_NDP_4K};
static void sps_device_de_init(void);
#ifdef CONFIG_DEBUG_FS
u8 debugfs_record_enabled;
u8 logging_option;
u8 debug_level_option;
u8 print_limit_option;
u8 reg_dump_option;
u32 testbus_sel;
u32 bam_pipe_sel;
u32 desc_option;
/*
* Specifies range of log level from level 0 to level 3 to have fine-granularity
* for logging to serve all BAM use cases.
*/
u32 log_level_sel;
static char *debugfs_buf;
static u32 debugfs_buf_size;
static u32 debugfs_buf_used;
static int wraparound;
static struct mutex sps_debugfs_lock;
struct dentry *dent;
struct dentry *dfile_info;
struct dentry *dfile_logging_option;
struct dentry *dfile_debug_level_option;
struct dentry *dfile_print_limit_option;
struct dentry *dfile_reg_dump_option;
struct dentry *dfile_testbus_sel;
struct dentry *dfile_bam_pipe_sel;
struct dentry *dfile_desc_option;
struct dentry *dfile_bam_addr;
struct dentry *dfile_log_level_sel;
static struct sps_bam *phy2bam(phys_addr_t phys_addr);
/* record debug info for debugfs */
void sps_debugfs_record(const char *msg)
{
mutex_lock(&sps_debugfs_lock);
if (debugfs_record_enabled) {
if (debugfs_buf_used + MAX_MSG_LEN >= debugfs_buf_size) {
debugfs_buf_used = 0;
wraparound = true;
}
debugfs_buf_used += scnprintf(debugfs_buf + debugfs_buf_used,
debugfs_buf_size - debugfs_buf_used, msg);
if (wraparound)
scnprintf(debugfs_buf + debugfs_buf_used,
debugfs_buf_size - debugfs_buf_used,
"\n**** end line of sps log ****\n\n");
}
mutex_unlock(&sps_debugfs_lock);
}
/* read the recorded debug info to userspace */
static ssize_t sps_read_info(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
int ret = 0;
int size;
mutex_lock(&sps_debugfs_lock);
if (debugfs_record_enabled) {
if (wraparound)
size = debugfs_buf_size - MAX_MSG_LEN;
else
size = debugfs_buf_used;
ret = simple_read_from_buffer(ubuf, count, ppos,
debugfs_buf, size);
}
mutex_unlock(&sps_debugfs_lock);
return ret;
}
/*
* set the buffer size (in KB) for debug info
*/
static ssize_t sps_set_info(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long missing;
char str[MAX_MSG_LEN];
int i;
u32 buf_size_kb = 0;
u32 new_buf_size;
u32 size = sizeof(str) < count ? sizeof(str) : count;
memset(str, 0, sizeof(str));
missing = copy_from_user(str, buf, size);
if (missing)
return -EFAULT;
for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
buf_size_kb = (buf_size_kb * 10) + (str[i] - '0');
pr_info("sps:debugfs: input buffer size is %dKB\n", buf_size_kb);
if ((logging_option == 0) || (logging_option == 2)) {
pr_info("sps:debugfs: need to first turn on recording.\n");
return -EFAULT;
}
if (buf_size_kb < 1) {
pr_info("sps:debugfs:buffer size should be no less than 1KB\n");
return -EFAULT;
}
if (buf_size_kb > (INT_MAX/SZ_1K)) {
pr_err("sps:debugfs: buffer size is too large\n");
return -EFAULT;
}
new_buf_size = buf_size_kb * SZ_1K;
mutex_lock(&sps_debugfs_lock);
if (debugfs_record_enabled) {
if (debugfs_buf_size == new_buf_size) {
/* need do nothing */
pr_info(
"sps:debugfs: input buffer size is the same as before.\n"
);
mutex_unlock(&sps_debugfs_lock);
return count;
}
/* release the current buffer */
debugfs_record_enabled = false;
debugfs_buf_used = 0;
wraparound = false;
kfree(debugfs_buf);
debugfs_buf = NULL;
}
/* allocate new buffer */
debugfs_buf_size = new_buf_size;
debugfs_buf = kzalloc(debugfs_buf_size, GFP_KERNEL);
if (!debugfs_buf) {
debugfs_buf_size = 0;
pr_err("sps:fail to allocate memory for debug_fs.\n");
mutex_unlock(&sps_debugfs_lock);
return -ENOMEM;
}
debugfs_buf_used = 0;
wraparound = false;
debugfs_record_enabled = true;
mutex_unlock(&sps_debugfs_lock);
return count;
}
const struct file_operations sps_info_ops = {
.read = sps_read_info,
.write = sps_set_info,
};
/* return the current logging option to userspace */
static ssize_t sps_read_logging_option(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
char value[MAX_MSG_LEN];
int nbytes;
nbytes = snprintf(value, MAX_MSG_LEN, "%d\n", logging_option);
return simple_read_from_buffer(ubuf, count, ppos, value, nbytes);
}
/*
* set the logging option
*/
static ssize_t sps_set_logging_option(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long missing;
char str[MAX_MSG_LEN];
int i;
u8 option = 0;
u32 size = sizeof(str) < count ? sizeof(str) : count;
memset(str, 0, sizeof(str));
missing = copy_from_user(str, buf, size);
if (missing)
return -EFAULT;
for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
option = (option * 10) + (str[i] - '0');
pr_info("sps:debugfs: try to change logging option to %d\n", option);
if (option > 3) {
pr_err("sps:debugfs: invalid logging option:%d\n", option);
return count;
}
mutex_lock(&sps_debugfs_lock);
if (((option == 0) || (option == 2)) &&
((logging_option == 1) || (logging_option == 3))) {
debugfs_record_enabled = false;
kfree(debugfs_buf);
debugfs_buf = NULL;
debugfs_buf_used = 0;
debugfs_buf_size = 0;
wraparound = false;
}
logging_option = option;
mutex_unlock(&sps_debugfs_lock);
return count;
}
const struct file_operations sps_logging_option_ops = {
.read = sps_read_logging_option,
.write = sps_set_logging_option,
};
/*
* input the bam physical address
*/
static ssize_t sps_set_bam_addr(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long missing;
char str[MAX_MSG_LEN];
u32 i;
u32 bam_addr = 0;
struct sps_bam *bam;
u32 num_pipes = 0;
void *vir_addr;
u32 size = sizeof(str) < count ? sizeof(str) : count;
memset(str, 0, sizeof(str));
missing = copy_from_user(str, buf, size);
if (missing)
return -EFAULT;
for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
bam_addr = (bam_addr * 10) + (str[i] - '0');
pr_info("sps:debugfs:input BAM physical address:0x%x\n", bam_addr);
bam = phy2bam(bam_addr);
if (bam == NULL) {
pr_err("sps:debugfs:BAM 0x%x is not registered.", bam_addr);
return count;
}
vir_addr = &bam->base;
num_pipes = bam->props.num_pipes;
if (log_level_sel <= SPS_IPC_MAX_LOGLEVEL)
bam->ipc_loglevel = log_level_sel;
switch (reg_dump_option) {
case 1: /* output all registers of this BAM */
print_bam_reg(bam->base);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_reg(bam->base, i);
break;
case 2: /* output BAM-level registers */
print_bam_reg(bam->base);
break;
case 3: /* output selected BAM-level registers */
print_bam_selected_reg(vir_addr, bam->props.ee);
break;
case 4: /* output selected registers of all pipes */
for (i = 0; i < num_pipes; i++)
print_bam_pipe_selected_reg(vir_addr, i);
break;
case 5: /* output selected registers of selected pipes */
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
break;
case 6: /* output selected registers of typical pipes */
print_bam_pipe_selected_reg(vir_addr, 4);
print_bam_pipe_selected_reg(vir_addr, 5);
break;
case 7: /* output desc FIFO of all pipes */
for (i = 0; i < num_pipes; i++)
print_bam_pipe_desc_fifo(vir_addr, i, 0);
break;
case 8: /* output desc FIFO of selected pipes */
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 0);
break;
case 9: /* output desc FIFO of typical pipes */
print_bam_pipe_desc_fifo(vir_addr, 4, 0);
print_bam_pipe_desc_fifo(vir_addr, 5, 0);
break;
case 10: /* output selected registers and desc FIFO of all pipes */
for (i = 0; i < num_pipes; i++) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i, 0);
}
break;
case 11: /* output selected registers and desc FIFO of selected pipes */
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i)) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i, 0);
}
break;
case 12: /* output selected registers and desc FIFO of typical pipes */
print_bam_pipe_selected_reg(vir_addr, 4);
print_bam_pipe_desc_fifo(vir_addr, 4, 0);
print_bam_pipe_selected_reg(vir_addr, 5);
print_bam_pipe_desc_fifo(vir_addr, 5, 0);
break;
case 13: /* output BAM_TEST_BUS_REG */
if (testbus_sel)
print_bam_test_bus_reg(vir_addr, testbus_sel);
else {
pr_info("sps:output TEST_BUS_REG for all TEST_BUS_SEL");
print_bam_test_bus_reg(vir_addr, testbus_sel);
}
break;
case 14: /* output partial desc FIFO of selected pipes */
if (desc_option == 0)
desc_option = 1;
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i,
desc_option);
break;
case 15: /* output partial data blocks of descriptors */
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 100);
break;
case 16: /* output all registers of selected pipes */
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_reg(bam->base, i);
break;
case 91: /*
* output testbus register, BAM global regisers
* and registers of all pipes
*/
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_selected_reg(vir_addr, i);
break;
case 92: /*
* output testbus register, BAM global regisers
* and registers of selected pipes
*/
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
break;
case 93: /*
* output registers and partial desc FIFOs
* of selected pipes: format 1
*/
if (desc_option == 0)
desc_option = 1;
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i,
desc_option);
break;
case 94: /*
* output registers and partial desc FIFOs
* of selected pipes: format 2
*/
if (desc_option == 0)
desc_option = 1;
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i)) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i,
desc_option);
}
break;
case 95: /*
* output registers and desc FIFOs
* of selected pipes: format 1
*/
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 0);
break;
case 96: /*
* output registers and desc FIFOs
* of selected pipes: format 2
*/
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i)) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i, 0);
}
break;
case 97: /*
* output registers, desc FIFOs and partial data blocks
* of selected pipes: format 1
*/
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 0);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 100);
break;
case 98: /*
* output registers, desc FIFOs and partial data blocks
* of selected pipes: format 2
*/
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (bam_pipe_sel & (1UL << i)) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i, 0);
print_bam_pipe_desc_fifo(vir_addr, i, 100);
}
break;
case 99: /* output all registers, desc FIFOs and partial data blocks */
print_bam_test_bus_reg(vir_addr, testbus_sel);
print_bam_reg(bam->base);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_reg(bam->base, i);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_selected_reg(vir_addr, i);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_desc_fifo(vir_addr, i, 0);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_desc_fifo(vir_addr, i, 100);
break;
default:
pr_info("sps:no dump option is chosen yet.");
}
return count;
}
const struct file_operations sps_bam_addr_ops = {
.write = sps_set_bam_addr,
};
static void sps_debugfs_init(void)
{
debugfs_record_enabled = false;
logging_option = 0;
debug_level_option = 0;
print_limit_option = 0;
reg_dump_option = 0;
testbus_sel = 0;
bam_pipe_sel = 0;
desc_option = 0;
debugfs_buf_size = 0;
debugfs_buf_used = 0;
wraparound = false;
log_level_sel = SPS_IPC_MAX_LOGLEVEL + 1;
dent = debugfs_create_dir("sps", 0);
if (IS_ERR(dent)) {
pr_err("sps:fail to create the folder for debug_fs.\n");
return;
}
dfile_info = debugfs_create_file("info", 0664, dent, 0,
&sps_info_ops);
if (!dfile_info || IS_ERR(dfile_info)) {
pr_err("sps:fail to create the file for debug_fs info.\n");
goto info_err;
}
dfile_logging_option = debugfs_create_file("logging_option", 0664,
dent, 0, &sps_logging_option_ops);
if (!dfile_logging_option || IS_ERR(dfile_logging_option)) {
pr_err("sps:fail to create debug_fs for logging_option.\n");
goto logging_option_err;
}
dfile_debug_level_option = debugfs_create_u8("debug_level_option",
0664, dent, &debug_level_option);
if (!dfile_debug_level_option || IS_ERR(dfile_debug_level_option)) {
pr_err("sps:fail to create debug_fs for debug_level_option.\n");
goto debug_level_option_err;
}
dfile_print_limit_option = debugfs_create_u8("print_limit_option",
0664, dent, &print_limit_option);
if (!dfile_print_limit_option || IS_ERR(dfile_print_limit_option)) {
pr_err("sps:fail to create debug_fs for print_limit_option.\n");
goto print_limit_option_err;
}
dfile_reg_dump_option = debugfs_create_u8("reg_dump_option", 0664,
dent, &reg_dump_option);
if (!dfile_reg_dump_option || IS_ERR(dfile_reg_dump_option)) {
pr_err("sps:fail to create debug_fs for reg_dump_option.\n");
goto reg_dump_option_err;
}
dfile_testbus_sel = debugfs_create_u32("testbus_sel", 0664,
dent, &testbus_sel);
if (!dfile_testbus_sel || IS_ERR(dfile_testbus_sel)) {
pr_err("sps:fail to create debug_fs file for testbus_sel.\n");
goto testbus_sel_err;
}
dfile_bam_pipe_sel = debugfs_create_u32("bam_pipe_sel", 0664,
dent, &bam_pipe_sel);
if (!dfile_bam_pipe_sel || IS_ERR(dfile_bam_pipe_sel)) {
pr_err("sps:fail to create debug_fs file for bam_pipe_sel.\n");
goto bam_pipe_sel_err;
}
dfile_desc_option = debugfs_create_u32("desc_option", 0664,
dent, &desc_option);
if (!dfile_desc_option || IS_ERR(dfile_desc_option)) {
pr_err("sps:fail to create debug_fs file for desc_option.\n");
goto desc_option_err;
}
dfile_bam_addr = debugfs_create_file("bam_addr", 0664,
dent, 0, &sps_bam_addr_ops);
if (!dfile_bam_addr || IS_ERR(dfile_bam_addr)) {
pr_err("sps:fail to create the file for debug_fs bam_addr.\n");
goto bam_addr_err;
}
dfile_log_level_sel = debugfs_create_u32("log_level_sel", 0664,
dent, &log_level_sel);
if (!dfile_log_level_sel || IS_ERR(dfile_log_level_sel)) {
pr_err("sps:fail to create debug_fs file for log_level_sel.\n");
goto bam_log_level_err;
}
mutex_init(&sps_debugfs_lock);
return;
bam_log_level_err:
debugfs_remove(dfile_bam_addr);
bam_addr_err:
debugfs_remove(dfile_desc_option);
desc_option_err:
debugfs_remove(dfile_bam_pipe_sel);
bam_pipe_sel_err:
debugfs_remove(dfile_testbus_sel);
testbus_sel_err:
debugfs_remove(dfile_reg_dump_option);
reg_dump_option_err:
debugfs_remove(dfile_print_limit_option);
print_limit_option_err:
debugfs_remove(dfile_debug_level_option);
debug_level_option_err:
debugfs_remove(dfile_logging_option);
logging_option_err:
debugfs_remove(dfile_info);
info_err:
debugfs_remove(dent);
}
static void sps_debugfs_exit(void)
{
debugfs_remove(dfile_info);
debugfs_remove(dfile_logging_option);
debugfs_remove(dfile_debug_level_option);
debugfs_remove(dfile_print_limit_option);
debugfs_remove(dfile_reg_dump_option);
debugfs_remove(dfile_testbus_sel);
debugfs_remove(dfile_bam_pipe_sel);
debugfs_remove(dfile_desc_option);
debugfs_remove(dfile_bam_addr);
debugfs_remove(dent);
debugfs_remove(dfile_log_level_sel);
kfree(debugfs_buf);
debugfs_buf = NULL;
}
#endif
/* Get the debug info of BAM registers and descriptor FIFOs */
int sps_get_bam_debug_info(unsigned long dev, u32 option, u32 para,
u32 tb_sel, u32 desc_sel)
{
int res = 0;
struct sps_bam *bam;
u32 i;
u32 num_pipes = 0;
void *vir_addr;
if (dev == 0) {
SPS_ERR(sps,
"sps:%s:device handle should not be 0.\n", __func__);
return SPS_ERROR;
}
if (sps == NULL || !sps->is_ready) {
SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
return -EPROBE_DEFER;
}
mutex_lock(&sps->lock);
/* Search for the target BAM device */
bam = sps_h2bam(dev);
if (bam == NULL) {
pr_err("sps:Can't find any BAM with handle 0x%pK.",
(void *)dev);
mutex_unlock(&sps->lock);
return SPS_ERROR;
}
mutex_unlock(&sps->lock);
vir_addr = &bam->base;
num_pipes = bam->props.num_pipes;
SPS_DUMP("sps:<bam-addr> dump BAM:%pa.\n", &bam->props.phys_addr);
switch (option) {
case 1: /* output all registers of this BAM */
print_bam_reg(bam->base);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_reg(bam->base, i);
break;
case 2: /* output BAM-level registers */
print_bam_reg(bam->base);
break;
case 3: /* output selected BAM-level registers */
print_bam_selected_reg(vir_addr, bam->props.ee);
break;
case 4: /* output selected registers of all pipes */
for (i = 0; i < num_pipes; i++)
print_bam_pipe_selected_reg(vir_addr, i);
break;
case 5: /* output selected registers of selected pipes */
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
break;
case 6: /* output selected registers of typical pipes */
print_bam_pipe_selected_reg(vir_addr, 4);
print_bam_pipe_selected_reg(vir_addr, 5);
break;
case 7: /* output desc FIFO of all pipes */
for (i = 0; i < num_pipes; i++)
print_bam_pipe_desc_fifo(vir_addr, i, 0);
break;
case 8: /* output desc FIFO of selected pipes */
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 0);
break;
case 9: /* output desc FIFO of typical pipes */
print_bam_pipe_desc_fifo(vir_addr, 4, 0);
print_bam_pipe_desc_fifo(vir_addr, 5, 0);
break;
case 10: /* output selected registers and desc FIFO of all pipes */
for (i = 0; i < num_pipes; i++) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i, 0);
}
break;
case 11: /* output selected registers and desc FIFO of selected pipes */
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i)) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i, 0);
}
break;
case 12: /* output selected registers and desc FIFO of typical pipes */
print_bam_pipe_selected_reg(vir_addr, 4);
print_bam_pipe_desc_fifo(vir_addr, 4, 0);
print_bam_pipe_selected_reg(vir_addr, 5);
print_bam_pipe_desc_fifo(vir_addr, 5, 0);
break;
case 13: /* output BAM_TEST_BUS_REG */
if (tb_sel)
print_bam_test_bus_reg(vir_addr, tb_sel);
else
pr_info("sps:TEST_BUS_SEL should NOT be zero.");
break;
case 14: /* output partial desc FIFO of selected pipes */
if (desc_sel == 0)
desc_sel = 1;
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i,
desc_sel);
break;
case 15: /* output partial data blocks of descriptors */
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 100);
break;
case 16: /* output all registers of selected pipes */
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_reg(bam->base, i);
break;
case 91: /*
* output testbus register, BAM global regisers
* and registers of all pipes
*/
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_selected_reg(vir_addr, i);
break;
case 92: /*
* output testbus register, BAM global regisers
* and registers of selected pipes
*/
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
break;
case 93: /*
* output registers and partial desc FIFOs
* of selected pipes: format 1
*/
if (desc_sel == 0)
desc_sel = 1;
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i,
desc_sel);
break;
case 94: /*
* output registers and partial desc FIFOs
* of selected pipes: format 2
*/
if (desc_sel == 0)
desc_sel = 1;
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i)) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i,
desc_sel);
}
break;
case 95: /*
* output registers and desc FIFOs
* of selected pipes: format 1
*/
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 0);
break;
case 96: /*
* output registers and desc FIFOs
* of selected pipes: format 2
*/
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i)) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i, 0);
}
break;
case 97: /*
* output registers, desc FIFOs and partial data blocks
* of selected pipes: format 1
*/
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_selected_reg(vir_addr, i);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 0);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i))
print_bam_pipe_desc_fifo(vir_addr, i, 100);
break;
case 98: /*
* output registers, desc FIFOs and partial data blocks
* of selected pipes: format 2
*/
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
if (para & (1UL << i)) {
print_bam_pipe_selected_reg(vir_addr, i);
print_bam_pipe_desc_fifo(vir_addr, i, 0);
print_bam_pipe_desc_fifo(vir_addr, i, 100);
}
break;
case 99: /* output all registers, desc FIFOs and partial data blocks */
print_bam_test_bus_reg(vir_addr, tb_sel);
print_bam_reg(bam->base);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_reg(bam->base, i);
print_bam_selected_reg(vir_addr, bam->props.ee);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_selected_reg(vir_addr, i);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_desc_fifo(vir_addr, i, 0);
for (i = 0; i < num_pipes; i++)
print_bam_pipe_desc_fifo(vir_addr, i, 100);
break;
default:
pr_info("sps:no option is chosen yet.");
}
return res;
}
EXPORT_SYMBOL(sps_get_bam_debug_info);
/**
* Initialize SPS device
*
* This function initializes the SPS device.
*
* @return 0 on success, negative value on error
*
*/
static int sps_device_init(void)
{
int result;
int success;
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
struct sps_bam_props bamdma_props = {0};
#endif
SPS_DBG3(sps, "sps:%s.", __func__);
success = false;
result = sps_mem_init(sps->pipemem_phys_base, sps->pipemem_size);
if (result) {
SPS_ERR(sps, "sps:%s:SPS memory init failed", __func__);
goto exit_err;
}
INIT_LIST_HEAD(&sps->bams_q);
mutex_init(&sps->lock);
if (sps_rm_init(&sps->connection_ctrl, sps->options)) {
SPS_ERR(sps, "sps:%s:Fail to init SPS resource manager",
__func__);
goto exit_err;
}
result = sps_bam_driver_init(sps->options);
if (result) {
SPS_ERR(sps, "sps:%s:SPS BAM driver init failed", __func__);
goto exit_err;
}
/* Initialize the BAM DMA device */
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
bamdma_props.phys_addr = sps->bamdma_bam_phys_base;
bamdma_props.virt_addr = ioremap(sps->bamdma_bam_phys_base,
sps->bamdma_bam_size);
if (!bamdma_props.virt_addr) {
SPS_ERR(sps, "sps:%s:Fail to IO map BAM-DMA BAM registers.\n",
__func__);
goto exit_err;
}
SPS_DBG3(sps, "sps:bamdma_bam.phys=%pa.virt=0x%pK.",
&bamdma_props.phys_addr,
bamdma_props.virt_addr);
bamdma_props.periph_phys_addr = sps->bamdma_dma_phys_base;
bamdma_props.periph_virt_size = sps->bamdma_dma_size;
bamdma_props.periph_virt_addr = ioremap(sps->bamdma_dma_phys_base,
sps->bamdma_dma_size);
if (!bamdma_props.periph_virt_addr) {
SPS_ERR(sps, "sps:%s:Fail to IO map BAM-DMA peripheral reg.\n",
__func__);
goto exit_err;
}
SPS_DBG3(sps, "sps:bamdma_dma.phys=%pa.virt=0x%pK.",
&bamdma_props.periph_phys_addr,
bamdma_props.periph_virt_addr);
bamdma_props.irq = sps->bamdma_irq;
bamdma_props.event_threshold = 0x10; /* Pipe event threshold */
bamdma_props.summing_threshold = 0x10; /* BAM event threshold */
bamdma_props.options = SPS_BAM_OPT_BAMDMA;
bamdma_props.restricted_pipes = sps->bamdma_restricted_pipes;
result = sps_dma_init(&bamdma_props);
if (result) {
SPS_ERR(sps, "sps:%s:SPS BAM DMA driver init failed", __func__);
goto exit_err;
}
#endif /* CONFIG_SPS_SUPPORT_BAMDMA */
result = sps_map_init(NULL, sps->options);
if (result) {
SPS_ERR(sps,
"sps:%s:SPS connection mapping init failed", __func__);
goto exit_err;
}
success = true;
exit_err:
if (!success) {
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
sps_device_de_init();
#endif
return SPS_ERROR;
}
return 0;
}
/**
* De-initialize SPS device
*
* This function de-initializes the SPS device.
*
* @return 0 on success, negative value on error
*
*/
static void sps_device_de_init(void)
{
SPS_DBG3(sps, "sps:%s.", __func__);
if (sps != NULL) {
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
sps_dma_de_init();
#endif
/* Are there any remaining BAM registrations? */
if (!list_empty(&sps->bams_q))
SPS_ERR(sps,
"sps:%s:BAMs are still registered", __func__);
sps_map_de_init();
}
sps_mem_de_init();
}
/**
* Initialize client state context
*
* This function initializes a client state context struct.
*
* @client - Pointer to client state context
*
* @return 0 on success, negative value on error
*
*/
static int sps_client_init(struct sps_pipe *client)
{
SPS_DBG(sps, "sps:%s.", __func__);
if (client == NULL)
return -EINVAL;
/*
* NOTE: Cannot store any state within the SPS driver because
* the driver init function may not have been called yet.
*/
memset(client, 0, sizeof(*client));
sps_rm_config_init(&client->connect);
client->client_state = SPS_STATE_DISCONNECT;
client->bam = NULL;
return 0;
}
/**
* De-initialize client state context
*
* This function de-initializes a client state context struct.
*
* @client - Pointer to client state context
*
* @return 0 on success, negative value on error
*
*/
static int sps_client_de_init(struct sps_pipe *client)
{
SPS_DBG(sps, "sps:%s.", __func__);
if (client->client_state != SPS_STATE_DISCONNECT) {
SPS_ERR(sps, "sps:De-init client in connected state: 0x%x",
client->client_state);
return SPS_ERROR;
}
client->bam = NULL;
client->map = NULL;
memset(&client->connect, 0, sizeof(client->connect));
return 0;
}
/**
* Find the BAM device from the physical address
*
* This function finds a BAM device in the BAM registration list that
* matches the specified physical address.
*
* @phys_addr - physical address of the BAM
*
* @return - pointer to the BAM device struct, or NULL on error
*
*/
static struct sps_bam *phy2bam(phys_addr_t phys_addr)
{
struct sps_bam *bam;
SPS_DBG2(sps, "sps:%s.", __func__);
list_for_each_entry(bam, &sps->bams_q, list) {
if (bam->props.phys_addr == phys_addr)
return bam;
}
return NULL;
}
/**
* Find the handle of a BAM device based on the physical address
*
* This function finds a BAM device in the BAM registration list that
* matches the specified physical address, and returns its handle.
*
* @phys_addr - physical address of the BAM
*
* @h - device handle of the BAM
*
* @return 0 on success, negative value on error
*
*/
int sps_phy2h(phys_addr_t phys_addr, unsigned long *handle)
{
struct sps_bam *bam;
SPS_DBG2(sps, "sps:%s.", __func__);
if (sps == NULL || !sps->is_ready) {
SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
return -EPROBE_DEFER;
}
if (handle == NULL) {
SPS_ERR(sps, "sps:%s:handle is NULL.\n", __func__);
return SPS_ERROR;
}
list_for_each_entry(bam, &sps->bams_q, list) {
if (bam->props.phys_addr == phys_addr) {
*handle = (uintptr_t) bam;
return 0;
}
}
SPS_ERR(sps,
"sps: BAM device %pa is not registered yet.\n", &phys_addr);
return -ENODEV;
}
EXPORT_SYMBOL(sps_phy2h);
/**
* Setup desc/data FIFO for bam-to-bam connection
*
* @mem_buffer - Pointer to struct for allocated memory properties.
*
* @addr - address of FIFO
*
* @size - FIFO size
*
* @use_offset - use address offset instead of absolute address
*
* @return 0 on success, negative value on error
*
*/
int sps_setup_bam2bam_fifo(struct sps_mem_buffer *mem_buffer,
u32 addr, u32 size, int use_offset)
{
SPS_DBG1(sps, "sps:%s.", __func__);
if ((mem_buffer == NULL) || (size == 0)) {
SPS_ERR(sps, "sps:%s:invalid buffer address or size.",
__func__);
return SPS_ERROR;
}
if (sps == NULL || !sps->is_ready) {
SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
return -EPROBE_DEFER;
}
if (use_offset) {
if ((addr + size) <= sps->pipemem_size)
mem_buffer->phys_base = sps->pipemem_phys_base + addr;
else {
SPS_ERR(sps,
"sps:%s:requested mem is out of pipe mem range.\n",
__func__);
return SPS_ERROR;
}
} else {
if (addr >= sps->pipemem_phys_base &&
(addr + size) <= (sps->pipemem_phys_base
+ sps->pipemem_size))
mem_buffer->phys_base = addr;
else {
SPS_ERR(sps,
"sps:%s:requested mem is out of pipe mem range.\n",
__func__);
return SPS_ERROR;
}
}
mem_buffer->base = spsi_get_mem_ptr(mem_buffer->phys_base);
mem_buffer->size = size;
memset(mem_buffer->base, 0, mem_buffer->size);
return 0;
}
EXPORT_SYMBOL(sps_setup_bam2bam_fifo);
/**
* Find the BAM device from the handle
*
* This function finds a BAM device in the BAM registration list that
* matches the specified device handle.
*
* @h - device handle of the BAM
*
* @return - pointer to the BAM device struct, or NULL on error
*
*/
struct sps_bam *sps_h2bam(unsigned long h)
{
struct sps_bam *bam;
SPS_DBG1(sps, "sps:%s: BAM handle:0x%pK.", __func__, (void *)h);
if (h == SPS_DEV_HANDLE_MEM || h == SPS_DEV_HANDLE_INVALID)
return NULL;
list_for_each_entry(bam, &sps->bams_q, list) {
if ((uintptr_t) bam == h)
return bam;
}
SPS_ERR(sps, "sps:Can't find BAM device for handle 0x%pK.", (void *)h);
return NULL;
}
/**
* Lock BAM device
*
* This function obtains the BAM spinlock on the client's connection.
*
* @pipe - pointer to client pipe state
*
* @return pointer to BAM device struct, or NULL on error
*
*/
static struct sps_bam *sps_bam_lock(struct sps_pipe *pipe)
{
struct sps_bam *bam;
u32 pipe_index;
bam = pipe->bam;
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:Connection is not in connected state.",
__func__);
return NULL;
}
spin_lock_irqsave(&bam->connection_lock, bam->irqsave_flags);
/* Verify client owns this pipe */
pipe_index = pipe->pipe_index;
if (pipe_index >= bam->props.num_pipes ||
pipe != bam->pipes[pipe_index]) {
SPS_ERR(bam,
"sps:Client not owner of BAM %pa pipe: %d (max %d)",
&bam->props.phys_addr, pipe_index,
bam->props.num_pipes);
spin_unlock_irqrestore(&bam->connection_lock,
bam->irqsave_flags);
return NULL;
}
return bam;
}
/**
* Unlock BAM device
*
* This function releases the BAM spinlock on the client's connection.
*
* @bam - pointer to BAM device struct
*
*/
static inline void sps_bam_unlock(struct sps_bam *bam)
{
spin_unlock_irqrestore(&bam->connection_lock, bam->irqsave_flags);
}
/**
* Connect an SPS connection end point
*
*/
int sps_connect(struct sps_pipe *h, struct sps_connect *connect)
{
struct sps_pipe *pipe = h;
unsigned long dev;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (connect == NULL) {
SPS_ERR(sps, "sps:%s:connection is NULL.\n", __func__);
return SPS_ERROR;
}
if (sps == NULL)
return -ENODEV;
if (!sps->is_ready) {
SPS_ERR(sps, "sps:%s:sps driver is not ready.\n", __func__);
return -EAGAIN;
}
if ((connect->lock_group != SPSRM_CLEAR)
&& (connect->lock_group > BAM_MAX_P_LOCK_GROUP_NUM)) {
SPS_ERR(sps,
"sps:%s:The value of pipe lock group is invalid.\n",
__func__);
return SPS_ERROR;
}
mutex_lock(&sps->lock);
/*
* Must lock the BAM device at the top level function, so must
* determine which BAM is the target for the connection
*/
if (connect->mode == SPS_MODE_SRC)
dev = connect->source;
else
dev = connect->destination;
bam = sps_h2bam(dev);
if (bam == NULL) {
SPS_ERR(sps, "sps:Invalid BAM device handle: 0x%pK",
(void *)dev);
result = SPS_ERROR;
goto exit_err;
}
mutex_lock(&bam->lock);
SPS_DBG2(bam, "sps:%s: bam %pa src 0x%pK dest 0x%pK mode %s",
__func__,
BAM_ID(bam),
(void *)connect->source,
(void *)connect->destination,
connect->mode == SPS_MODE_SRC ? "SRC" : "DEST");
/* Allocate resources for the specified connection */
pipe->connect = *connect;
result = sps_rm_state_change(pipe, SPS_STATE_ALLOCATE);
if (result) {
mutex_unlock(&bam->lock);
goto exit_err;
}
/* Configure the connection */
result = sps_rm_state_change(pipe, SPS_STATE_CONNECT);
mutex_unlock(&bam->lock);
if (result) {
sps_disconnect(h);
goto exit_err;
}
exit_err:
mutex_unlock(&sps->lock);
return result;
}
EXPORT_SYMBOL(sps_connect);
/**
* Disconnect an SPS connection end point
*
* This function disconnects an SPS connection end point.
* The SPS hardware associated with that end point will be disabled.
* For a connection involving system memory (SPS_DEV_HANDLE_MEM), all
* connection resources are deallocated. For a peripheral-to-peripheral
* connection, the resources associated with the connection will not be
* deallocated until both end points are closed.
*
* The client must call sps_connect() for the handle before calling
* this function.
*
* @h - client context for SPS connection end point
*
* @return 0 on success, negative value on error
*
*/
int sps_disconnect(struct sps_pipe *h)
{
struct sps_pipe *pipe = h;
struct sps_pipe *check;
struct sps_bam *bam;
int result;
if (pipe == NULL) {
SPS_ERR(sps, "sps:%s:Invalid pipe.", __func__);
return SPS_ERROR;
}
bam = pipe->bam;
if (bam == NULL) {
SPS_ERR(sps,
"sps:%s:BAM device of this pipe is NULL.", __func__);
return SPS_ERROR;
}
SPS_DBG2(bam,
"sps:%s: bam %pa src 0x%pK dest 0x%pK mode %s",
__func__,
BAM_ID(bam),
(void *)pipe->connect.source,
(void *)pipe->connect.destination,
pipe->connect.mode == SPS_MODE_SRC ? "SRC" : "DEST");
result = SPS_ERROR;
/* Cross-check client with map table */
if (pipe->connect.mode == SPS_MODE_SRC)
check = pipe->map->client_src;
else
check = pipe->map->client_dest;
if (check != pipe) {
SPS_ERR(sps, "sps:%s:Client context is corrupt", __func__);
goto exit_err;
}
/* Disconnect the BAM pipe */
mutex_lock(&bam->lock);
result = sps_rm_state_change(pipe, SPS_STATE_DISCONNECT);
mutex_unlock(&bam->lock);
if (result)
goto exit_err;
sps_rm_config_init(&pipe->connect);
result = 0;
exit_err:
return result;
}
EXPORT_SYMBOL(sps_disconnect);
/**
* Register an event object for an SPS connection end point
*
*/
int sps_register_event(struct sps_pipe *h, struct sps_register_event *reg)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (reg == NULL) {
SPS_ERR(sps, "sps:%s:registered event is NULL.\n", __func__);
return SPS_ERROR;
}
if (sps == NULL)
return -ENODEV;
if (!sps->is_ready) {
SPS_ERR(sps, "sps:%s:sps driver not ready.\n", __func__);
return -EAGAIN;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG2(bam, "sps:%s; events:%d.\n", __func__, reg->options);
result = sps_bam_pipe_reg_event(bam, pipe->pipe_index, reg);
sps_bam_unlock(bam);
if (result)
SPS_ERR(bam,
"sps:Fail to register event for BAM %pa pipe %d",
&pipe->bam->props.phys_addr, pipe->pipe_index);
return result;
}
EXPORT_SYMBOL(sps_register_event);
/**
* Enable an SPS connection end point
*
*/
int sps_flow_on(struct sps_pipe *h)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result = 0;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG2(bam, "sps:%s.\n", __func__);
bam_pipe_halt(&bam->base, pipe->pipe_index, false);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_flow_on);
/**
* Disable an SPS connection end point
*
*/
int sps_flow_off(struct sps_pipe *h, enum sps_flow_off mode)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result = 0;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG2(bam, "sps:%s.\n", __func__);
bam_pipe_halt(&bam->base, pipe->pipe_index, true);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_flow_off);
/**
* Check if the flags on a descriptor/iovec are valid
*
* @flags - flags on a descriptor/iovec
*
* @return 0 on success, negative value on error
*
*/
static int sps_check_iovec_flags(u32 flags)
{
if ((flags & SPS_IOVEC_FLAG_NWD) &&
!(flags & (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_CMD))) {
SPS_ERR(sps,
"sps:%s:NWD is only valid with EOT or CMD.\n",
__func__);
return SPS_ERROR;
} else if ((flags & SPS_IOVEC_FLAG_EOT) &&
(flags & SPS_IOVEC_FLAG_CMD)) {
SPS_ERR(sps,
"sps:%s:EOT and CMD are not allowed to coexist.\n",
__func__);
return SPS_ERROR;
} else if (!(flags & SPS_IOVEC_FLAG_CMD) &&
(flags & (SPS_IOVEC_FLAG_LOCK | SPS_IOVEC_FLAG_UNLOCK))) {
static char err_msg[] =
"pipe lock/unlock flags are only valid with Command Descriptor";
SPS_ERR(sps, "sps:%s.\n", err_msg);
return SPS_ERROR;
} else if ((flags & SPS_IOVEC_FLAG_LOCK) &&
(flags & SPS_IOVEC_FLAG_UNLOCK)) {
static char err_msg[] =
"Can't lock and unlock a pipe by the same Command Descriptor";
SPS_ERR(sps, "sps:%s.\n", err_msg);
return SPS_ERROR;
} else if ((flags & SPS_IOVEC_FLAG_IMME) &&
(flags & SPS_IOVEC_FLAG_CMD)) {
SPS_ERR(sps,
"sps:%s:Immediate and CMD are not allowed to coexist.\n",
__func__);
return SPS_ERROR;
} else if ((flags & SPS_IOVEC_FLAG_IMME) &&
(flags & SPS_IOVEC_FLAG_NWD)) {
SPS_ERR(sps,
"sps:%s:Immediate and NWD are not allowed to coexist.\n",
__func__);
return SPS_ERROR;
}
return 0;
}
/**
* Perform a DMA transfer on an SPS connection end point
*
*/
int sps_transfer(struct sps_pipe *h, struct sps_transfer *transfer)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
struct sps_iovec *iovec;
int i;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (transfer == NULL) {
SPS_ERR(sps, "sps:%s:transfer is NULL.\n", __func__);
return SPS_ERROR;
} else if (transfer->iovec == NULL) {
SPS_ERR(sps, "sps:%s:iovec list is NULL.\n", __func__);
return SPS_ERROR;
} else if (transfer->iovec_count == 0) {
SPS_ERR(sps, "sps:%s:iovec list is empty.\n", __func__);
return SPS_ERROR;
} else if (transfer->iovec_phys == 0) {
SPS_ERR(sps,
"sps:%s:iovec list address is invalid.\n", __func__);
return SPS_ERROR;
}
/* Verify content of IOVECs */
iovec = transfer->iovec;
for (i = 0; i < transfer->iovec_count; i++) {
u32 flags = iovec->flags;
if (iovec->size > SPS_IOVEC_MAX_SIZE) {
SPS_ERR(sps,
"sps:%s:iovec size is invalid.\n", __func__);
return SPS_ERROR;
}
if (sps_check_iovec_flags(flags))
return SPS_ERROR;
iovec++;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG(bam, "sps:%s.\n", __func__);
result = sps_bam_pipe_transfer(bam, pipe->pipe_index, transfer);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_transfer);
/**
* Perform a single DMA transfer on an SPS connection end point
*
*/
int sps_transfer_one(struct sps_pipe *h, phys_addr_t addr, u32 size,
void *user, u32 flags)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
}
if (sps_check_iovec_flags(flags))
return SPS_ERROR;
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG(bam, "sps:%s.\n", __func__);
result = sps_bam_pipe_transfer_one(bam, pipe->pipe_index,
SPS_GET_LOWER_ADDR(addr), size, user,
DESC_FLAG_WORD(flags, addr));
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_transfer_one);
/**
* Read event queue for an SPS connection end point
*
*/
int sps_get_event(struct sps_pipe *h, struct sps_event_notify *notify)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (notify == NULL) {
SPS_ERR(sps, "sps:%s:event_notify is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG1(bam, "sps:%s.\n", __func__);
result = sps_bam_pipe_get_event(bam, pipe->pipe_index, notify);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_get_event);
/**
* Determine whether an SPS connection end point FIFO is empty
*
*/
int sps_is_pipe_empty(struct sps_pipe *h, u32 *empty)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (empty == NULL) {
SPS_ERR(sps, "sps:%s:result pointer is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG1(bam, "sps:%s.\n", __func__);
result = sps_bam_pipe_is_empty(bam, pipe->pipe_index, empty);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_is_pipe_empty);
/**
* Get number of free transfer entries for an SPS connection end point
*
*/
int sps_get_free_count(struct sps_pipe *h, u32 *count)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (count == NULL) {
SPS_ERR(sps, "sps:%s:result pointer is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG(bam, "sps:%s.\n", __func__);
result = sps_bam_get_free_count(bam, pipe->pipe_index, count);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_get_free_count);
/**
* Reset an SPS BAM device
*
*/
int sps_device_reset(unsigned long dev)
{
struct sps_bam *bam;
int result;
if (dev == 0) {
SPS_ERR(sps,
"sps:%s:device handle should not be 0.\n", __func__);
return SPS_ERROR;
}
if (sps == NULL || !sps->is_ready) {
SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
return -EPROBE_DEFER;
}
mutex_lock(&sps->lock);
/* Search for the target BAM device */
bam = sps_h2bam(dev);
if (bam == NULL) {
SPS_ERR(sps, "sps:Invalid BAM device handle: 0x%pK",
(void *)dev);
result = SPS_ERROR;
goto exit_err;
}
SPS_DBG3(bam, "sps:%s.\n", __func__);
mutex_lock(&bam->lock);
result = sps_bam_reset(bam);
mutex_unlock(&bam->lock);
if (result) {
SPS_ERR(sps, "sps:Fail to reset BAM device: 0x%pK",
(void *)dev);
goto exit_err;
}
exit_err:
mutex_unlock(&sps->lock);
return result;
}
EXPORT_SYMBOL(sps_device_reset);
/**
* Get the configuration parameters for an SPS connection end point
*
*/
int sps_get_config(struct sps_pipe *h, struct sps_connect *config)
{
struct sps_pipe *pipe = h;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (config == NULL) {
SPS_ERR(sps, "sps:%s:config pointer is NULL.\n", __func__);
return SPS_ERROR;
}
if (pipe->bam == NULL)
SPS_DBG(sps, "sps:%s.\n", __func__);
else
SPS_DBG(pipe->bam,
"sps:%s; BAM: %pa; pipe index:%d; options:0x%x.\n",
__func__, BAM_ID(pipe->bam), pipe->pipe_index,
pipe->connect.options);
/* Copy current client connection state */
*config = pipe->connect;
return 0;
}
EXPORT_SYMBOL(sps_get_config);
/**
* Set the configuration parameters for an SPS connection end point
*
*/
int sps_set_config(struct sps_pipe *h, struct sps_connect *config)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (config == NULL) {
SPS_ERR(sps, "sps:%s:config pointer is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is NULL.\n", __func__);
return SPS_ERROR;
}
SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d, config-options:0x%x.\n",
__func__, BAM_ID(bam), pipe->pipe_index, config->options);
result = sps_bam_pipe_set_params(bam, pipe->pipe_index,
config->options);
if (result == 0)
pipe->connect.options = config->options;
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_set_config);
/**
* Set ownership of an SPS connection end point
*
*/
int sps_set_owner(struct sps_pipe *h, enum sps_owner owner,
struct sps_satellite *connect)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (connect == NULL) {
SPS_ERR(sps, "sps:%s:connection is NULL.\n", __func__);
return SPS_ERROR;
}
if (owner != SPS_OWNER_REMOTE) {
SPS_ERR(sps, "sps:Unsupported ownership state: %d", owner);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
__func__, BAM_ID(bam), pipe->pipe_index);
result = sps_bam_set_satellite(bam, pipe->pipe_index);
if (result)
goto exit_err;
/* Return satellite connect info */
if (connect == NULL)
goto exit_err;
if (pipe->connect.mode == SPS_MODE_SRC) {
connect->dev = pipe->map->src.bam_phys;
connect->pipe_index = pipe->map->src.pipe_index;
} else {
connect->dev = pipe->map->dest.bam_phys;
connect->pipe_index = pipe->map->dest.pipe_index;
}
connect->config = SPS_CONFIG_SATELLITE;
connect->options = (enum sps_option) 0;
exit_err:
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_set_owner);
/**
* Allocate memory from the SPS Pipe-Memory.
*
*/
int sps_alloc_mem(struct sps_pipe *h, enum sps_mem mem,
struct sps_mem_buffer *mem_buffer)
{
if (sps == NULL)
return -ENODEV;
if (!sps->is_ready) {
SPS_ERR(sps, "sps:%s:sps driver is not ready.", __func__);
return -EAGAIN;
}
if (mem_buffer == NULL || mem_buffer->size == 0) {
SPS_ERR(sps, "sps:%s:invalid memory buffer address or size",
__func__);
return SPS_ERROR;
}
if (h == NULL)
SPS_DBG2(sps,
"sps:%s:allocate pipe memory before setup pipe",
__func__);
else
SPS_DBG2(sps,
"sps:allocate pipe memory for pipe %d", h->pipe_index);
mem_buffer->phys_base = sps_mem_alloc_io(mem_buffer->size);
if (mem_buffer->phys_base == SPS_ADDR_INVALID) {
SPS_ERR(sps, "sps:%s:invalid address of allocated memory",
__func__);
return SPS_ERROR;
}
mem_buffer->base = spsi_get_mem_ptr(mem_buffer->phys_base);
return 0;
}
EXPORT_SYMBOL(sps_alloc_mem);
/**
* Free memory from the SPS Pipe-Memory.
*
*/
int sps_free_mem(struct sps_pipe *h, struct sps_mem_buffer *mem_buffer)
{
SPS_DBG(sps, "sps:%s.", __func__);
if (mem_buffer == NULL || mem_buffer->phys_base == SPS_ADDR_INVALID) {
SPS_ERR(sps, "sps:%s:invalid memory to free", __func__);
return SPS_ERROR;
}
if (h == NULL)
SPS_DBG2(sps, "sps:%s:free pipe memory.", __func__);
else
SPS_DBG2(sps,
"sps:free pipe memory for pipe %d.", h->pipe_index);
sps_mem_free_io(mem_buffer->phys_base, mem_buffer->size);
return 0;
}
EXPORT_SYMBOL(sps_free_mem);
/**
* Get the number of unused descriptors in the descriptor FIFO
* of a pipe
*
*/
int sps_get_unused_desc_num(struct sps_pipe *h, u32 *desc_num)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (desc_num == NULL) {
SPS_ERR(sps, "sps:%s:result pointer is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
__func__, BAM_ID(bam), pipe->pipe_index);
result = sps_bam_pipe_get_unused_desc_num(bam, pipe->pipe_index,
desc_num);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_get_unused_desc_num);
/**
* Vote for or relinquish BAM DMA clock
*
*/
int sps_ctrl_bam_dma_clk(bool clk_on)
{
int ret;
if (sps == NULL || !sps->is_ready) {
SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
return -EPROBE_DEFER;
}
if (clk_on == true) {
SPS_DBG1(sps, "%s", "sps:vote for bam dma clk.\n");
ret = clk_prepare_enable(sps->bamdma_clk);
if (ret) {
SPS_ERR(sps,
"sps:fail to enable bamdma_clk:ret=%d\n", ret);
return ret;
}
} else {
SPS_DBG1(sps, "%s", "sps:relinquish bam dma clk.\n");
clk_disable_unprepare(sps->bamdma_clk);
}
return 0;
}
EXPORT_SYMBOL(sps_ctrl_bam_dma_clk);
/**
* Register a BAM device
*
*/
int sps_register_bam_device(const struct sps_bam_props *bam_props,
unsigned long *dev_handle)
{
struct sps_bam *bam = NULL;
void __iomem *virt_addr = NULL;
char bam_name[MAX_MSG_LEN];
u32 manage;
int ok;
int result;
if (bam_props == NULL) {
SPS_ERR(sps, "sps:%s:bam_props is NULL.\n", __func__);
return SPS_ERROR;
} else if (dev_handle == NULL) {
SPS_ERR(sps, "sps:%s:device handle is NULL.\n", __func__);
return SPS_ERROR;
}
if (sps == NULL) {
pr_err("sps:%s:sps driver is not ready.\n", __func__);
return -EPROBE_DEFER;
}
SPS_DBG3(sps, "sps:%s: Client requests to register BAM %pa.\n",
__func__, &bam_props->phys_addr);
/* BAM-DMA is registered internally during power-up */
if ((!sps->is_ready) && !(bam_props->options & SPS_BAM_OPT_BAMDMA)) {
SPS_ERR(sps, "sps:%s:sps driver not ready.\n", __func__);
return -EAGAIN;
}
/* Check BAM parameters */
manage = bam_props->manage & SPS_BAM_MGR_ACCESS_MASK;
if (manage != SPS_BAM_MGR_NONE) {
if (bam_props->virt_addr == NULL && bam_props->virt_size == 0) {
SPS_ERR(sps, "sps:Invalid properties for BAM: %pa",
&bam_props->phys_addr);
return SPS_ERROR;
}
}
if ((bam_props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) {
/* BAM global is configured by local processor */
if (bam_props->summing_threshold == 0) {
SPS_ERR(sps,
"sps:Invalid device ctrl properties for BAM: %pa",
&bam_props->phys_addr);
return SPS_ERROR;
}
}
manage = bam_props->manage &
(SPS_BAM_MGR_PIPE_NO_CONFIG | SPS_BAM_MGR_PIPE_NO_CTRL);
/* In case of error */
*dev_handle = SPS_DEV_HANDLE_INVALID;
result = SPS_ERROR;
mutex_lock(&sps->lock);
/* Is this BAM already registered? */
bam = phy2bam(bam_props->phys_addr);
if (bam != NULL) {
mutex_unlock(&sps->lock);
SPS_ERR(sps, "sps:BAM is already registered: %pa",
&bam->props.phys_addr);
result = -EEXIST;
bam = NULL; /* Avoid error clean-up kfree(bam) */
goto exit_err;
}
/* Perform virtual mapping if required */
if ((bam_props->manage & SPS_BAM_MGR_ACCESS_MASK) !=
SPS_BAM_MGR_NONE && bam_props->virt_addr == NULL) {
/* Map the memory region */
virt_addr = ioremap(bam_props->phys_addr, bam_props->virt_size);
if (virt_addr == NULL) {
SPS_ERR(sps,
"sps:Unable to map BAM IO mem:%pa size:0x%x",
&bam_props->phys_addr, bam_props->virt_size);
goto exit_err;
}
}
bam = kzalloc(sizeof(*bam), GFP_KERNEL);
if (bam == NULL) {
SPS_ERR(sps,
"sps:Unable to allocate BAM device state: size is %zu",
sizeof(*bam));
goto exit_err;
}
memset(bam, 0, sizeof(*bam));
mutex_init(&bam->lock);
mutex_lock(&bam->lock);
/* Copy configuration to BAM device descriptor */
bam->props = *bam_props;
if (virt_addr != NULL)
bam->props.virt_addr = virt_addr;
snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_0",
&bam->props.phys_addr);
bam->ipc_log0 = ipc_log_context_create(SPS_IPC_LOGPAGES,
bam_name, 0);
if (!bam->ipc_log0)
SPS_ERR(sps, "%s : unable to create IPC Logging 0 for bam %pa",
__func__, &bam->props.phys_addr);
snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_1",
&bam->props.phys_addr);
bam->ipc_log1 = ipc_log_context_create(SPS_IPC_LOGPAGES,
bam_name, 0);
if (!bam->ipc_log1)
SPS_ERR(sps, "%s : unable to create IPC Logging 1 for bam %pa",
__func__, &bam->props.phys_addr);
snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_2",
&bam->props.phys_addr);
bam->ipc_log2 = ipc_log_context_create(SPS_IPC_LOGPAGES,
bam_name, 0);
if (!bam->ipc_log2)
SPS_ERR(sps, "%s : unable to create IPC Logging 2 for bam %pa",
__func__, &bam->props.phys_addr);
snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_3",
&bam->props.phys_addr);
bam->ipc_log3 = ipc_log_context_create(SPS_IPC_LOGPAGES,
bam_name, 0);
if (!bam->ipc_log3)
SPS_ERR(sps, "%s : unable to create IPC Logging 3 for bam %pa",
__func__, &bam->props.phys_addr);
snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_4",
&bam->props.phys_addr);
bam->ipc_log4 = ipc_log_context_create(SPS_IPC_LOGPAGES,
bam_name, 0);
if (!bam->ipc_log4)
SPS_ERR(sps, "%s : unable to create IPC Logging 4 for bam %pa",
__func__, &bam->props.phys_addr);
if (bam_props->ipc_loglevel)
bam->ipc_loglevel = bam_props->ipc_loglevel;
else
bam->ipc_loglevel = SPS_IPC_DEFAULT_LOGLEVEL;
ok = sps_bam_device_init(bam);
mutex_unlock(&bam->lock);
if (ok) {
ipc_log_context_destroy(bam->ipc_log0);
ipc_log_context_destroy(bam->ipc_log1);
ipc_log_context_destroy(bam->ipc_log2);
ipc_log_context_destroy(bam->ipc_log3);
ipc_log_context_destroy(bam->ipc_log4);
SPS_ERR(sps, "sps:Fail to init BAM device: phys %pa",
&bam->props.phys_addr);
goto exit_err;
}
/* Add BAM to the list */
list_add_tail(&bam->list, &sps->bams_q);
*dev_handle = (uintptr_t) bam;
result = 0;
exit_err:
mutex_unlock(&sps->lock);
if (result) {
if (bam != NULL) {
if (virt_addr != NULL)
iounmap(bam->props.virt_addr);
kfree(bam);
}
return result;
}
/* If this BAM is attached to a BAM-DMA, init the BAM-DMA device */
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
if (sps_dma_device_init((uintptr_t) bam)) {
bam->props.options &= ~SPS_BAM_OPT_BAMDMA;
sps_deregister_bam_device((uintptr_t) bam);
SPS_ERR(bam, "sps:Fail to init BAM-DMA BAM: phys %pa",
&bam->props.phys_addr);
return SPS_ERROR;
}
}
#endif /* CONFIG_SPS_SUPPORT_BAMDMA */
SPS_INFO(bam, "sps:BAM %pa is registered.", &bam->props.phys_addr);
return 0;
}
EXPORT_SYMBOL(sps_register_bam_device);
/**
* Deregister a BAM device
*
*/
int sps_deregister_bam_device(unsigned long dev_handle)
{
struct sps_bam *bam;
int n;
if (dev_handle == 0) {
SPS_ERR(sps, "sps:%s:device handle should not be 0.\n",
__func__);
return SPS_ERROR;
}
bam = sps_h2bam(dev_handle);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:did not find a BAM for this handle",
__func__);
return SPS_ERROR;
}
SPS_DBG3(sps, "sps:%s: SPS deregister BAM: phys %pa.",
__func__, &bam->props.phys_addr);
if (bam->props.options & SPS_BAM_HOLD_MEM) {
for (n = 0; n < BAM_MAX_PIPES; n++)
if (bam->desc_cache_pointers[n] != NULL)
kfree(bam->desc_cache_pointers[n]);
}
/* If this BAM is attached to a BAM-DMA, init the BAM-DMA device */
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
mutex_lock(&bam->lock);
(void)sps_dma_device_de_init((uintptr_t) bam);
bam->props.options &= ~SPS_BAM_OPT_BAMDMA;
mutex_unlock(&bam->lock);
}
#endif
/* Remove the BAM from the registration list */
mutex_lock(&sps->lock);
list_del(&bam->list);
mutex_unlock(&sps->lock);
/* De-init the BAM and free resources */
mutex_lock(&bam->lock);
sps_bam_device_de_init(bam);
mutex_unlock(&bam->lock);
ipc_log_context_destroy(bam->ipc_log0);
ipc_log_context_destroy(bam->ipc_log1);
ipc_log_context_destroy(bam->ipc_log2);
ipc_log_context_destroy(bam->ipc_log3);
ipc_log_context_destroy(bam->ipc_log4);
if (bam->props.virt_size)
(void)iounmap(bam->props.virt_addr);
kfree(bam);
return 0;
}
EXPORT_SYMBOL(sps_deregister_bam_device);
/**
* Get processed I/O vector (completed transfers)
*
*/
int sps_get_iovec(struct sps_pipe *h, struct sps_iovec *iovec)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (iovec == NULL) {
SPS_ERR(sps, "sps:%s:iovec pointer is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_bam_lock(pipe);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
return SPS_ERROR;
}
SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
__func__, BAM_ID(bam), pipe->pipe_index);
/* Get an iovec from the BAM pipe descriptor FIFO */
result = sps_bam_pipe_get_iovec(bam, pipe->pipe_index, iovec);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_get_iovec);
/**
* Perform timer control
*
*/
int sps_timer_ctrl(struct sps_pipe *h,
struct sps_timer_ctrl *timer_ctrl,
struct sps_timer_result *timer_result)
{
struct sps_pipe *pipe = h;
struct sps_bam *bam;
int result;
if (h == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
} else if (timer_ctrl == NULL) {
SPS_ERR(sps, "sps:%s:timer_ctrl pointer is NULL.\n", __func__);
return SPS_ERROR;
} else if (timer_result == NULL) {
SPS_DBG(sps, "sps:%s:no result to return.\n", __func__);
}
bam = sps_bam_lock(pipe);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
return SPS_ERROR;
}
SPS_DBG2(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
__func__, BAM_ID(bam), pipe->pipe_index);
/* Perform the BAM pipe timer control operation */
result = sps_bam_pipe_timer_ctrl(bam, pipe->pipe_index, timer_ctrl,
timer_result);
sps_bam_unlock(bam);
return result;
}
EXPORT_SYMBOL(sps_timer_ctrl);
/*
* Reset a BAM pipe
*/
int sps_pipe_reset(unsigned long dev, u32 pipe)
{
struct sps_bam *bam;
if (!dev) {
SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
return SPS_ERROR;
}
if (pipe >= BAM_MAX_PIPES) {
SPS_ERR(sps, "sps:%s:pipe index is invalid.\n", __func__);
return SPS_ERROR;
}
bam = sps_h2bam(dev);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
return SPS_ERROR;
}
SPS_DBG2(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
__func__, BAM_ID(bam), pipe);
bam_pipe_reset(&bam->base, pipe);
return 0;
}
EXPORT_SYMBOL(sps_pipe_reset);
/*
* Disable a BAM pipe
*/
int sps_pipe_disable(unsigned long dev, u32 pipe)
{
struct sps_bam *bam;
if (!dev) {
SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
return SPS_ERROR;
}
if (pipe >= BAM_MAX_PIPES) {
SPS_ERR(sps, "sps:%s:pipe index is invalid.\n", __func__);
return SPS_ERROR;
}
bam = sps_h2bam(dev);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
return SPS_ERROR;
}
SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
__func__, BAM_ID(bam), pipe);
bam_disable_pipe(&bam->base, pipe);
return 0;
}
EXPORT_SYMBOL(sps_pipe_disable);
/*
* Check pending descriptors in the descriptor FIFO
* of a pipe
*/
int sps_pipe_pending_desc(unsigned long dev, u32 pipe, bool *pending)
{
struct sps_bam *bam;
if (!dev) {
SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
return SPS_ERROR;
}
if (pipe >= BAM_MAX_PIPES) {
SPS_ERR(sps, "sps:%s:pipe index is invalid.\n", __func__);
return SPS_ERROR;
}
if (!pending) {
SPS_ERR(sps, "sps:%s:input flag is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_h2bam(dev);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
return SPS_ERROR;
}
SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
__func__, BAM_ID(bam), pipe);
*pending = sps_bam_pipe_pending_desc(bam, pipe);
return 0;
}
EXPORT_SYMBOL(sps_pipe_pending_desc);
/*
* Process any pending IRQ of a BAM
*/
int sps_bam_process_irq(unsigned long dev)
{
struct sps_bam *bam;
int ret = 0;
if (!dev) {
SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_h2bam(dev);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
return SPS_ERROR;
}
SPS_DBG1(bam, "sps:%s; BAM: %pa.\n", __func__, BAM_ID(bam));
ret = sps_bam_check_irq(bam);
return ret;
}
EXPORT_SYMBOL(sps_bam_process_irq);
/*
* Get address info of a BAM
*/
int sps_get_bam_addr(unsigned long dev, phys_addr_t *base,
u32 *size)
{
struct sps_bam *bam;
if (!dev) {
SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
return SPS_ERROR;
}
bam = sps_h2bam(dev);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
return SPS_ERROR;
}
*base = bam->props.phys_addr;
*size = bam->props.virt_size;
SPS_DBG2(bam, "sps:%s; BAM: %pa; base:%pa; size:%d.\n",
__func__, BAM_ID(bam), base, *size);
return 0;
}
EXPORT_SYMBOL(sps_get_bam_addr);
/*
* Inject a ZLT with EOT for a BAM pipe
*/
int sps_pipe_inject_zlt(unsigned long dev, u32 pipe_index)
{
struct sps_bam *bam;
int rc;
if (!dev) {
SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
return SPS_ERROR;
}
if (pipe_index >= BAM_MAX_PIPES) {
SPS_ERR(sps, "sps:%s:pipe index is invalid.\n", __func__);
return SPS_ERROR;
}
bam = sps_h2bam(dev);
if (bam == NULL) {
SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
return SPS_ERROR;
}
SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
__func__, BAM_ID(bam), pipe_index);
rc = sps_bam_pipe_inject_zlt(bam, pipe_index);
if (rc)
SPS_ERR(bam, "sps:%s:failed to inject a ZLT.\n", __func__);
return rc;
}
EXPORT_SYMBOL(sps_pipe_inject_zlt);
/**
* Allocate client state context
*
*/
struct sps_pipe *sps_alloc_endpoint(void)
{
struct sps_pipe *ctx = NULL;
SPS_DBG(sps, "sps:%s.", __func__);
ctx = kzalloc(sizeof(struct sps_pipe), GFP_KERNEL);
if (ctx == NULL) {
SPS_ERR(sps, "sps:%s:Fail to allocate pipe context.",
__func__);
return NULL;
}
sps_client_init(ctx);
return ctx;
}
EXPORT_SYMBOL(sps_alloc_endpoint);
/**
* Free client state context
*
*/
int sps_free_endpoint(struct sps_pipe *ctx)
{
int res;
SPS_DBG(sps, "sps:%s.", __func__);
if (ctx == NULL) {
SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
return SPS_ERROR;
}
res = sps_client_de_init(ctx);
if (res == 0)
kfree(ctx);
return res;
}
EXPORT_SYMBOL(sps_free_endpoint);
/**
* Platform Driver.
*/
static int get_platform_data(struct platform_device *pdev)
{
struct resource *resource;
struct msm_sps_platform_data *pdata;
SPS_DBG3(sps, "sps:%s.", __func__);
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
SPS_ERR(sps, "sps:%s:inavlid platform data.\n", __func__);
sps->bamdma_restricted_pipes = 0;
return -EINVAL;
}
sps->bamdma_restricted_pipes = pdata->bamdma_restricted_pipes;
SPS_DBG3(sps, "sps:bamdma_restricted_pipes=0x%x.\n",
sps->bamdma_restricted_pipes);
resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"pipe_mem");
if (resource) {
sps->pipemem_phys_base = resource->start;
sps->pipemem_size = resource_size(resource);
SPS_DBG3(sps, "sps:pipemem.base=%pa,size=0x%x.\n",
&sps->pipemem_phys_base,
sps->pipemem_size);
}
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"bamdma_bam");
if (resource) {
sps->bamdma_bam_phys_base = resource->start;
sps->bamdma_bam_size = resource_size(resource);
SPS_DBG(sps, "sps:bamdma_bam.base=%pa,size=0x%x.",
&sps->bamdma_bam_phys_base,
sps->bamdma_bam_size);
}
resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"bamdma_dma");
if (resource) {
sps->bamdma_dma_phys_base = resource->start;
sps->bamdma_dma_size = resource_size(resource);
SPS_DBG(sps, "sps:bamdma_dma.base=%pa,size=0x%x.",
&sps->bamdma_dma_phys_base,
sps->bamdma_dma_size);
}
resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"bamdma_irq");
if (resource) {
sps->bamdma_irq = resource->start;
SPS_DBG(sps, "sps:bamdma_irq=%d.", sps->bamdma_irq);
}
#endif
return 0;
}
/**
* Read data from device tree
*/
static int get_device_tree_data(struct platform_device *pdev)
{
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
struct resource *resource;
SPS_DBG(sps, "sps:%s.", __func__);
if (of_property_read_u32((&pdev->dev)->of_node,
"qcom,bam-dma-res-pipes",
&sps->bamdma_restricted_pipes))
SPS_DBG(sps,
"sps:%s:No restricted bamdma pipes on this target.\n",
__func__);
else
SPS_DBG(sps, "sps:bamdma_restricted_pipes=0x%x.",
sps->bamdma_restricted_pipes);
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (resource) {
sps->bamdma_bam_phys_base = resource->start;
sps->bamdma_bam_size = resource_size(resource);
SPS_DBG(sps, "sps:bamdma_bam.base=%pa,size=0x%x.",
&sps->bamdma_bam_phys_base,
sps->bamdma_bam_size);
} else {
SPS_ERR(sps, "sps:%s:BAM DMA BAM mem unavailable.", __func__);
return -ENODEV;
}
resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (resource) {
sps->bamdma_dma_phys_base = resource->start;
sps->bamdma_dma_size = resource_size(resource);
SPS_DBG(sps, "sps:bamdma_dma.base=%pa,size=0x%x.",
&sps->bamdma_dma_phys_base,
sps->bamdma_dma_size);
} else {
SPS_ERR(sps, "sps:%s:BAM DMA mem unavailable.", __func__);
return -ENODEV;
}
resource = platform_get_resource(pdev, IORESOURCE_MEM, 2);
if (resource) {
imem = true;
sps->pipemem_phys_base = resource->start;
sps->pipemem_size = resource_size(resource);
SPS_DBG(sps, "sps:pipemem.base=%pa,size=0x%x.",
&sps->pipemem_phys_base,
sps->pipemem_size);
} else {
imem = false;
SPS_DBG(sps, "sps:%s:No pipe memory on this target.\n",
__func__);
}
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (resource) {
sps->bamdma_irq = resource->start;
SPS_DBG(sps, "sps:bamdma_irq=%d.", sps->bamdma_irq);
} else {
SPS_ERR(sps, "sps:%s:BAM DMA IRQ unavailable.", __func__);
return -ENODEV;
}
#endif
if (of_property_read_u32((&pdev->dev)->of_node,
"qcom,device-type",
&d_type)) {
d_type = 3;
SPS_DBG3(sps, "sps:default device type %d.\n", d_type);
} else
SPS_DBG3(sps, "sps:device type is %d.", d_type);
enhd_pipe = of_property_read_bool((&pdev->dev)->of_node,
"qcom,pipe-attr-ee");
SPS_DBG3(sps, "sps:PIPE_ATTR_EE is %s supported.\n",
(enhd_pipe ? "" : "not"));
return 0;
}
static const struct of_device_id msm_sps_match[] = {
{ .compatible = "qcom,msm-sps",
.data = &bam_types[SPS_BAM_NDP]
},
{ .compatible = "qcom,msm-sps-4k",
.data = &bam_types[SPS_BAM_NDP_4K]
},
{}
};
static int msm_sps_probe(struct platform_device *pdev)
{
int ret = -ENODEV;
SPS_DBG3(sps, "sps:%s.", __func__);
if (pdev->dev.of_node) {
const struct of_device_id *match;
if (get_device_tree_data(pdev)) {
SPS_ERR(sps,
"sps:%s:Fail to get data from device tree.",
__func__);
return -ENODEV;
}
SPS_DBG(sps, "%s", "sps:get data from device tree.");
match = of_match_device(msm_sps_match, &pdev->dev);
if (match) {
bam_type = *((enum sps_bam_type *)(match->data));
SPS_DBG3(sps, "sps:BAM type is:%d\n", bam_type);
} else {
bam_type = SPS_BAM_NDP;
SPS_DBG3(sps, "sps:use default BAM type:%d\n",
bam_type);
}
} else {
d_type = 0;
if (get_platform_data(pdev)) {
SPS_ERR(sps, "sps:%s:Fail to get platform data.",
__func__);
return -ENODEV;
}
SPS_DBG(sps, "%s", "sps:get platform data.");
bam_type = SPS_BAM_LEGACY;
}
/* Create Device */
sps->dev_class = class_create(THIS_MODULE, SPS_DRV_NAME);
ret = alloc_chrdev_region(&sps->dev_num, 0, 1, SPS_DRV_NAME);
if (ret) {
SPS_ERR(sps, "sps:%s:alloc_chrdev_region err.", __func__);
goto alloc_chrdev_region_err;
}
sps->dev = device_create(sps->dev_class, NULL, sps->dev_num, sps,
SPS_DRV_NAME);
if (IS_ERR(sps->dev)) {
SPS_ERR(sps, "sps:%s:device_create err.", __func__);
goto device_create_err;
}
if (pdev->dev.of_node)
sps->dev->of_node = pdev->dev.of_node;
if (!d_type) {
sps->pmem_clk = clk_get(sps->dev, "mem_clk");
if (IS_ERR(sps->pmem_clk)) {
if (PTR_ERR(sps->pmem_clk) == -EPROBE_DEFER)
ret = -EPROBE_DEFER;
else
SPS_ERR(sps, "sps:%s:fail to get pmem_clk.",
__func__);
goto pmem_clk_err;
} else {
ret = clk_prepare_enable(sps->pmem_clk);
if (ret) {
SPS_ERR(sps,
"sps:%s:failed to enable pmem_clk.",
__func__);
goto pmem_clk_en_err;
}
}
}
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
sps->dfab_clk = clk_get(sps->dev, "dfab_clk");
if (IS_ERR(sps->dfab_clk)) {
if (PTR_ERR(sps->dfab_clk) == -EPROBE_DEFER)
ret = -EPROBE_DEFER;
else
SPS_ERR(sps, "sps:%s:fail to get dfab_clk.", __func__);
goto dfab_clk_err;
} else {
ret = clk_set_rate(sps->dfab_clk, 64000000);
if (ret) {
SPS_ERR(sps, "sps:%s:failed to set dfab_clk rate.",
__func__);
clk_put(sps->dfab_clk);
goto dfab_clk_err;
}
}
sps->bamdma_clk = clk_get(sps->dev, "dma_bam_pclk");
if (IS_ERR(sps->bamdma_clk)) {
if (PTR_ERR(sps->bamdma_clk) == -EPROBE_DEFER)
ret = -EPROBE_DEFER;
else
SPS_ERR(sps, "sps:%s:fail to get bamdma_clk.",
__func__);
clk_put(sps->dfab_clk);
goto dfab_clk_err;
} else {
ret = clk_prepare_enable(sps->bamdma_clk);
if (ret) {
SPS_ERR(sps, "sps:failed to enable bamdma_clk. ret=%d",
ret);
clk_put(sps->bamdma_clk);
clk_put(sps->dfab_clk);
goto dfab_clk_err;
}
}
ret = clk_prepare_enable(sps->dfab_clk);
if (ret) {
SPS_ERR(sps, "sps:failed to enable dfab_clk. ret=%d", ret);
clk_disable_unprepare(sps->bamdma_clk);
clk_put(sps->bamdma_clk);
clk_put(sps->dfab_clk);
goto dfab_clk_err;
}
#endif
ret = sps_device_init();
if (ret) {
SPS_ERR(sps, "sps:%s:sps_device_init err.", __func__);
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
clk_disable_unprepare(sps->dfab_clk);
clk_disable_unprepare(sps->bamdma_clk);
clk_put(sps->bamdma_clk);
clk_put(sps->dfab_clk);
#endif
goto dfab_clk_err;
}
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
clk_disable_unprepare(sps->dfab_clk);
clk_disable_unprepare(sps->bamdma_clk);
#endif
sps->is_ready = true;
SPS_INFO(sps, "%s", "sps:sps is ready.\n");
return 0;
dfab_clk_err:
if (!d_type)
clk_disable_unprepare(sps->pmem_clk);
pmem_clk_en_err:
if (!d_type)
clk_put(sps->pmem_clk);
pmem_clk_err:
device_destroy(sps->dev_class, sps->dev_num);
device_create_err:
unregister_chrdev_region(sps->dev_num, 1);
alloc_chrdev_region_err:
class_destroy(sps->dev_class);
return ret;
}
static int msm_sps_remove(struct platform_device *pdev)
{
SPS_DBG3(sps, "sps:%s.\n", __func__);
device_destroy(sps->dev_class, sps->dev_num);
unregister_chrdev_region(sps->dev_num, 1);
class_destroy(sps->dev_class);
sps_device_de_init();
clk_put(sps->dfab_clk);
if (!d_type)
clk_put(sps->pmem_clk);
clk_put(sps->bamdma_clk);
return 0;
}
static struct platform_driver msm_sps_driver = {
.probe = msm_sps_probe,
.driver = {
.name = SPS_DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = msm_sps_match,
.suppress_bind_attrs = true,
},
.remove = msm_sps_remove,
};
/**
* Module Init.
*/
static int __init sps_init(void)
{
int ret;
#ifdef CONFIG_DEBUG_FS
sps_debugfs_init();
#endif
pr_debug("sps:%s.", __func__);
/* Allocate the SPS driver state struct */
sps = kzalloc(sizeof(*sps), GFP_KERNEL);
if (sps == NULL)
return -ENOMEM;
sps->ipc_log0 = ipc_log_context_create(SPS_IPC_LOGPAGES,
"sps_ipc_log0", 0);
if (!sps->ipc_log0)
pr_err("Failed to create IPC log0\n");
sps->ipc_log1 = ipc_log_context_create(SPS_IPC_LOGPAGES,
"sps_ipc_log1", 0);
if (!sps->ipc_log1)
pr_err("Failed to create IPC log1\n");
sps->ipc_log2 = ipc_log_context_create(SPS_IPC_LOGPAGES,
"sps_ipc_log2", 0);
if (!sps->ipc_log2)
pr_err("Failed to create IPC log2\n");
sps->ipc_log3 = ipc_log_context_create(SPS_IPC_LOGPAGES,
"sps_ipc_log3", 0);
if (!sps->ipc_log3)
pr_err("Failed to create IPC log3\n");
sps->ipc_log4 = ipc_log_context_create(SPS_IPC_LOGPAGES *
SPS_IPC_REG_DUMP_FACTOR, "sps_ipc_log4", 0);
if (!sps->ipc_log4)
pr_err("Failed to create IPC log4\n");
ret = platform_driver_register(&msm_sps_driver);
return ret;
}
/**
* Module Exit.
*/
static void __exit sps_exit(void)
{
pr_debug("sps:%s.", __func__);
platform_driver_unregister(&msm_sps_driver);
if (sps != NULL) {
kfree(sps);
sps = NULL;
}
#ifdef CONFIG_DEBUG_FS
sps_debugfs_exit();
#endif
}
arch_initcall(sps_init);
module_exit(sps_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Smart Peripheral Switch (SPS)");