282 lines
5.9 KiB
C
Executable File
282 lines
5.9 KiB
C
Executable File
/*
|
|
* this is only for , adopt to other must change the m and n.
|
|
*/
|
|
#include "../platform-msgbox.h"
|
|
#include "hal_msgbox.h"
|
|
#include "hal_interrupt.h"
|
|
#ifdef CONFIG_ARCH_SUN20IW2
|
|
#ifdef CONFIG_ARCH_XTENXA /* only for dsp */
|
|
#include "aw_io.h"
|
|
#endif
|
|
#endif
|
|
#include "hal_clk.h"
|
|
#include "hal_reset.h"
|
|
#include "hal_mutex.h"
|
|
#include "hal_log.h"
|
|
#ifdef CONFIG_STANDBY
|
|
#include <standby/standby.h>
|
|
#endif
|
|
|
|
static struct msg_endpoint *it_edp = NULL;
|
|
static hal_mutex_t it_edp_mutex;
|
|
|
|
#define MSGBOX_MAX_QUEUE 8
|
|
|
|
static inline int calculte_n(int local, int remote)
|
|
{
|
|
#if defined(CONFIG_ARCH_SUN8IW20)|| defined(CONFIG_SOC_SUN20IW1) \
|
|
|| defined(CONFIG_SOC_SUN20IW3)
|
|
/*
|
|
* | remote core (send) |
|
|
* | ARM:0 | DSP:1 | RV:2 |
|
|
* local | ARM:0 | / | 0 | 1 |
|
|
* core | DSP:1 | 0 | / | 1 |
|
|
* (recv) | RV :2 | 0 | 1 | / |
|
|
*/
|
|
if (remote < local)
|
|
return remote;
|
|
else
|
|
return remote - 1;
|
|
#elif defined(CONFIG_ARCH_SUN20IW2)
|
|
/*
|
|
* | remote core (send) |
|
|
* | ARM:0 | DSP:1 | RV:2 |
|
|
* local | ARM:0 | / | 1 | 0 |
|
|
* core | DSP:1 | 0 | / | 1 |
|
|
* (recv) | RV :2 | 0 | 1 | / |
|
|
*/
|
|
if (remote < 2)
|
|
return remote;
|
|
else
|
|
return local;
|
|
#endif
|
|
}
|
|
|
|
static void irq_msgbox_channel_handler(struct msg_endpoint *medp)
|
|
{
|
|
void *msg_sts, *msg_reg, *msg_irq_s;
|
|
u32 data;
|
|
|
|
msg_sts = (void *)MSGBOX_MSG_STA_REG(
|
|
medp->local_amp, calculte_n(medp->local_amp, medp->remote_amp),
|
|
medp->read_ch);
|
|
msg_reg = (void *)MSGBOX_MSG_REG(
|
|
medp->local_amp, calculte_n(medp->local_amp, medp->remote_amp),
|
|
medp->read_ch);
|
|
msg_irq_s = (void *)MSGBOX_RD_IRQ_STA_REG(
|
|
medp->local_amp, calculte_n(medp->local_amp, medp->remote_amp));
|
|
|
|
while (readl(msg_sts)) {
|
|
data = readl(msg_reg);
|
|
if (medp->rec)
|
|
medp->rec(data, medp->private);
|
|
}
|
|
|
|
writel(1 << (medp->read_ch * 2), msg_irq_s);
|
|
}
|
|
|
|
static hal_irqreturn_t irq_msgbox_handler(void *p)
|
|
{
|
|
struct msg_endpoint *t;
|
|
|
|
/* shouled not use mutex in interrupt contex */
|
|
for (t = it_edp; t != NULL; t = t->next) {
|
|
irq_msgbox_channel_handler(t);
|
|
}
|
|
|
|
return HAL_IRQ_OK;
|
|
}
|
|
|
|
#ifdef CONFIG_STANDBY
|
|
static void msgbox_enable_rec_int(struct msg_endpoint *medp);
|
|
|
|
static int msgbox_suspend(void *data)
|
|
{
|
|
hal_log_debug("msgbox suspend\r\n");
|
|
return 0;
|
|
}
|
|
|
|
static int msgbox_resume(void *data)
|
|
{
|
|
struct reset_control *rst;
|
|
hal_clk_t clk;
|
|
|
|
hal_log_debug("msgbox resume\r\n");
|
|
rst = hal_reset_control_get(HAL_SUNXI_RESET, RST_MSGBOX);
|
|
hal_reset_control_deassert(rst);
|
|
hal_reset_control_put(rst);
|
|
|
|
clk = hal_clock_get(HAL_SUNXI_CCU, CLK_MSGBOX);
|
|
hal_clock_enable(clk);
|
|
hal_clock_put(clk);
|
|
|
|
hal_mutex_lock(it_edp_mutex);
|
|
/* add to global list */
|
|
if (it_edp != NULL) {
|
|
struct msg_endpoint *t = it_edp;
|
|
while (t) {
|
|
msgbox_enable_rec_int(t);
|
|
t = t->next;
|
|
}
|
|
}
|
|
hal_mutex_unlock(it_edp_mutex);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
uint32_t hal_msgbox_init(void)
|
|
{
|
|
struct reset_control *rst;
|
|
hal_clk_t clk;
|
|
|
|
it_edp_mutex = hal_mutex_create();
|
|
// TODO: maybe not always NULL when failed in different OS?
|
|
if (it_edp_mutex == NULL) {
|
|
hal_log_err("%s(%d): Failed to create it_edp_mutex\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
rst = hal_reset_control_get(HAL_SUNXI_RESET, RST_MSGBOX);
|
|
hal_reset_control_assert(rst);
|
|
hal_reset_control_deassert(rst);
|
|
hal_reset_control_put(rst);
|
|
|
|
clk = hal_clock_get(HAL_SUNXI_CCU, CLK_MSGBOX);
|
|
#ifdef CONFIG_DRIVERS_SUNXI_CLK
|
|
hal_clock_disable(clk);
|
|
#endif
|
|
hal_clock_enable(clk);
|
|
hal_clock_put(clk);
|
|
|
|
hal_request_irq(IRQ_MSGBOX, irq_msgbox_handler, "msgbox", it_edp);
|
|
hal_enable_irq(IRQ_MSGBOX);
|
|
|
|
#ifdef CONFIG_STANDBY
|
|
register_pm_dev_notify(msgbox_suspend, msgbox_resume, NULL);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void msgbox_enable_rec_int(struct msg_endpoint *medp)
|
|
{
|
|
void *msg_irq_e;
|
|
|
|
msg_irq_e = (void *)MSGBOX_RD_IRQ_EN_REG(
|
|
medp->local_amp, calculte_n(medp->local_amp, medp->remote_amp));
|
|
|
|
writel(readl(msg_irq_e) | (1 << (medp->read_ch * 2)), msg_irq_e);
|
|
|
|
}
|
|
|
|
static void msgbox_disable_rec_int(struct msg_endpoint *medp)
|
|
{
|
|
void *msg_irq_e;
|
|
|
|
msg_irq_e = (void *)MSGBOX_RD_IRQ_EN_REG(
|
|
medp->local_amp, calculte_n(medp->local_amp, medp->remote_amp));
|
|
|
|
writel(readl(msg_irq_e) & ~(1 << (medp->read_ch * 2)), msg_irq_e);
|
|
|
|
}
|
|
|
|
uint32_t hal_msgbox_alloc_channel(struct msg_endpoint *edp, int32_t remote,
|
|
int32_t read, int32_t write)
|
|
{
|
|
edp->local_amp = THIS_MSGBOX_USE;
|
|
edp->remote_amp = remote;
|
|
edp->read_ch = read;
|
|
edp->write_ch = write;
|
|
|
|
hal_mutex_lock(it_edp_mutex);
|
|
/* add to global list */
|
|
if (it_edp == NULL) {
|
|
it_edp = edp;
|
|
} else {
|
|
struct msg_endpoint *t = it_edp;
|
|
while (t) {
|
|
if (t->next == NULL) {
|
|
t->next = edp;
|
|
break;
|
|
}
|
|
t = t->next;
|
|
}
|
|
}
|
|
edp->next = NULL;
|
|
hal_mutex_unlock(it_edp_mutex);
|
|
|
|
if (read >= 0)
|
|
msgbox_enable_rec_int(edp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void hal_msgbox_free_channel(struct msg_endpoint *edp)
|
|
{
|
|
struct msg_endpoint *t = it_edp;
|
|
|
|
if (t == edp) {
|
|
it_edp = t->next;
|
|
} else {
|
|
while (t) {
|
|
if (t->next == edp) {
|
|
t->next = t->next->next;
|
|
break;
|
|
}
|
|
t = t->next;
|
|
}
|
|
}
|
|
edp->next = NULL;
|
|
|
|
int rev_int = 0;
|
|
t = it_edp;
|
|
while (t) {
|
|
if (t->read_ch >= 0) {
|
|
rev_int = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rev_int == 0)
|
|
msgbox_disable_rec_int(edp);
|
|
|
|
return ;
|
|
}
|
|
|
|
static void msgbox_channel_send_data(struct msg_endpoint *medp, u32 data)
|
|
{
|
|
void *msg_sts, *msg_reg;
|
|
|
|
msg_sts = (void *)MSGBOX_MSG_STA_REG(
|
|
medp->remote_amp, calculte_n(medp->remote_amp, medp->local_amp),
|
|
medp->write_ch);
|
|
msg_reg = (void *)MSGBOX_MSG_REG(
|
|
medp->remote_amp, calculte_n(medp->remote_amp, medp->local_amp),
|
|
medp->write_ch);
|
|
|
|
while (readl(msg_sts) == MSGBOX_MAX_QUEUE);
|
|
writel(data, msg_reg);
|
|
}
|
|
|
|
u32 hal_msgbox_channel_send(struct msg_endpoint *medp, uint8_t *bf,
|
|
uint32_t len)
|
|
{
|
|
u32 data, i;
|
|
|
|
data = 0;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (!(i % 4))
|
|
data = 0;
|
|
|
|
data |= *bf++ << ((i % 4) << 3);
|
|
|
|
if ((i % 4) == 3 || i == len - 1) {
|
|
msgbox_channel_send_data(medp, data);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|