blob: 3a23da14557a1d466331b115ff58b87cac92b945 [file] [log] [blame]
/* Copyright (c) 2022 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <cavs_ipc.h>
#include <cavs-ipc-regs.h>
#include <zephyr/spinlock.h>
void cavs_ipc_set_message_handler(const struct device *dev,
cavs_ipc_handler_t fn, void *arg)
{
struct cavs_ipc_data *devdata = dev->data;
k_spinlock_key_t key = k_spin_lock(&devdata->lock);
devdata->handle_message = fn;
devdata->handler_arg = arg;
k_spin_unlock(&devdata->lock, key);
}
void cavs_ipc_set_done_handler(const struct device *dev,
cavs_ipc_done_t fn, void *arg)
{
struct cavs_ipc_data *devdata = dev->data;
k_spinlock_key_t key = k_spin_lock(&devdata->lock);
devdata->done_notify = fn;
devdata->done_arg = arg;
k_spin_unlock(&devdata->lock, key);
}
void z_cavs_ipc_isr(const void *devarg)
{
const struct device *dev = devarg;
const struct cavs_ipc_config *config = dev->config;
struct cavs_ipc_data *devdata = dev->data;
volatile struct cavs_ipc *regs = config->regs;
k_spinlock_key_t key = k_spin_lock(&devdata->lock);
if (regs->tdr & CAVS_IPC_BUSY) {
bool done = true;
if (devdata->handle_message != NULL) {
uint32_t msg = regs->tdr & ~CAVS_IPC_BUSY;
uint32_t ext = regs->tdd;
done = devdata->handle_message(dev, devdata->handler_arg,
msg, ext);
}
regs->tdr = CAVS_IPC_BUSY;
if (done && !IS_ENABLED(CONFIG_SOC_SERIES_INTEL_CAVS_V15)) {
regs->tda = CAVS_IPC_DONE;
}
}
/* Same signal, but on different bits in 1.5 */
bool done = IS_ENABLED(CONFIG_SOC_SERIES_INTEL_CAVS_V15) ?
(regs->idd & CAVS_IPC_IDD15_DONE) : (regs->ida & CAVS_IPC_DONE);
if (done) {
if (devdata->done_notify != NULL) {
devdata->done_notify(dev, devdata->done_arg);
}
k_sem_give(&devdata->sem);
if (IS_ENABLED(CONFIG_SOC_SERIES_INTEL_CAVS_V15)) {
regs->idd = CAVS_IPC_IDD15_DONE;
} else {
regs->ida = CAVS_IPC_DONE;
}
}
k_spin_unlock(&devdata->lock, key);
}
int cavs_ipc_init(const struct device *dev)
{
struct cavs_ipc_data *devdata = dev->data;
const struct cavs_ipc_config *config = dev->config;
memset(devdata, 0, sizeof(*devdata));
/* ACK any latched interrupts (including TDA to clear IDA on
* the other side!), then enable.
*/
config->regs->tdr = CAVS_IPC_BUSY;
if (IS_ENABLED(CONFIG_SOC_SERIES_INTEL_CAVS_V15)) {
config->regs->idd = CAVS_IPC_IDD15_DONE;
} else {
config->regs->ida = CAVS_IPC_DONE;
config->regs->tda = CAVS_IPC_DONE;
}
config->regs->ctl |= (CAVS_IPC_CTL_IDIE | CAVS_IPC_CTL_TBIE);
return 0;
}
void cavs_ipc_complete(const struct device *dev)
{
const struct cavs_ipc_config *config = dev->config;
config->regs->tda = CAVS_IPC_DONE;
}
bool cavs_ipc_is_complete(const struct device *dev)
{
const struct cavs_ipc_config *config = dev->config;
return (config->regs->idr & CAVS_IPC_BUSY) == 0;
}
bool cavs_ipc_send_message(const struct device *dev,
uint32_t data, uint32_t ext_data)
{
const struct cavs_ipc_config *config = dev->config;
struct cavs_ipc_data *devdata = dev->data;
k_spinlock_key_t key = k_spin_lock(&devdata->lock);
if ((config->regs->idr & CAVS_IPC_BUSY) != 0) {
k_spin_unlock(&devdata->lock, key);
return false;
}
k_sem_init(&devdata->sem, 0, 1);
config->regs->idd = ext_data;
config->regs->idr = data | CAVS_IPC_BUSY;
k_spin_unlock(&devdata->lock, key);
return true;
}
bool cavs_ipc_send_message_sync(const struct device *dev,
uint32_t data, uint32_t ext_data,
k_timeout_t timeout)
{
struct cavs_ipc_data *devdata = dev->data;
bool ret = cavs_ipc_send_message(dev, data, ext_data);
if (ret) {
k_sem_take(&devdata->sem, timeout);
}
return ret;
}
#if DT_NODE_EXISTS(CAVS_HOST_DTNODE)
static int dt_init(const struct device *dev)
{
IRQ_CONNECT(DT_IRQN(CAVS_HOST_DTNODE), 0, z_cavs_ipc_isr, CAVS_HOST_DEV, 0);
irq_enable(DT_IRQN(CAVS_HOST_DTNODE));
return cavs_ipc_init(dev);
}
static const struct cavs_ipc_config ipc_host_config = {
.regs = (void *)DT_REG_ADDR(CAVS_HOST_DTNODE),
};
static struct cavs_ipc_data ipc_host_data;
DEVICE_DT_DEFINE(CAVS_HOST_DTNODE, dt_init, NULL, &ipc_host_data, &ipc_host_config,
PRE_KERNEL_2, 0, NULL);
#endif