Commit 1bca5068 authored by carbon's avatar carbon

Add mailbox based inter-processor communication

parent 56595260
...@@ -270,6 +270,7 @@ CONFIG_ION_CARVEOUT_HEAP=y ...@@ -270,6 +270,7 @@ CONFIG_ION_CARVEOUT_HEAP=y
CONFIG_ION_CMA_HEAP=y CONFIG_ION_CMA_HEAP=y
# CONFIG_IOMMU_SUPPORT is not set # CONFIG_IOMMU_SUPPORT is not set
CONFIG_CV1835_SYSDMA_REMAP=y CONFIG_CV1835_SYSDMA_REMAP=y
CONFIG_CVI_MAILBOX=y
CONFIG_PWM=y CONFIG_PWM=y
CONFIG_SIFIVE_PLIC=y CONFIG_SIFIVE_PLIC=y
CONFIG_ANDROID=y CONFIG_ANDROID=y
......
...@@ -11,22 +11,9 @@ ...@@ -11,22 +11,9 @@
enum SYS_CMD_ID { enum SYS_CMD_ID {
SYS_CMD_INFO_TRANS = 0x50, CMD_TEST_A = 0x10,
SYS_CMD_INFO_LINUX_INIT_DONE, CMD_TEST_B,
SYS_CMD_INFO_RTOS_INIT_DONE, CMD_TEST_C,
SYS_CMD_INFO_STOP_ISR,
SYS_CMD_INFO_STOP_ISR_DONE,
SYS_CMD_INFO_LINUX,
SYS_CMD_INFO_RTOS,
SYS_CMD_SYNC_TIME,
SYS_CMD_INFO_DUMP_MSG,
SYS_CMD_INFO_DUMP_EN,
SYS_CMD_INFO_DUMP_DIS,
SYS_CMD_INFO_DUMP_JPG,
SYS_CMD_INFO_TRACE_SNAPSHOT_START,
SYS_CMD_INFO_TRACE_SNAPSHOT_STOP,
SYS_CMD_INFO_TRACE_STREAM_START,
SYS_CMD_INFO_TRACE_STREAM_STOP,
SYS_CMD_INFO_LIMIT, SYS_CMD_INFO_LIMIT,
}; };
......
...@@ -19,49 +19,80 @@ ...@@ -19,49 +19,80 @@
#include "cvi_spinlock.h" #include "cvi_spinlock.h"
//#define __DEBUG__ // #define __DEBUG__
#ifdef __DEBUG__ #ifdef __DEBUG__
#define debug_printf printf #define debug_printf printf
#else #else
#define debug_printf(...) #define debug_printf(...)
#endif #endif
typedef struct _TASK_CTX_S {
char name[32];
u16 stack_size;
UBaseType_t priority;
void (*runTask)(void *pvParameters);
u8 queLength;
QueueHandle_t queHandle;
} TASK_CTX_S;
/**************************************************************************** /****************************************************************************
* Function prototypes * Function prototypes
****************************************************************************/ ****************************************************************************/
void prvQueueISR(void);
void prvCmdQuRunTask(void *pvParameters);
/**************************************************************************** /****************************************************************************
* Global parameters * Global parameters
****************************************************************************/ ****************************************************************************/
TASK_CTX_S gTaskCtx[1] = {
{
.name = "CMDQU",
.stack_size = configMINIMAL_STACK_SIZE,
.priority = tskIDLE_PRIORITY + 5,
.runTask = prvCmdQuRunTask,
.queLength = 30,
.queHandle = NULL,
},
};
/* mailbox parameters */ /* mailbox parameters */
volatile struct mailbox_set_register *mbox_reg; volatile struct mailbox_set_register *mbox_reg;
volatile struct mailbox_done_register *mbox_done_reg; volatile struct mailbox_done_register *mbox_done_reg;
volatile unsigned long *mailbox_context; // mailbox buffer context is 64 Bytess volatile unsigned long *mailbox_context; // mailbox buffer context is 64 Bytess
/**************************************************************************** /****************************************************************************
* Function definitions * Function definitions
****************************************************************************/ ****************************************************************************/
DEFINE_CVI_SPINLOCK(mailbox_lock, SPIN_MBOX);
void main_create_tasks(void)
{
u8 i = 0;
#define TASK_INIT(_idx) \
do { \
gTaskCtx[_idx].queHandle = xQueueCreate(gTaskCtx[_idx].queLength, sizeof(cmdqu_t)); \
if (gTaskCtx[_idx].queHandle != NULL && gTaskCtx[_idx].runTask != NULL) { \
xTaskCreate(gTaskCtx[_idx].runTask, gTaskCtx[_idx].name, gTaskCtx[_idx].stack_size, \
NULL, gTaskCtx[_idx].priority, NULL); \
} \
} while(0)
for (; i < ARRAY_SIZE(gTaskCtx); i++) {
DEFINE_CVI_SPINLOCK(mailbox_lock, SPIN_MBOX); TASK_INIT(i);
}
}
void main_cvirtos(void) void main_cvirtos(void)
{ {
printf("create cvi task\n"); printf("create cvi task\n");
/* Start the tasks and timer running. */ /* Start the tasks and timer running. */
request_irq(MBOX_INT_C906_2ND, prvQueueISR, 0, "mailbox", (void *)0);
main_create_tasks();
/* Start the tasks and timer running. */
vTaskStartScheduler();
/* If all is well, the scheduler will now be running, and the following /* If all is well, the scheduler will now be running, and the following
...@@ -77,3 +108,160 @@ void main_cvirtos(void) ...@@ -77,3 +108,160 @@ void main_cvirtos(void)
for (;;) for (;;)
; ;
} }
void prvCmdQuRunTask(void *pvParameters)
{
/* Remove compiler warning about unused parameter. */
(void)pvParameters;
cmdqu_t rtos_cmdq;
cmdqu_t *cmdq;
cmdqu_t *rtos_cmdqu_t;
static int stop_ip = 0;
int ret = 0;
int flags;
int valid;
int send_to_cpu = SEND_TO_CPU1;
unsigned int reg_base = MAILBOX_REG_BASE;
/* to compatible code with linux side */
cmdq = &rtos_cmdq;
mbox_reg = (struct mailbox_set_register *) reg_base;
mbox_done_reg = (struct mailbox_done_register *) (reg_base + 2);
mailbox_context = (unsigned long *) (MAILBOX_REG_BUFF);
cvi_spinlock_init();
printf("prvCmdQuRunTask run\n");
for (;;) {
xQueueReceive(gTaskCtx[0].queHandle, &rtos_cmdq, portMAX_DELAY);
switch (rtos_cmdq.cmd_id) {
case CMD_TEST_A:
//do something
//send to C906B
rtos_cmdq.cmd_id = CMD_TEST_A;
rtos_cmdq.param_ptr = 0x12345678;
rtos_cmdq.resv.valid.rtos_valid = 1;
rtos_cmdq.resv.valid.linux_valid = 0;
printf("recv cmd(%d) from C906B...send [0x%x] to C906B\n", rtos_cmdq.cmd_id, rtos_cmdq.param_ptr);
goto send_label;
case CMD_TEST_B:
//nothing to do
printf("nothing to do...\n");
break;
case CMD_TEST_C:
rtos_cmdq.cmd_id = CMD_TEST_C;
rtos_cmdq.param_ptr = 0x55aa;
rtos_cmdq.resv.valid.rtos_valid = 1;
rtos_cmdq.resv.valid.linux_valid = 0;
printf("recv cmd(%d) from C906B...send [0x%x] to C906B\n", rtos_cmdq.cmd_id, rtos_cmdq.param_ptr);
goto send_label;
default:
send_label:
/* used to send command to linux*/
rtos_cmdqu_t = (cmdqu_t *) mailbox_context;
debug_printf("RTOS_CMDQU_SEND %d\n", send_to_cpu);
debug_printf("ip_id=%d cmd_id=%d param_ptr=%x\n", cmdq->ip_id, cmdq->cmd_id, (unsigned int)cmdq->param_ptr);
debug_printf("mailbox_context = %x\n", mailbox_context);
debug_printf("linux_cmdqu_t = %x\n", rtos_cmdqu_t);
debug_printf("cmdq->ip_id = %d\n", cmdq->ip_id);
debug_printf("cmdq->cmd_id = %d\n", cmdq->cmd_id);
debug_printf("cmdq->block = %d\n", cmdq->block);
debug_printf("cmdq->para_ptr = %x\n", cmdq->param_ptr);
drv_spin_lock_irqsave(&mailbox_lock, flags);
if (flags == MAILBOX_LOCK_FAILED) {
printf("[%s][%d] drv_spin_lock_irqsave failed! ip_id = %d , cmd_id = %d\n" , cmdq->ip_id , cmdq->cmd_id);
break;
}
for (valid = 0; valid < MAILBOX_MAX_NUM; valid++) {
if (rtos_cmdqu_t->resv.valid.linux_valid == 0 && rtos_cmdqu_t->resv.valid.rtos_valid == 0) {
// mailbox buffer context is 4 bytes write access
int *ptr = (int *)rtos_cmdqu_t;
cmdq->resv.valid.rtos_valid = 1;
*ptr = ((cmdq->ip_id << 0) | (cmdq->cmd_id << 8) | (cmdq->block << 15) |
(cmdq->resv.valid.linux_valid << 16) |
(cmdq->resv.valid.rtos_valid << 24));
rtos_cmdqu_t->param_ptr = cmdq->param_ptr;
debug_printf("rtos_cmdqu_t->linux_valid = %d\n", rtos_cmdqu_t->resv.valid.linux_valid);
debug_printf("rtos_cmdqu_t->rtos_valid = %d\n", rtos_cmdqu_t->resv.valid.rtos_valid);
debug_printf("rtos_cmdqu_t->ip_id =%x %d\n", &rtos_cmdqu_t->ip_id, rtos_cmdqu_t->ip_id);
debug_printf("rtos_cmdqu_t->cmd_id = %d\n", rtos_cmdqu_t->cmd_id);
debug_printf("rtos_cmdqu_t->block = %d\n", rtos_cmdqu_t->block);
debug_printf("rtos_cmdqu_t->param_ptr addr=%x %x\n", &rtos_cmdqu_t->param_ptr, rtos_cmdqu_t->param_ptr);
debug_printf("*ptr = %x\n", *ptr);
// clear mailbox
mbox_reg->cpu_mbox_set[send_to_cpu].cpu_mbox_int_clr.mbox_int_clr = (1 << valid);
// trigger mailbox valid to rtos
mbox_reg->cpu_mbox_en[send_to_cpu].mbox_info |= (1 << valid);
mbox_reg->mbox_set.mbox_set = (1 << valid);
break;
}
rtos_cmdqu_t++;
}
drv_spin_unlock_irqrestore(&mailbox_lock, flags);
if (valid >= MAILBOX_MAX_NUM) {
printf("No valid mailbox is available\n");
return -1;
}
break;
}
}
}
void prvQueueISR(void)
{
printf("prvQueueISR\n");
unsigned char set_val;
unsigned char valid_val;
int i;
cmdqu_t *cmdq;
BaseType_t YieldRequired = pdFALSE;
set_val = mbox_reg->cpu_mbox_set[RECEIVE_CPU].cpu_mbox_int_int.mbox_int;
if (set_val) {
for(i = 0; i < MAILBOX_MAX_NUM; i++) {
valid_val = set_val & (1 << i);
if (valid_val) {
cmdqu_t rtos_cmdq;
cmdq = (cmdqu_t *)(mailbox_context) + i;
debug_printf("mailbox_context =%x\n", mailbox_context);
debug_printf("sizeof mailbox_context =%x\n", sizeof(cmdqu_t));
/* mailbox buffer context is send from linux, clear mailbox interrupt */
mbox_reg->cpu_mbox_set[RECEIVE_CPU].cpu_mbox_int_clr.mbox_int_clr = valid_val;
// need to disable enable bit
mbox_reg->cpu_mbox_en[RECEIVE_CPU].mbox_info &= ~valid_val;
// copy cmdq context (8 bytes) to buffer ASAP
*((unsigned long *) &rtos_cmdq) = *((unsigned long *)cmdq);
/* need to clear mailbox interrupt before clear mailbox buffer */
*((unsigned long*) cmdq) = 0;
/* mailbox buffer context is send from linux*/
if (rtos_cmdq.resv.valid.linux_valid == 1) {
debug_printf("cmdq=%x\n", cmdq);
debug_printf("cmdq->ip_id =%d\n", rtos_cmdq.ip_id);
debug_printf("cmdq->cmd_id =%d\n", rtos_cmdq.cmd_id);
debug_printf("cmdq->param_ptr =%x\n", rtos_cmdq.param_ptr);
debug_printf("cmdq->block =%x\n", rtos_cmdq.block);
debug_printf("cmdq->linux_valid =%d\n", rtos_cmdq.resv.valid.linux_valid);
debug_printf("cmdq->rtos_valid =%x\n", rtos_cmdq.resv.valid.rtos_valid);
xQueueSendFromISR(gTaskCtx[0].queHandle, &rtos_cmdq, &YieldRequired);
portYIELD_FROM_ISR(YieldRequired);
} else
printf("rtos cmdq is not valid %d, ip=%d , cmd=%d\n",
rtos_cmdq.resv.valid.rtos_valid, rtos_cmdq.ip_id, rtos_cmdq.cmd_id);
}
}
}
}
...@@ -103,6 +103,7 @@ within this file. */ ...@@ -103,6 +103,7 @@ within this file. */
void vApplicationMallocFailedHook(void); void vApplicationMallocFailedHook(void);
void vApplicationIdleHook(void); void vApplicationIdleHook(void);
void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName); void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName);
void vApplicationTickHook(void);
/* configAPPLICATION_ALLOCATED_HEAP is set to 1 in FreeRTOSConfig.h so the /* configAPPLICATION_ALLOCATED_HEAP is set to 1 in FreeRTOSConfig.h so the
application can define the array used as the FreeRTOS heap. This is done so the application can define the array used as the FreeRTOS heap. This is done so the
...@@ -220,6 +221,17 @@ void vApplicationIdleHook(void) ...@@ -220,6 +221,17 @@ void vApplicationIdleHook(void)
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
void vApplicationTickHook(void)
{
#ifdef FULL_DEMO
{
/* Only the comprehensive demo actually uses the tick hook. */
extern void vFullDemoTickHook(void);
vFullDemoTickHook();
}
#endif
}
/*-----------------------------------------------------------*/
/* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide an /* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide an
implementation of vApplicationGetIdleTaskMemory() to provide the memory that is implementation of vApplicationGetIdleTaskMemory() to provide the memory that is
......
...@@ -30,4 +30,5 @@ config CVI_BT_PIN ...@@ -30,4 +30,5 @@ config CVI_BT_PIN
GPIO number to do certain action, ex. power on GPIO number to do certain action, ex. power on
or wakeup pin. or wakeup pin.
source "drivers/soc/cvitek/rtos_cmdqu/Kconfig"
endmenu endmenu
obj-$(CONFIG_CV1835_SYSDMA_REMAP) += sysdma/cv1835_dma_remap.o obj-$(CONFIG_CV1835_SYSDMA_REMAP) += sysdma/cv1835_dma_remap.o
obj-$(CONFIG_CVI_WIFI_PIN) += wifi_pin/cvi_wifi_pin.o obj-$(CONFIG_CVI_WIFI_PIN) += wifi_pin/cvi_wifi_pin.o
obj-$(CONFIG_CVI_BT_PIN) += bt_pin/cvi_bt_pin.o obj-$(CONFIG_CVI_BT_PIN) += bt_pin/cvi_bt_pin.o
\ No newline at end of file obj-$(CONFIG_CVI_MAILBOX) += rtos_cmdqu/
config CVI_MAILBOX
tristate "cv180x/cv181x mailbox dirver"
help
"cv180x/cv181x mailbox driver"
obj-$(CONFIG_CVI_MAILBOX) := cvi_mbox.o
cvi_mbox-y := rtos_cmdqu.o \
cvi_spinlock.o
ccflags-y += -I$(srctree)/$(src)/
#ifndef __CVI_MAILBOX_H__
#define __CVI_MAILBOX_H__
union cpu_mailbox_info_offset{
char mbox_info;
int reserved;
};
union cpu_mailbox_int_clr_offset{
char mbox_int_clr;
int reserved;
};
union cpu_mailbox_int_mask_offset{
char mbox_int_mask;
int reserved;
};
union cpu_mailbox_int_offset{
char mbox_int;
int reserved;
};
union cpu_mailbox_int_raw_offset{
char mbox_int_raw;
int reserved;
};
union mailbox_set{
char mbox_set;
int reserved;
};
union mailbox_status{
char mbox_status;
int reserved;
};
union cpu_mailbox_status{
char mbox_status;
int reserved;
};
/* register mapping refers to mailbox user guide*/
struct cpu_mbox_int{
union cpu_mailbox_int_clr_offset cpu_mbox_int_clr;
union cpu_mailbox_int_mask_offset cpu_mbox_int_mask;
union cpu_mailbox_int_offset cpu_mbox_int_int;
union cpu_mailbox_int_raw_offset cpu_mbox_int_raw;
};
struct mailbox_set_register{
union cpu_mailbox_info_offset cpu_mbox_en[4]; //0x00, 0x04, 0x08, 0x0c
struct cpu_mbox_int cpu_mbox_set[4]; //0x10~0x1C, 0x20~0x2C, 0x30~0x3C, 0x40~0x4C
int reserved[4]; //0x50~0x5C
union mailbox_set mbox_set; //0x60
union mailbox_status mbox_status; //0x64
int reserved2[2]; //0x68~0x6C
union cpu_mailbox_status cpu_mbox_status[4]; //0x70
};
struct mailbox_done_register{
union cpu_mailbox_info_offset cpu_mbox_done_en[4];
struct cpu_mbox_int cpu_mbox_done[4];
};
volatile struct mailbox_set_register *mbox_reg;
volatile struct mailbox_done_register *mbox_done_reg;
volatile unsigned long *mailbox_context; // mailbox buffer context is 64 Bytess
#define MAILBOX_MAX_NUM 0x0008
#define MAILBOX_DONE_OFFSET 0x0002
#define MAILBOX_CONTEXT_OFFSET 0x0400
// C906B
#define RECEIVE_CPU 1
// C906L
#define SEND_TO_CPU 2
#endif // end of__CVI_MAILBOX_H__
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) Cvitek Co., Ltd. 2019-2022. All rights reserved.
*
* File Name: cvi_spinlock.c
* Description:
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_reserved_mem.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include "cvi_spinlock.h"
static unsigned long reg_base;
spinlock_t reg_write_lock;
static unsigned char lockCount[SPIN_MAX+1] = {0};
static void *__iomem c906l_pc_reg;
void cvi_spinlock_init(void)
{
spin_lock_init(&reg_write_lock);
c906l_pc_reg = ioremap(0x1901070, 4);
if (c906l_pc_reg == NULL) {
pr_err("c906l_pc_reg ioremap failed!\n");
}
pr_info("[%s] success\n", __func__);
}
void cvi_spinlock_uninit(void)
{
iounmap(c906l_pc_reg);
}
void spinlock_base(unsigned long mb_base)
{
reg_base = mb_base;
}
static inline int hw_spin_trylock(hw_raw_spinlock_t *lock)
{
writew(lock->locks, (void *)(reg_base + sizeof(int) * lock->hw_field));
if (readw((void *)(reg_base + sizeof(int) * lock->hw_field)) == lock->locks)
return MAILBOX_LOCK_SUCCESS;
return MAILBOX_LOCK_FAILED;
}
int hw_spin_lock(hw_raw_spinlock_t *lock)
{
u64 i;
u64 loops = 1000000;
hw_raw_spinlock_t _lock = {.hw_field = lock->hw_field, .locks=lock->locks};
if (lock->hw_field >= SPIN_LINUX_RTOS) {
unsigned long flags;
spin_lock_irqsave(&reg_write_lock, flags);
if (lockCount[lock->hw_field] == 0) {
lockCount[lock->hw_field]++;
}
_lock.locks = lockCount[lock->hw_field];
lockCount[lock->hw_field]++;
spin_unlock_irqrestore(&reg_write_lock, flags);
}
else {
//....
}
for (i = 0; i < loops; i++) {
if (hw_spin_trylock(&_lock) == MAILBOX_LOCK_SUCCESS)
{
lock->locks = _lock.locks;
return MAILBOX_LOCK_SUCCESS;
}
udelay(1);
}
pr_err("__spin_lock_debug fail! loops = %lld\n", loops);
return MAILBOX_LOCK_FAILED;
}
int _hw_raw_spin_lock_irqsave(hw_raw_spinlock_t *lock)
{
int flag = MAILBOX_LOCK_SUCCESS;
// lock
if (hw_spin_lock(lock) == MAILBOX_LOCK_FAILED) {
pr_err("spin lock fail! C906L pc = 0x%x,reg_val=0x%x, lock->locks=0x%x\n",
ioread32(c906l_pc_reg), readw((void *)(reg_base + sizeof(int) * lock->hw_field)), lock->locks);
return MAILBOX_LOCK_FAILED;
}
return flag;
}
void _hw_raw_spin_unlock_irqrestore(hw_raw_spinlock_t *lock, int flag)
{
// unlock
if (readw((void *)(reg_base + sizeof(int) * lock->hw_field)) == lock->locks) {
writew(lock->locks, (void *)(reg_base + sizeof(int) * lock->hw_field));
} else {
pr_err("spin unlock fail! C906L pc=0x%x,reg_val=0x%x, lock->locks=0x%x\n",
ioread32(c906l_pc_reg), readw((void *)(reg_base + sizeof(int) * lock->hw_field)), lock->locks);
}
}
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __DRV_SPINLOCK_H__
#define __DRV_SPINLOCK_H__
enum SPINLOCK_FIELD {
SPIN_UART,
SPIN_LINUX_RTOS = 4, // this spinlock field is used for linux & rtos
SPIN_MBOX = SPIN_LINUX_RTOS,
SPIN_MAX = 7,
};
typedef struct hw_raw_spinlock {
unsigned short locks;
unsigned short hw_field;
} hw_raw_spinlock_t;
#define MAILBOX_LOCK_SUCCESS 1
#define MAILBOX_LOCK_FAILED (-1)
#define __CVI_ARCH_SPIN_LOCK_UNLOCKED \
(0)
#define __CVI_RAW_SPIN_LOCK_INITIALIZER(spinlock_hw_field) \
{ .locks = __CVI_ARCH_SPIN_LOCK_UNLOCKED, .hw_field = spinlock_hw_field, }
#define DEFINE_CVI_SPINLOCK(x, y) \
hw_raw_spinlock_t x = __CVI_RAW_SPIN_LOCK_INITIALIZER(y)
int _hw_raw_spin_lock_irqsave(hw_raw_spinlock_t *lock);
void _hw_raw_spin_unlock_irqrestore(hw_raw_spinlock_t *lock, int flag);
#define drv_spin_lock_irqsave(lock, flags) \
{ flags = _hw_raw_spin_lock_irqsave(lock); }
#define drv_spin_unlock_irqrestore(lock, flags) \
_hw_raw_spin_unlock_irqrestore(lock, flags)
void spinlock_base(unsigned long mb_base);
void cvi_spinlock_init(void);
void cvi_spinlock_uninit(void);
#endif // end of __DRV_SPINLOCK_H__
#include <linux/init.h>
#include <linux/module.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/list.h>
#include <linux/time.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/of_reserved_mem.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include "rtos_cmdqu.h"
#include "cvi_mailbox.h"
#include "cvi_spinlock.h"
struct cvi_rtos_cmdqu_device {
struct device *dev;
struct miscdevice miscdev;
};
spinlock_t mailbox_queue_lock;
spinlock_t send_queue_lock;
static __u64 reg_base;
static int mailbox_irq;
static int cvi_rtos_cmdqu_open(struct inode *inode, struct file *file)
{
return 0;
}
static int cvi_rtos_cmdqu_release(struct inode *inode, struct file *file)
{
return 0;
}
struct rtos_cmdqu_wait_list_t {
struct list_head list;
cmdqu_t cmdq;
wait_queue_head_t wq;
int condition;
};
static struct rtos_cmdqu_wait_list_t rtos_cmdqu_wait_head;
/* used for callback test*/
static void callback_rtos_irq_handler(int cmd_id, unsigned int ptr, void *dev_id)
{
pr_info("callback_rtos_hander cmd_id = %x ptr=%x dev_id=%p\n", cmd_id, ptr, dev_id);
}
DEFINE_CVI_SPINLOCK(mailbox_lock, SPIN_MBOX);
irqreturn_t rtos_irq_handler(int irq, void *dev_id)
{
char set_val, done_val;
int i;
int flags;
cmdqu_t *cmdq;
struct rtos_cmdqu_wait_list_t *wait_list;
struct list_head *pos;
drv_spin_lock_irqsave(&mailbox_lock, flags);
if (flags == MAILBOX_LOCK_FAILED) {
pr_err("drv_spin_lock_irqsave failed!\n");
//must clear irq?
return IRQ_HANDLED;
}
pr_info("rtos_irq_handler irq=%d\n", irq);
set_val = mbox_reg->cpu_mbox_set[RECEIVE_CPU].cpu_mbox_int_int.mbox_int;
done_val = mbox_done_reg->cpu_mbox_done[RECEIVE_CPU].cpu_mbox_int_int.mbox_int;
pr_debug("set_val=%x\n", set_val);
pr_debug("done_val=%x\n", done_val);
for (i = 0; i < MAILBOX_MAX_NUM && set_val > 0; i++) {
/* valid_val uses unsigned char because of mailbox register table
* ~valid_val will be 0xFF
*/
unsigned char valid_val = set_val & (1 << i);
pr_debug("MAILBOX_MAX_NUM = %d\n", MAILBOX_MAX_NUM);
pr_debug("valid_val = %d set_val=%d i = %d\n", valid_val, set_val, i);
if (valid_val) {
cmdqu_t linux_cmdq;
cmdq = (cmdqu_t *)(mailbox_context) + i;
/* mailbox buffer context is send from rtos, clear mailbox interrupt */
mbox_reg->cpu_mbox_set[RECEIVE_CPU].cpu_mbox_int_clr.mbox_int_clr = valid_val;
// need to disable enable bit
mbox_reg->cpu_mbox_en[RECEIVE_CPU].mbox_info &= ~valid_val;
// copy cmdq context (8 bytes) to buffer ASAP ??
*((unsigned long long *) &linux_cmdq) = *((unsigned long long *)cmdq);
/* need to clear mailbox interrupt before clear mailbox buffer ??*/
*((unsigned long long *) cmdq) = 0;
/* mailbox buffer context is send from rtos */
pr_debug("cmdq=%p\n", cmdq);
pr_debug("cmdq->ip_id =%d\n", linux_cmdq.ip_id);
pr_debug("cmdq->cmd_id =%d\n", linux_cmdq.cmd_id);
pr_debug("cmdq->param_ptr =%x\n", linux_cmdq.param_ptr);
pr_debug("cmdq->block =%d\n", linux_cmdq.block);
pr_debug("cmdq->linux_valid =%d\n", linux_cmdq.resv.valid.linux_valid);
pr_debug("cmdq->rtos_valid =%x", linux_cmdq.resv.valid.rtos_valid);
if (linux_cmdq.resv.valid.rtos_valid == 1 &&
linux_cmdq.block == 1) {
// dewait
list_for_each(pos, &rtos_cmdqu_wait_head.list) {
wait_list = list_entry(pos, struct rtos_cmdqu_wait_list_t, list);
pr_debug("s wait_list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id);
pr_debug("s wait_list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id);
if (wait_list->cmdq.ip_id == linux_cmdq.ip_id &&
wait_list->cmdq.cmd_id == linux_cmdq.cmd_id) {
/* copy data to wait_list and return to user space */
*((unsigned long long *) &wait_list->cmdq) =
*((unsigned long long *) &linux_cmdq);
pr_debug("wait_list->cmdq.ip_id=%d\n",
wait_list->cmdq.ip_id);
pr_debug("wait_list->cmdq.cmd_id=%d\n",
wait_list->cmdq.cmd_id);
pr_debug("wait_list->cmdq.param_ptr=%x\n",
wait_list->cmdq.param_ptr);
wait_list->condition = 1;
wake_up_interruptible(&wait_list->wq);
break;
}
}
} else
pr_err("error ip=%d , cmd=%d\n", linux_cmdq.ip_id, linux_cmdq.cmd_id);
}
}
drv_spin_unlock_irqrestore(&mailbox_lock, flags);
return IRQ_HANDLED;
}
long rtos_cmdqu_init(void)
{
long ret = 0;
int i;
pr_debug("RTOS_CMDQU_INIT\n");
spin_lock_init(&mailbox_queue_lock);
spin_lock_init(&send_queue_lock);
mbox_reg = (struct mailbox_set_register *) reg_base;
mbox_done_reg = (struct mailbox_done_register *) (reg_base + MAILBOX_DONE_OFFSET);
mailbox_context = (unsigned long *) (reg_base + MAILBOX_CONTEXT_OFFSET);//MAILBOX_CONTEXT;
pr_debug("mbox_reg=%p\n", mbox_reg);
pr_debug("mbox_done_reg=%p\n", mbox_done_reg);
pr_debug("mailbox_context=%p\n", mailbox_context);
// init mailbox_context
for ( i=0;i < MAILBOX_MAX_NUM;i++)
mailbox_context[i] = 0;
/* init sqirq parameters*/
return ret;
}
long rtos_cmdqu_deinit(void)
{
long ret = 0;
pr_debug("RTOS_CMDQU_DEINIT\n");
//mailbox deinit
return ret;
}
int rtos_cmdqu_send(cmdqu_t *cmdq)
{
int ret = 0;
int valid;
unsigned long flags;
int mb_flags;
cmdqu_t *linux_cmdqu_t;
pr_debug("RTOS_CMDQU_SEND\n");
pr_debug("ip_id=%d cmd_id=%d param_ptr=%x\n", cmdq->ip_id, cmdq->cmd_id, (unsigned int)cmdq->param_ptr);
spin_lock_irqsave(&mailbox_queue_lock, flags);
// when linux and rtos send command at the same time, it might cause a problem.
// might need to spinlock with rtos, do it later
drv_spin_lock_irqsave(&mailbox_lock, mb_flags);
if (mb_flags == MAILBOX_LOCK_FAILED) {
pr_err("ip_id=%d cmd_id=%d param_ptr=%x\n", cmdq->ip_id, cmdq->cmd_id, (unsigned int)cmdq->param_ptr);
spin_unlock_irqrestore(&mailbox_queue_lock, flags);
return -ENOBUFS;
}
linux_cmdqu_t = (cmdqu_t *) mailbox_context;
pr_debug("mailbox_context = %p\n", mailbox_context);
pr_debug("linux_cmdqu_t = %p\n", linux_cmdqu_t);
pr_debug("cmdq->ip_id = %d\n", cmdq->ip_id);
pr_debug("cmdq->cmd_id = %d\n", cmdq->cmd_id);
pr_debug("cmdq->block = %d\n", cmdq->block);
pr_debug("cmdq->para_ptr = %d\n", cmdq->param_ptr);
for (valid = 0; valid < MAILBOX_MAX_NUM; valid++) {
if (linux_cmdqu_t->resv.valid.linux_valid == 0 && linux_cmdqu_t->resv.valid.rtos_valid == 0) {
// mailbox buffer context is int (4 bytes) access
int *ptr = (int *)linux_cmdqu_t;
linux_cmdqu_t->resv.valid.linux_valid = 1;
*ptr = ((cmdq->ip_id << 0) | (cmdq->cmd_id << 8) | (cmdq->block << 15) |
(linux_cmdqu_t->resv.valid.linux_valid << 16) |
(linux_cmdqu_t->resv.valid.rtos_valid << 24));
linux_cmdqu_t->param_ptr = cmdq->param_ptr;
pr_debug("linux_valid = %d\n", linux_cmdqu_t->resv.valid.linux_valid);
pr_debug("rtos_valid = %d\n", linux_cmdqu_t->resv.valid.rtos_valid);
pr_debug("ip_id = %d\n", linux_cmdqu_t->ip_id);
pr_debug("cmd_id = %d\n", linux_cmdqu_t->cmd_id);
pr_debug("block = %d\n", linux_cmdqu_t->block);
pr_debug("param_ptr = %x\n", linux_cmdqu_t->param_ptr);
pr_debug("*ptr = %x\n", *ptr);
// clear mailbox
mbox_reg->cpu_mbox_set[SEND_TO_CPU].cpu_mbox_int_clr.mbox_int_clr = (1 << valid);
// trigger mailbox valid to rtos
mbox_reg->cpu_mbox_en[SEND_TO_CPU].mbox_info |= (1 << valid);
mbox_reg->mbox_set.mbox_set = (1 << valid);
break;
}
linux_cmdqu_t++;
}
if (valid >= MAILBOX_MAX_NUM) {
pr_err("No valid mailbox is available\n");
drv_spin_unlock_irqrestore(&mailbox_lock, mb_flags);
spin_unlock_irqrestore(&mailbox_queue_lock, flags);
return -ENOBUFS;
}
drv_spin_unlock_irqrestore(&mailbox_lock, mb_flags);
spin_unlock_irqrestore(&mailbox_queue_lock, flags);
return ret;
}
EXPORT_SYMBOL(rtos_cmdqu_send);
int rtos_cmdqu_send_wait(cmdqu_t *cmdq, int wait_cmd_id)
{
unsigned long flags;
struct rtos_cmdqu_wait_list_t *wait_list;
struct list_head *pos;
int delaytime;
int ret = 0;
pr_debug("%s %d\n", __func__, __LINE__);
spin_lock_irqsave(&send_queue_lock, flags);
/* check list with same commands? if yes, ignore it */
list_for_each(pos, &rtos_cmdqu_wait_head.list) {
wait_list = list_entry(pos, struct rtos_cmdqu_wait_list_t, list);
pr_debug("list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id);
pr_debug("list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id);
if (cmdq->ip_id == wait_list->cmdq.ip_id &&
wait_cmd_id == wait_list->cmdq.cmd_id) {
pr_debug("exist : wait_list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id);
pr_debug("exist : wait_list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id);
pr_debug("exist : wait_list->cmdq.param_ptr=%d\n", wait_list->cmdq.param_ptr);
spin_unlock_irqrestore(&send_queue_lock, flags);
return -EEXIST;
}
}
cmdq->block = 1;
wait_list = kzalloc(sizeof(struct rtos_cmdqu_wait_list_t), GFP_KERNEL);
if (!wait_list) {
spin_unlock_irqrestore(&send_queue_lock, flags);
return -ENOMEM;
}
*((unsigned long long *) &wait_list->cmdq) = *((unsigned long long *) cmdq);
wait_list->cmdq.cmd_id = wait_cmd_id;
init_waitqueue_head(&wait_list->wq);
list_add_tail(&wait_list->list, &rtos_cmdqu_wait_head.list);
spin_unlock_irqrestore(&send_queue_lock, flags);
/* check the delay ms
* if mstime is 65535 (-1), it will be blocked infinite (MAX_JIFFY_OFFSET)
*/
delaytime = wait_list->cmdq.resv.mstime;
if (delaytime == 0xFFFF)
delaytime = -1;
ret = rtos_cmdqu_send(cmdq);
ret = wait_event_interruptible_timeout(wait_list->wq,
wait_list->condition != 0, msecs_to_jiffies(delaytime));
spin_lock_irqsave(&send_queue_lock, flags);
list_del_init(&wait_list->list);
spin_unlock_irqrestore(&send_queue_lock, flags);
if (!ret) {
ret = -ETIME;
kfree(wait_list);
pr_err("RTOS_CMDQU_SEND_WAIT timeout\n");
return ret;
}
pr_debug("RTOS_CMDQU_SEND_WAIT done\n");
pr_debug("list wait_list->cmdq.ip_id=%d wait_list->cmdq.cmd_id=%d wait_list->cmdq.param_ptr=%x\n",
wait_list->cmdq.ip_id, wait_list->cmdq.cmd_id, wait_list->cmdq.param_ptr);
/* get context from interrupt and return to userspace */
*((unsigned long long *) cmdq) = *((unsigned long long *) &wait_list->cmdq);
kfree(wait_list);
return 0;
}
EXPORT_SYMBOL(rtos_cmdqu_send_wait);
static long cvi_rtos_cmdqu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct cvi_rtos_cmdqu_device *dev = filp->private_data;
struct rtos_cmdqu_wait_list_t *wait_list;
struct list_head *pos;
long ret = 0;
unsigned long flags;
cmdqu_t cmdq;
pr_debug("%s dev=%p\n", __func__, dev);
switch (cmd) {
case RTOS_CMDQU_SEND:
pr_debug("RTOS_CMDQU_SEND\n");
copy_from_user(&cmdq,
(struct cmdqu_t __user *)arg,
sizeof(struct cmdqu_t));
ret = rtos_cmdqu_send(&cmdq);
break;
case RTOS_CMDQU_SEND_WAKEUP:
pr_debug("RTOS_CMDQU_SEND_WAKEUP\n");
copy_from_user(&cmdq,
(struct cmdqu_t __user *)arg,
sizeof(struct cmdqu_t));
pr_debug("cmdq.ip_id=%d cmdq.cmd_id=%d\n", cmdq.ip_id, cmdq.cmd_id);
spin_lock_irqsave(&send_queue_lock, flags);
list_for_each(pos, &rtos_cmdqu_wait_head.list) {
wait_list = list_entry(pos, struct rtos_cmdqu_wait_list_t, list);
pr_debug("list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id);
pr_debug("list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id);
if (cmdq.ip_id == wait_list->cmdq.ip_id &&
cmdq.cmd_id == wait_list->cmdq.cmd_id &&
wait_list->cmdq.block == 1) {
/* copy data to wait_list and return to user space */
*((unsigned long long *) &wait_list->cmdq) = *((unsigned long long *) &cmdq);
pr_debug("wait_list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id);
pr_debug("wait_list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id);
pr_debug("wait_list->cmdq.param_ptr=%d\n", wait_list->cmdq.param_ptr);
wait_list->condition = 1;
wake_up_interruptible(&wait_list->wq);
break;
}
}
spin_unlock_irqrestore(&send_queue_lock, flags);
pr_debug("RTOS_CMDQU_SEND_WAKEUP done\n");
break;
case RTOS_CMDQU_SEND_WAIT:
pr_debug("RTOS_CMDQU_SEND_WAIT\n");
ret = copy_from_user(&cmdq,
(struct cmdqu_t __user *)arg,
sizeof(struct cmdqu_t));
if (ret) {
return -EFAULT;
}
rtos_cmdqu_send_wait(&cmdq, cmdq.cmd_id);
ret = copy_to_user((struct cmdqu_t __user *)arg,
&cmdq,
sizeof(struct cmdqu_t));
break;
default:
ret = -EFAULT;
break;
}
return ret;
}
static const struct file_operations rtos_cmdqu_fops = {
.owner = THIS_MODULE,
.open = cvi_rtos_cmdqu_open,
.release = cvi_rtos_cmdqu_release,
.unlocked_ioctl = cvi_rtos_cmdqu_ioctl,
};
static int _register_dev(struct cvi_rtos_cmdqu_device *ndev)
{
int rc;
ndev->miscdev.minor = MISC_DYNAMIC_MINOR;
ndev->miscdev.name = RTOS_CMDQU_DEV_NAME;
ndev->miscdev.fops = &rtos_cmdqu_fops;
rc = misc_register(&ndev->miscdev);
if (rc) {
dev_err(ndev->dev, "cvi_rtos_cmdqu: failed to register misc device.\n");
return rc;
}
return 0;
}
static int cvi_rtos_cmdqu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cvi_rtos_cmdqu_device *ndev;
struct resource *res;
int ret = 0;
int err = -1;
pr_info("%s start ---\n", __func__);
pr_info("name=%s\n", pdev->name);
ndev = devm_kzalloc(&pdev->dev, sizeof(*ndev), GFP_KERNEL);
if (!ndev)
return -ENOMEM;
ndev->dev = dev;
ret = _register_dev(ndev);
if (ret < 0) {
pr_err("regsiter cvi_rtos_cmdqu chrdev error\n");
return ret;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
// void __iomem *regs;
// regs = devm_ioremap_resource(dev, res);
// if (IS_ERR(regs)) {
// ret = PTR_ERR(regs);
// goto ERROR_PROVE_DEVICE;
// }
// printk("regs=%x\n", regs);
reg_base = (__u64)devm_ioremap(&pdev->dev, res->start,
res->end - res->start);
pr_info("res-reg: start: 0x%llx, end: 0x%llx, virt-addr(%llx).\n",
res->start, res->end, le64_to_cpu(reg_base));
// spinlock ip offset address (0xc0)
spinlock_base(reg_base + 0xc0);
mailbox_irq = platform_get_irq_byname(pdev, "mailbox");
INIT_LIST_HEAD(&rtos_cmdqu_wait_head.list);
/* init cmdqu*/
rtos_cmdqu_init();
platform_set_drvdata(pdev, ndev);
err = request_irq(mailbox_irq, rtos_irq_handler, 0, "mailbox",
(void *)ndev);
if (err) {
pr_err("fail to register interrupt handler\n");
return -1;
}
pr_info("%s DONE\n", __func__);
return 0;
//ERROR_PROVE_DEVICE:
// return err;
}
static int cvi_rtos_cmdqu_remove(struct platform_device *pdev)
{
struct cvi_rtos_cmdqu_device *ndev = platform_get_drvdata(pdev);
misc_deregister(&ndev->miscdev);
platform_set_drvdata(pdev, NULL);
/* remove irq handler*/
free_irq(mailbox_irq, ndev);
rtos_cmdqu_deinit();
pr_debug("%s DONE\n", __func__);
return 0;
}
static const struct of_device_id cvi_rtos_cmdqu_match[] = {
{ .compatible = "cvitek,rtos_cmdqu" },
{},
};
static struct platform_driver cvi_rtos_cmdqu_driver = {
.probe = cvi_rtos_cmdqu_probe,
.remove = cvi_rtos_cmdqu_remove,
.driver = {
.owner = THIS_MODULE,
.name = RTOS_CMDQU_DEV_NAME,
.of_match_table = cvi_rtos_cmdqu_match,
},
};
struct class *pbase_class;
static void cvi_rtos_cmdqu_exit(void)
{
platform_driver_unregister(&cvi_rtos_cmdqu_driver);
class_destroy(pbase_class);
cvi_spinlock_uninit();
pr_debug("%s DONE\n", __func__);
}
static int cvi_rtos_cmdqu_init(void)
{
int rc;
pr_info("cvi_rtos_cmdqu_init\n");
pbase_class = class_create(THIS_MODULE, RTOS_CMDQU_DEV_NAME);
if (IS_ERR(pbase_class)) {
pr_err("create class failed\n");
rc = PTR_ERR(pbase_class);
goto cleanup;
}
platform_driver_register(&cvi_rtos_cmdqu_driver);
pr_debug("%s done\n", __func__);
cvi_spinlock_init();
return 0;
cleanup:
cvi_rtos_cmdqu_exit();
return rc;
}
MODULE_DESCRIPTION("RTOS_CMD_QUEUE");
MODULE_LICENSE("GPL");
module_init(cvi_rtos_cmdqu_init);
module_exit(cvi_rtos_cmdqu_exit);
#ifndef __RTOS_COMMAND_QUEUE__
#define __RTOS_COMMAND_QUEUE__
#include <linux/kernel.h>
#define NR_SYSTEM_CMD 20
struct valid_t {
unsigned char linux_valid;
unsigned char rtos_valid;
} __attribute__((packed));
typedef union resv_t {
struct valid_t valid;
unsigned short mstime; // 0 : noblock, -1 : block infinite
} resv_t;
typedef struct cmdqu_t cmdqu_t;
/* cmdqu size should be 8 bytes because of mailbox buffer size */
struct cmdqu_t {
unsigned char ip_id;
unsigned char cmd_id : 7;
unsigned char block : 1;
union resv_t resv;
unsigned int param_ptr;
} __attribute__((packed)) __attribute__((aligned(0x8)));
/* keep those commands for ioctl system used */
enum SYSTEM_CMD_TYPE {
CMDQU_SEND = 1,
CMDQU_SEND_WAIT,
CMDQU_SEND_WAKEUP,
CMDQU_SYSTEM_LIMIT = NR_SYSTEM_CMD,
};
enum SYS_CMD_ID {
CMD_TEST_A = 0x10,
CMD_TEST_B,
CMD_TEST_C,
SYS_CMD_INFO_LIMIT,
};
#define RTOS_CMDQU_DEV_NAME "cvi-rtos-cmdqu"
#define RTOS_CMDQU_SEND _IOW('r', CMDQU_SEND, unsigned long)
#define RTOS_CMDQU_SEND_WAIT _IOW('r', CMDQU_SEND_WAIT, unsigned long)
#define RTOS_CMDQU_SEND_WAKEUP _IOW('r', CMDQU_SEND_WAKEUP, unsigned long)
int rtos_cmdqu_send(cmdqu_t *cmdq);
int rtos_cmdqu_send_wait(cmdqu_t *cmdq, int wait_cmd_id);
#endif // end of __RTOS_COMMAND_QUEUE__
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment