unplugged-kernel/drivers/misc/mediatek/eccci/port/port_smem.c

1000 lines
29 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/kmemleak.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif
#include "mt-plat/mtk_ccci_common.h"
#include "ccci_fsm.h"
#include "port_smem.h"
#define TAG SMEM
#define DUMMY_PAGE_SIZE (128)
#define DUMMY_PADDING_CNT (5)
#define CTRL_PAGE_SIZE (1024)
#define CTRL_PAGE_NUM (32)
#define MD_EX_PAGE_SIZE (20*1024)
#define MD_EX_PAGE_NUM (6)
/*
* Note : Moidy this size will affect dhl frame size in this page
* Minimum : 352B to reserve 256B for header frame
*/
#define MD_HW_PAGE_SIZE (512)
/* replace with HW page */
#define MD_BUF1_PAGE_SIZE (MD_HW_PAGE_SIZE)
#define MD_BUF1_PAGE_NUM (72)
#define AP_BUF1_PAGE_SIZE (1024)
#define AP_BUF1_PAGE_NUM (32)
#define MD_BUF2_0_PAGE_SIZE (MD_HW_PAGE_SIZE)
#define MD_BUF2_1_PAGE_SIZE (MD_HW_PAGE_SIZE)
#define MD_BUF2_2_PAGE_SIZE (MD_HW_PAGE_SIZE)
#define MD_BUF2_0_PAGE_NUM (64)
#define MD_BUF2_1_PAGE_NUM (64)
#define MD_BUF2_2_PAGE_NUM (256)
#define MD_MDM_PAGE_SIZE (MD_HW_PAGE_SIZE)
#define MD_MDM_PAGE_NUM (32)
#define AP_MDM_PAGE_SIZE (1024)
#define AP_MDM_PAGE_NUM (16)
#define MD_META_PAGE_SIZE (65*1024)
#define MD_META_PAGE_NUM (8)
#define AP_META_PAGE_SIZE (63*1024)
/*kernel 4.14 diff kernel 4.19
kernel4.14中
MT6765-----GEN93
MT6833-----GEN97
MT6853 (MT6877)------GEN97
#define AP_META_PAGE_SIZE (65*1024)
其他部分
#define AP_META_PAGE_SIZE (63*1024)
kernel4.19中
#define AP_META_PAGE_SIZE (63*1024)
*/
#define AP_META_PAGE_NUM (8)
struct ccci_ccb_config ccb_configs[] = {
{SMEM_USER_CCB_DHL, P_CORE, CTRL_PAGE_SIZE,
CTRL_PAGE_SIZE, CTRL_PAGE_SIZE*CTRL_PAGE_NUM,
CTRL_PAGE_SIZE*CTRL_PAGE_NUM}, /* Ctrl */
{SMEM_USER_CCB_DHL, P_CORE, MD_EX_PAGE_SIZE,
DUMMY_PAGE_SIZE, MD_EX_PAGE_SIZE*MD_EX_PAGE_NUM,
DUMMY_PAGE_SIZE}, /* exception */
{SMEM_USER_CCB_DHL, P_CORE, MD_BUF1_PAGE_SIZE,
AP_BUF1_PAGE_SIZE, (MD_BUF1_PAGE_SIZE*MD_BUF1_PAGE_NUM),
AP_BUF1_PAGE_SIZE*AP_BUF1_PAGE_NUM},/* PS */
{SMEM_USER_CCB_DHL, P_CORE, MD_BUF2_0_PAGE_SIZE,
DUMMY_PAGE_SIZE, MD_BUF2_0_PAGE_SIZE*MD_BUF2_0_PAGE_NUM,
DUMMY_PAGE_SIZE}, /* HWLOGGER1 */
{SMEM_USER_CCB_DHL, P_CORE, MD_BUF2_1_PAGE_SIZE,
DUMMY_PAGE_SIZE, MD_BUF2_1_PAGE_SIZE*MD_BUF2_1_PAGE_NUM,
DUMMY_PAGE_SIZE}, /* HWLOGGER2 */
{SMEM_USER_CCB_DHL, P_CORE, MD_BUF2_2_PAGE_SIZE,
DUMMY_PAGE_SIZE, MD_BUF2_2_PAGE_SIZE*MD_BUF2_2_PAGE_NUM,
DUMMY_PAGE_SIZE}, /* HWLOGGER3 */
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_DHL, P_CORE, DUMMY_PAGE_SIZE,
DUMMY_PAGE_SIZE, DUMMY_PAGE_SIZE*DUMMY_PADDING_CNT,
DUMMY_PAGE_SIZE},
{SMEM_USER_CCB_MD_MONITOR, P_CORE, MD_MDM_PAGE_SIZE,
AP_MDM_PAGE_SIZE, MD_MDM_PAGE_SIZE*MD_MDM_PAGE_NUM,
AP_MDM_PAGE_SIZE*AP_MDM_PAGE_NUM}, /* MDM */
{SMEM_USER_CCB_META, P_CORE, MD_META_PAGE_SIZE,
AP_META_PAGE_SIZE, MD_META_PAGE_SIZE*MD_META_PAGE_NUM,
AP_META_PAGE_SIZE*AP_META_PAGE_NUM}, /* META */
};
unsigned int ccb_configs_len =
sizeof(ccb_configs)/sizeof(struct ccci_ccb_config);
#ifdef DEBUG_FOR_CCB
static struct buffer_header *s_ccb_ctl_head_tbl;
static unsigned int *s_dl_last_w;
static unsigned int s_dl_active_bitmap;
static unsigned int dl_active_scan(void)
{
unsigned int i;
struct buffer_header *ptr = NULL;
unsigned int bit_mask;
if (!s_ccb_ctl_head_tbl)
return 0;
if (!s_dl_last_w)
return 0;
ptr = s_ccb_ctl_head_tbl;
bit_mask = 0;
for (i = 0; i < ccb_configs_len; i++) {
if ((s_dl_last_w[i] != ptr[i].dl_write_index) ||
(ptr[i].dl_read_index != ptr[i].dl_write_index)) {
bit_mask |= 1 << i;
s_dl_last_w[i] = ptr[i].dl_write_index;
}
}
return bit_mask;
}
static inline int append_ccb_str(char buf[], int offset, int size,
unsigned int id, unsigned int w, unsigned int r)
{
int ret;
if (!buf)
return 0;
ret = snprintf(&buf[offset], size - offset, "[%u]w:%u-r:%u,", id, w, r);
if (ret > 0)
return ret + offset;
return 0;
}
static void ccb_fifo_peek(struct buffer_header *ptr)
{
unsigned int i, r, w, wakeup_map = 0;
int offset = 0;
char *out_buf;
out_buf = kmalloc(4096, GFP_ATOMIC);
for (i = 0; i < ccb_configs_len; i++) {
if (ptr[i].dl_read_index != ptr[i].dl_write_index) {
r = ptr[i].dl_read_index;
w = ptr[i].dl_write_index;
wakeup_map |= 1 << i;
offset = append_ccb_str(out_buf, offset, 4096, i, w, r);
}
}
if (out_buf) {
CCCI_NORMAL_LOG(0, "CCB", "Wakeup peek:0x%x %s\r\n", wakeup_map,
out_buf);
kfree(out_buf);
} else
CCCI_NORMAL_LOG(0, "CCB", "Wakeup peek bitmap: 0x%x\r\n",
wakeup_map);
}
void mtk_ccci_ccb_info_peek(void)
{
if (s_ccb_ctl_head_tbl)
ccb_fifo_peek(s_ccb_ctl_head_tbl);
}
#endif
static enum hrtimer_restart smem_tx_timer_func(struct hrtimer *timer)
{
struct ccci_smem_port *smem_port =
container_of(timer, struct ccci_smem_port, notify_timer);
ccci_md_send_ccb_tx_notify(smem_port->port->md_id,
smem_port->core_id);
return HRTIMER_NORESTART;
}
static void collect_ccb_info(int md_id, struct ccci_smem_port *smem_port)
{
unsigned int i, j, len, curr_size;
struct ccci_smem_region *prev = NULL, *curr = NULL;
if (md_id != MD_SYS1)
return;
if (smem_port->user_id < SMEM_USER_CCB_START
|| smem_port->user_id > SMEM_USER_CCB_END)
return;
/* check current port is CCB or not */
for (i = SMEM_USER_CCB_START; i <= SMEM_USER_CCB_END; i++) {
if (smem_port->user_id == i) {
curr_size = 0;
/* calculate length */
for (j = 0; j < ccb_configs_len; j++) {
/* search for first present */
if (smem_port->user_id ==
ccb_configs[j].user_id)
break;
}
smem_port->ccb_ctrl_offset = j;
for ( ; j < ccb_configs_len; j++) {
/* traverse to last present */
if (smem_port->user_id !=
ccb_configs[j].user_id)
break;
len = ccb_configs[j].dl_buff_size +
ccb_configs[j].ul_buff_size;
curr_size += len;
}
/* align to 4k */
curr_size = (curr_size + 0xFFF) & (~0xFFF);
curr = ccci_md_get_smem_by_user_id(md_id, i);
if (curr)
curr->size = curr_size;
CCCI_BOOTUP_LOG(md_id, TAG,
"CCB user %d: ccb_ctrl_offset=%d, length=%d\n",
i, smem_port->ccb_ctrl_offset, curr_size);
/* init other member */
smem_port->state = CCB_USER_INVALID;
smem_port->wakeup = 0;
smem_port->type = TYPE_CCB;
init_waitqueue_head(&smem_port->rx_wq);
hrtimer_init(&smem_port->notify_timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
smem_port->notify_timer.function = smem_tx_timer_func;
break;
}
}
/* refresh all CCB users' address, except the first one,
* because user's size has been re-calculated above
*/
if (SMEM_USER_CCB_END - SMEM_USER_CCB_START >= 1) {
for (i = SMEM_USER_CCB_START + 1;
i <= SMEM_USER_CCB_END; i++) {
curr = ccci_md_get_smem_by_user_id(md_id, i);
prev = ccci_md_get_smem_by_user_id(md_id, i - 1);
if (curr && prev) {
curr->base_ap_view_phy =
prev->base_ap_view_phy + prev->size;
curr->base_ap_view_vir =
prev->base_ap_view_vir + prev->size;
curr->base_md_view_phy =
prev->base_md_view_phy + prev->size;
curr->offset = prev->offset + prev->size;
CCCI_BOOTUP_LOG(md_id, TAG,
"CCB user %d: offset=%d, size=%d, base_ap = 0x%x, base_md = 0x%x\n",
i, curr->offset, curr->size,
(unsigned int)curr->base_ap_view_phy,
(unsigned int)curr->base_md_view_phy);
}
}
#ifdef DEBUG_FOR_CCB
curr = ccci_md_get_smem_by_user_id(md_id,
SMEM_USER_RAW_CCB_CTRL);
if (curr)
s_ccb_ctl_head_tbl =
(struct buffer_header *)curr->base_ap_view_vir;
#endif
}
}
int port_smem_tx_nofity(struct port_t *port, unsigned int user_data)
{
struct ccci_smem_port *smem_port =
(struct ccci_smem_port *)port->private_data;
if (smem_port->type != TYPE_CCB)
return -EFAULT;
if ((smem_port->addr_phy == 0) || (smem_port->length == 0))
return -EFAULT;
if (!hrtimer_active(&(smem_port->notify_timer))) {
smem_port->core_id = user_data;
ccci_md_send_ccb_tx_notify(smem_port->port->md_id,
smem_port->core_id);
hrtimer_start(&(smem_port->notify_timer),
ktime_set(0, 1000000), HRTIMER_MODE_REL);
}
return 0;
}
int port_smem_rx_poll(struct port_t *port, unsigned int user_data)
{
struct ccci_smem_port *smem_port =
(struct ccci_smem_port *)port->private_data;
#ifdef DEBUG_FOR_CCB
struct buffer_header *buf = smem_port->ccb_vir_addr;
unsigned char idx;
#endif
int md_state, ret;
unsigned long flags;
int md_id = port->md_id;
if (smem_port->type != TYPE_CCB)
return -EFAULT;
CCCI_DEBUG_LOG(md_id, TAG,
"before wait event, bitmask=%x\n", user_data);
#ifdef DEBUG_FOR_CCB
idx = smem_port->poll_save_idx;
if (idx >= CCB_POLL_PTR_MAX - 2) {
CCCI_ERROR_LOG(md_id, TAG,
"invalid idx = %d\n", idx);
return -EFAULT;
}
smem_port->last_poll_time[idx] = local_clock();
smem_port->last_in[idx].al_id = buf[0].dl_alloc_index;
smem_port->last_in[idx].fr_id = buf[0].dl_free_index;
smem_port->last_in[idx].r_id = buf[0].dl_read_index;
smem_port->last_in[idx].w_id = buf[0].dl_write_index;
smem_port->last_in[idx + 1].al_id = buf[1].dl_alloc_index;
smem_port->last_in[idx + 1].fr_id = buf[1].dl_free_index;
smem_port->last_in[idx + 1].r_id = buf[1].dl_read_index;
smem_port->last_in[idx + 1].w_id = buf[1].dl_write_index;
smem_port->last_in[idx + 2].al_id = buf[2].dl_alloc_index;
smem_port->last_in[idx + 2].fr_id = buf[2].dl_free_index;
smem_port->last_in[idx + 2].r_id = buf[2].dl_read_index;
smem_port->last_in[idx + 2].w_id = buf[2].dl_write_index;
if (user_data == 0x01) {
atomic_set(&smem_port->poll_processing[0], 1);
smem_port->last_mask[0] = user_data;
smem_port->last_poll_time[idx + 1] = local_clock();
} else {
atomic_set(&smem_port->poll_processing[1], 1);
smem_port->last_mask[1] = user_data;
smem_port->last_poll_time[idx + 2] = local_clock();
}
#endif
ret = wait_event_interruptible(smem_port->rx_wq,
smem_port->wakeup & user_data);
spin_lock_irqsave(&smem_port->write_lock, flags);
smem_port->wakeup &= ~user_data;
CCCI_DEBUG_LOG(md_id, TAG,
"after wait event, wakeup=%x\n", smem_port->wakeup);
spin_unlock_irqrestore(&smem_port->write_lock, flags);
if (ret == -ERESTARTSYS)
ret = -EINTR;
else {
md_state = ccci_fsm_get_md_state(md_id);
if (md_state == WAITING_TO_STOP) {
CCCI_REPEAT_LOG(md_id, TAG,
"smem poll return, md_state = %d\n", md_state);
ret = -ENODEV;
}
}
#ifdef DEBUG_FOR_CCB
smem_port->last_poll_t_exit[idx] = local_clock();
smem_port->last_out[idx].al_id = buf[0].dl_alloc_index;
smem_port->last_out[idx].fr_id = buf[0].dl_free_index;
smem_port->last_out[idx].r_id = buf[0].dl_read_index;
smem_port->last_out[idx].w_id = buf[0].dl_write_index;
smem_port->last_out[idx + 1].al_id = buf[1].dl_alloc_index;
smem_port->last_out[idx + 1].fr_id = buf[1].dl_free_index;
smem_port->last_out[idx + 1].r_id = buf[1].dl_read_index;
smem_port->last_out[idx + 1].w_id = buf[1].dl_write_index;
smem_port->last_out[idx + 2].al_id = buf[2].dl_alloc_index;
smem_port->last_out[idx + 2].fr_id = buf[2].dl_free_index;
smem_port->last_out[idx + 2].r_id = buf[2].dl_read_index;
smem_port->last_out[idx + 2].w_id = buf[2].dl_write_index;
if (user_data == 0x01) {
atomic_set(&smem_port->poll_processing[0], 0);
smem_port->last_poll_t_exit[idx + 1] = local_clock();
} else {
atomic_set(&smem_port->poll_processing[1], 0);
smem_port->last_poll_t_exit[idx + 2] = local_clock();
}
idx += 3;
if (idx >= CCB_POLL_PTR_MAX)
idx = 0;
smem_port->poll_save_idx = idx;
#endif
return ret;
}
int port_smem_rx_wakeup(struct port_t *port)
{
struct ccci_smem_port *smem_port =
(struct ccci_smem_port *)port->private_data;
unsigned long flags;
int md_id = port->md_id;
if (smem_port == NULL)
return -EFAULT;
if (smem_port->type != TYPE_CCB)
return -EFAULT;
if ((smem_port->addr_phy == 0) || (smem_port->length == 0))
return -EFAULT;
spin_lock_irqsave(&smem_port->write_lock, flags);
smem_port->wakeup = 0xFFFFFFFF;
spin_unlock_irqrestore(&smem_port->write_lock, flags);
__pm_wakeup_event(port->rx_wakelock, jiffies_to_msecs(HZ));
CCCI_DEBUG_LOG(md_id, TAG, "wakeup port.\n");
#ifdef DEBUG_FOR_CCB
s_dl_active_bitmap |= dl_active_scan();
smem_port->last_rx_wk_time = local_clock();
#endif
wake_up_all(&smem_port->rx_wq);
return 0;
}
void __iomem *get_smem_start_addr(int md_id,
enum SMEM_USER_ID user_id, int *size_o)
{
void __iomem *addr = NULL;
struct ccci_smem_region *smem_region =
ccci_md_get_smem_by_user_id(md_id, user_id);
if (smem_region) {
addr = smem_region->base_ap_view_vir;
#if (MD_GENERATION < 6297)
/* dbm addr returned to user should
* step over Guard pattern header
*/
if (user_id == SMEM_USER_RAW_DBM)
addr += CCCI_SMEM_SIZE_DBM_GUARD;
#endif
if (size_o)
*size_o = smem_region->size;
}
return addr;
}
phys_addr_t get_smem_phy_start_addr(int md_id,
enum SMEM_USER_ID user_id, int *size_o)
{
phys_addr_t addr = 0;
struct ccci_smem_region *smem_region =
ccci_md_get_smem_by_user_id(md_id, user_id);
if (smem_region) {
addr = smem_region->base_ap_view_phy;
CCCI_NORMAL_LOG(md_id, TAG, "phy address: 0x%lx, ",
(unsigned long)addr);
if (size_o) {
*size_o = smem_region->size;
CCCI_NORMAL_LOG(md_id, TAG, "0x%x",
*size_o);
} else {
CCCI_NORMAL_LOG(md_id, TAG, "size_0 is NULL(invalid)");
}
}
return addr;
}
EXPORT_SYMBOL(get_smem_phy_start_addr);
static struct port_t *find_smem_port_by_user_id(int md_id, int user_id)
{
return port_get_by_minor(md_id, user_id + CCCI_SMEM_MINOR_BASE);
}
long port_ccb_ioctl(struct port_t *port, unsigned int cmd, unsigned long arg)
{
int md_id = port->md_id;
long ret = 0;
struct ccci_ccb_config in_ccb, out_ccb;
struct ccci_smem_region *ccb_ctl =
ccci_md_get_smem_by_user_id(md_id, SMEM_USER_RAW_CCB_CTRL);
struct ccb_ctrl_info ctrl_info;
struct port_t *s_port = NULL;
struct ccci_smem_port *smem_port =
(struct ccci_smem_port *)port->private_data;
if (ccb_ctl == NULL) {
CCCI_ERROR_LOG(md_id, TAG, "ccb ctrl is NULL!\n");
return -1;
}
/*
* all users share this ccb ctrl region, and
* port CCCI_SMEM_CH's initailization is special,
* so, these ioctl cannot use CCCI_SMEM_CH channel.
*/
switch (cmd) {
case CCCI_IOC_CCB_CTRL_BASE:
ret = put_user((unsigned int)ccb_ctl->base_ap_view_phy,
(unsigned int __user *)arg);
break;
case CCCI_IOC_CCB_CTRL_LEN:
ret = put_user((unsigned int)ccb_ctl->size,
(unsigned int __user *)arg);
break;
case CCCI_IOC_CCB_CTRL_INFO:
if (copy_from_user(&ctrl_info, (void __user *)arg,
sizeof(struct ccb_ctrl_info))) {
CCCI_ERROR_LOG(md_id, TAG,
"get ccb ctrl fail: copy_from_user fail!\n");
ret = -EINVAL;
break;
}
/*user id counts from ccb start*/
if (ctrl_info.user_id + SMEM_USER_CCB_START >
SMEM_USER_CCB_END) {
CCCI_ERROR_LOG(md_id, TAG,
"get ccb ctrl fail: user_id = %d!\n",
ctrl_info.user_id);
ret = -EINVAL;
break;
}
/*get ctrl info by user id*/
s_port = find_smem_port_by_user_id(md_id,
ctrl_info.user_id + SMEM_USER_CCB_START);
if (!s_port) {
CCCI_ERROR_LOG(md_id, TAG,
"get ccb port fail: user_id = %d!\n",
ctrl_info.user_id);
ret = -EINVAL;
break;
}
CCCI_NORMAL_LOG(md_id, TAG, "find ccb port %s for user%d!\n",
s_port->name, ctrl_info.user_id + SMEM_USER_CCB_START);
smem_port = (struct ccci_smem_port *)s_port->private_data;
ctrl_info.ctrl_offset = smem_port->ccb_ctrl_offset;
ctrl_info.ctrl_addr = (unsigned int)ccb_ctl->base_ap_view_phy;
ctrl_info.ctrl_length = (unsigned int)ccb_ctl->size;
if (copy_to_user((void __user *)arg, &ctrl_info,
sizeof(struct ccb_ctrl_info))) {
CCCI_ERROR_LOG(md_id, TAG,
"copy_to_user ccb ctrl failed !!\n");
ret = -EINVAL;
break;
}
/*set smem state as OK if get ccb ctrl success*/
smem_port->state = CCB_USER_OK;
break;
case CCCI_IOC_GET_CCB_CONFIG_LENGTH:
CCCI_NORMAL_LOG(md_id, TAG, "ccb_configs_len: %d\n",
ccb_configs_len);
ret = put_user(ccb_configs_len, (unsigned int __user *)arg);
break;
case CCCI_IOC_GET_CCB_CONFIG:
if (copy_from_user(&in_ccb, (void __user *)arg,
sizeof(struct ccci_ccb_config))) {
CCCI_ERROR_LOG(md_id, TAG,
"set user_id fail: copy_from_user fail!\n");
ret = -EINVAL;
break;
}
/* use user_id as input param, which is the array index,
* and it will override user space's ID value
*/
if (in_ccb.user_id >= ccb_configs_len) {
ret = -EINVAL;
break;
}
memcpy(&out_ccb, &ccb_configs[in_ccb.user_id],
sizeof(struct ccci_ccb_config));
/* user space's CCB array index is count from zero,
* as it only deal with CCB user, no raw user
*/
out_ccb.user_id -= SMEM_USER_CCB_START;
if (copy_to_user((void __user *)arg, &out_ccb,
sizeof(struct ccci_ccb_config)))
CCCI_ERROR_LOG(md_id, TAG,
"copy_to_user ccb failed !!\n");
break;
default:
ret = -1;
break;
}
return ret;
}
long port_smem_ioctl(struct port_t *port, unsigned int cmd, unsigned long arg)
{
long ret = 0;
int md_id = port->md_id;
unsigned int data;
struct ccci_smem_port *smem_port =
(struct ccci_smem_port *)port->private_data;
switch (cmd) {
case CCCI_IOC_SMEM_BASE:
smem_port = (struct ccci_smem_port *)port->private_data;
CCCI_NORMAL_LOG(md_id, TAG, "smem_port->addr_phy=%lx\n",
(unsigned long)smem_port->addr_phy);
ret = put_user((unsigned int)smem_port->addr_phy,
(unsigned int __user *)arg);
break;
case CCCI_IOC_SMEM_LEN:
smem_port = (struct ccci_smem_port *)port->private_data;
ret = put_user((unsigned int)smem_port->length,
(unsigned int __user *)arg);
break;
case CCCI_IOC_CCB_CTRL_OFFSET:
CCCI_REPEAT_LOG(md_id, TAG,
"rx_ch who invoke CCCI_IOC_CCB_CTRL_OFFSET:%d\n",
port->rx_ch);
if ((smem_port->addr_phy == 0) || (smem_port->length == 0)) {
ret = -EFAULT;
break;
}
smem_port = (struct ccci_smem_port *)port->private_data;
ret = put_user((unsigned int)smem_port->ccb_ctrl_offset,
(unsigned int __user *)arg);
CCCI_REPEAT_LOG(md_id, TAG,
"get ctrl_offset=%d\n", smem_port->ccb_ctrl_offset);
break;
case CCCI_IOC_SMEM_TX_NOTIFY:
if (copy_from_user(&data, (void __user *)arg,
sizeof(unsigned int))) {
CCCI_NORMAL_LOG(md_id, TAG,
"smem tx notify fail: copy_from_user fail!\n");
ret = -EFAULT;
} else
ret = port_smem_tx_nofity(port, data);
break;
case CCCI_IOC_SMEM_RX_POLL:
if (copy_from_user(&data, (void __user *)arg,
sizeof(unsigned int))) {
CCCI_NORMAL_LOG(md_id, TAG,
"smem rx poll fail: copy_from_user fail!\n");
ret = -EFAULT;
} else {
ret = port_smem_rx_poll(port, data);
}
break;
case CCCI_IOC_SMEM_SET_STATE:
smem_port = (struct ccci_smem_port *)port->private_data;
if (copy_from_user(&data, (void __user *)arg,
sizeof(unsigned int))) {
CCCI_NORMAL_LOG(md_id, TAG,
"smem set state fail: copy_from_user fail!\n");
ret = -EFAULT;
} else
smem_port->state = data;
break;
case CCCI_IOC_SMEM_GET_STATE:
smem_port = (struct ccci_smem_port *)port->private_data;
ret = put_user((unsigned int)smem_port->state,
(unsigned int __user *)arg);
break;
default:
ret = -1;
break;
}
return ret;
}
static long smem_dev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long ret = 0;
struct port_t *port = file->private_data;
int ch = port->rx_ch;
if (ch == CCCI_SMEM_CH)
ret = port_smem_ioctl(port, cmd, arg);
else if (ch == CCCI_CCB_CTRL)
ret = port_ccb_ioctl(port, cmd, arg);
if (ret == -1)
ret = port_dev_ioctl(file, cmd, arg);
return ret;
}
static int smem_dev_mmap(struct file *fp, struct vm_area_struct *vma)
{
struct port_t *port = fp->private_data;
struct ccci_smem_port *smem_port =
(struct ccci_smem_port *)port->private_data;
int md_id = port->md_id;
int ret;
unsigned long pfn, len;
struct ccci_smem_region *ccb_ctl =
ccci_md_get_smem_by_user_id(md_id, SMEM_USER_RAW_CCB_CTRL);
if ((smem_port->addr_phy == 0) || (smem_port->length == 0))
return -EFAULT;
switch (port->rx_ch) {
case CCCI_CCB_CTRL:
CCCI_NORMAL_LOG(md_id, CHAR,
"remap control addr:0x%llx len:%d map-len:%lu\n",
(unsigned long long)ccb_ctl->base_ap_view_phy,
ccb_ctl->size, vma->vm_end - vma->vm_start);
if (vma->vm_end < vma->vm_start) {
CCCI_ERROR_LOG(md_id, CHAR,
"vm_end:%lu < vm_start:%lu request from %s\n",
vma->vm_end, vma->vm_start, port->name);
return -EINVAL;
}
len = vma->vm_end - vma->vm_start;
if (len > ccb_ctl->size) {
CCCI_ERROR_LOG(md_id, CHAR,
"invalid mm size request from %s\n",
port->name);
return -EINVAL;
}
pfn = ccb_ctl->base_ap_view_phy;
pfn >>= PAGE_SHIFT;
/* ensure that memory does not get swapped to disk */
vma->vm_flags |= VM_IO;
/* ensure non-cacheable */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
ret = remap_pfn_range(vma, vma->vm_start, pfn,
len, vma->vm_page_prot);
if (ret) {
CCCI_ERROR_LOG(md_id, CHAR,
"remap failed %d/%lx, 0x%llx -> 0x%llx\n",
ret, pfn,
(unsigned long long)ccb_ctl->base_ap_view_phy,
(unsigned long long)vma->vm_start);
return -EAGAIN;
}
CCCI_NORMAL_LOG(md_id, CHAR,
"remap succeed %lx, 0x%llx -> 0x%llx\n", pfn,
(unsigned long long)ccb_ctl->base_ap_view_phy,
(unsigned long long)vma->vm_start);
break;
case CCCI_SMEM_CH:
CCCI_NORMAL_LOG(md_id, CHAR,
"remap addr:0x%llx len:%d map-len:%lu\n",
(unsigned long long)smem_port->addr_phy,
smem_port->length,
vma->vm_end - vma->vm_start);
if (vma->vm_end < vma->vm_start) {
CCCI_ERROR_LOG(md_id, CHAR,
"vm_end:%lu < vm_start:%lu request from %s\n",
vma->vm_end, vma->vm_start, port->name);
return -EINVAL;
}
len = vma->vm_end - vma->vm_start;
if (len > smem_port->length) {
CCCI_ERROR_LOG(md_id, CHAR,
"invalid mm size request from %s\n",
port->name);
return -EINVAL;
}
pfn = smem_port->addr_phy;
pfn >>= PAGE_SHIFT;
/* ensure that memory does not get swapped to disk */
vma->vm_flags |= VM_IO;
/* ensure non-cacheable */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
ret = remap_pfn_range(vma, vma->vm_start, pfn, len,
vma->vm_page_prot);
if (ret) {
CCCI_ERROR_LOG(md_id, CHAR,
"remap failed %d/%lx, 0x%llx -> 0x%llx\n",
ret, pfn,
(unsigned long long)smem_port->addr_phy,
(unsigned long long)vma->vm_start);
return -EAGAIN;
}
break;
default:
return -EPERM;
}
return 0;
}
static const struct file_operations smem_dev_fops = {
.owner = THIS_MODULE,
.open = &port_dev_open, /*use default API*/
.read = &port_dev_read, /*use default API*/
.write = &port_dev_write, /*use default API*/
.release = &port_dev_close,/*use default API*/
.unlocked_ioctl = &smem_dev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = &port_dev_compat_ioctl,
#endif
.mmap = &smem_dev_mmap,
};
int port_smem_init(struct port_t *port)
{
struct cdev *dev = NULL;
int ret = 0;
int md_id = port->md_id;
struct ccci_smem_port *smem_port = NULL;
struct ccci_smem_region *smem_region =
ccci_md_get_smem_by_user_id(md_id, port->minor);
#if (MD_GENERATION < 6293)
if (!smem_region) {
CCCI_ERROR_LOG(md_id, CHAR,
"smem port %d not available\n", port->minor);
return -CCCI_ERR_INVALID_LOGIC_CHANNEL_ID;
}
#endif
/*Set SMEM MINOR base*/
port->minor += CCCI_SMEM_MINOR_BASE;
if (port->flags & PORT_F_WITH_CHAR_NODE) {
dev = kmalloc(sizeof(struct cdev), GFP_KERNEL);
kmemleak_ignore(dev);
if (unlikely(!dev)) {
CCCI_ERROR_LOG(port->md_id, CHAR,
"alloc smem char dev fail!!\n");
return -1;
}
cdev_init(dev, &smem_dev_fops);
dev->owner = THIS_MODULE;
ret = cdev_add(dev,
MKDEV(port->major, port->minor_base + port->minor), 1);
ret = ccci_register_dev_node(port->name, port->major,
port->minor_base + port->minor);
port->interception = 0;
port->flags |= PORT_F_ADJUST_HEADER;
}
port->private_data = smem_port =
kzalloc(sizeof(struct ccci_smem_port), GFP_KERNEL);
if (smem_port == NULL) {
CCCI_ERROR_LOG(port->md_id, CHAR,
"alloc ccci_smem_port fail\n");
return -1;
}
kmemleak_ignore(smem_port);
/*user ID is from 0*/
smem_port->user_id = port->minor - CCCI_SMEM_MINOR_BASE;
spin_lock_init(&smem_port->write_lock);
smem_port->port = port;
if (!smem_region || smem_region->size == 0) {
smem_port->addr_phy = 0;
smem_port->addr_vir = 0;
smem_port->length = 0;
} else {
smem_port->addr_phy = smem_region->base_ap_view_phy;
smem_port->addr_vir = smem_region->base_ap_view_vir;
smem_port->length = smem_region->size;
/* this may override addr_phy/vir and length */
collect_ccb_info(md_id, smem_port);
}
#ifdef DEBUG_FOR_CCB
{
struct ccci_smem_region *ccb_ctl =
ccci_md_get_smem_by_user_id(md_id, SMEM_USER_RAW_CCB_CTRL);
smem_port->ccb_vir_addr =
(struct buffer_header *)ccb_ctl->base_ap_view_vir;
smem_port->poll_save_idx = 0;
}
s_dl_last_w = kmalloc(sizeof(int) * ccb_configs_len, GFP_KERNEL);
if (!s_dl_last_w) {
CCCI_ERROR_LOG(port->md_id, CHAR,
"%s:kmalloc s_dl_last_w fail\n",
__func__);
return -1;
}
kmemleak_ignore(s_dl_last_w);
#endif
return 0;
}
static void port_smem_md_state_notify(struct port_t *port, unsigned int state)
{
switch (state) {
case EXCEPTION:
case RESET:
case WAITING_TO_STOP:
port_smem_rx_wakeup(port);
break;
default:
break;
};
}
static void port_smem_queue_state_notify(struct port_t *port, int dir, int qno,
unsigned int qstate)
{
if (port->rx_ch == CCCI_SMEM_CH && qstate == RX_IRQ)
port_smem_rx_wakeup(port);
}
#ifdef DEBUG_FOR_CCB
static void port_smem_dump_info(struct port_t *port, unsigned int flag)
{
struct ccci_smem_port *smem_port =
(struct ccci_smem_port *)port->private_data;
unsigned long long ts = 0, ts_e = 0, ts_s = 0;
unsigned long nsec_rem = 0, nsec_rem_s = 0, nsec_rem_e = 0;
unsigned char idx;
if (smem_port->last_poll_time[0] == 0 &&
smem_port->last_poll_t_exit[0] == 0
/*&& smem_port->last_rx_wk_time ==0 */)
return;
ts = smem_port->last_rx_wk_time;
nsec_rem = do_div(ts, NSEC_PER_SEC);
CCCI_MEM_LOG_TAG(port->md_id, TAG,
"ccb port_smem(%d) poll history: last_wake<%llu.%06lu>, poll(%d/%d, 0x%x/0x%x)\n",
smem_port->user_id, ts, nsec_rem / 1000,
atomic_read(&smem_port->poll_processing[0]),
atomic_read(&smem_port->poll_processing[1]),
smem_port->last_mask[0], smem_port->last_mask[1]);
for (idx = 0; idx < CCB_POLL_PTR_MAX; idx++) {
ts_s = smem_port->last_poll_time[idx];
nsec_rem_s = do_div(ts_s, NSEC_PER_SEC);
ts_e = smem_port->last_poll_t_exit[idx];
nsec_rem_e = do_div(ts_e, NSEC_PER_SEC);
CCCI_MEM_LOG(port->md_id, TAG,
"<%llu.%06lu ~ %llu.%06lu> ",
ts_s, nsec_rem_s/1000, ts_e, nsec_rem_e/1000);
}
CCCI_MEM_LOG(port->md_id, TAG, "\n");
for (idx = 0; idx < CCB_POLL_PTR_MAX;) {
CCCI_MEM_LOG(port->md_id, TAG,
"0x%x, 0x%x, 0x%x, 0x%x; 0x%x, 0x%x, 0x%x, 0x%x; 0x%x, 0x%x, 0x%x, 0x%x",
smem_port->last_in[idx + 0].al_id,
smem_port->last_in[idx + 0].fr_id,
smem_port->last_in[idx + 0].r_id,
smem_port->last_in[idx + 0].w_id,
smem_port->last_in[idx + 1].al_id,
smem_port->last_in[idx + 1].fr_id,
smem_port->last_in[idx + 1].r_id,
smem_port->last_in[idx + 1].w_id,
smem_port->last_in[idx + 2].al_id,
smem_port->last_in[idx + 2].fr_id,
smem_port->last_in[idx + 2].r_id,
smem_port->last_in[idx + 2].w_id);
CCCI_MEM_LOG(port->md_id, TAG,
" ~ 0x%x, 0x%x, 0x%x, 0x%x; 0x%x, 0x%x, 0x%x, 0x%x; 0x%x, 0x%x, 0x%x, 0x%x\n",
smem_port->last_out[idx + 0].al_id,
smem_port->last_out[idx + 0].fr_id,
smem_port->last_out[idx + 0].r_id,
smem_port->last_out[idx + 0].w_id,
smem_port->last_out[idx + 1].al_id,
smem_port->last_out[idx + 1].fr_id,
smem_port->last_out[idx + 1].r_id,
smem_port->last_out[idx + 1].w_id,
smem_port->last_out[idx + 2].al_id,
smem_port->last_out[idx + 2].fr_id,
smem_port->last_out[idx + 2].r_id,
smem_port->last_out[idx + 2].w_id);
idx += 3;
}
CCCI_NORMAL_LOG(0, "CCB", "CCB active bitmap:0x%x\r\n",
s_dl_active_bitmap);
s_dl_active_bitmap = 0;
}
#endif
struct port_ops smem_port_ops = {
.init = &port_smem_init,
.md_state_notify = &port_smem_md_state_notify,
.queue_state_notify = &port_smem_queue_state_notify,
#ifdef DEBUG_FOR_CCB
.dump_info = port_smem_dump_info,
#endif
};