131 lines
3.7 KiB
C
131 lines
3.7 KiB
C
/* Copyright 2023 The ChromiumOS Authors
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/irq.h>
|
|
#include <zephyr/devicetree.h>
|
|
#include <soc.h>
|
|
|
|
#define DT_DRV_COMPAT mediatek_mbox
|
|
|
|
/* Mailbox: a simple interrupt source. Each direction has a 5-bit
|
|
* command register and will latch an interrupt if any of the bits are
|
|
* non-zero. The interrupt bits get cleared/acknowledged by writing
|
|
* ones to the corresponding bits of "cmd_clear". There are five
|
|
* scratch registers for use as message data in each direction.
|
|
*
|
|
* The same device is mapped at the same address by the host and DSP,
|
|
* and the naming is from the perspective of the DSP: the "in"
|
|
* registers control interrupts on the DSP, the "out" registers are
|
|
* for transmitting data to the host.
|
|
*
|
|
* There is an array of the devices. Linux's device-tree defines two.
|
|
* SOF uses those for IPC, but also implements platform_trace_point()
|
|
* using the third (no linux driver though?). The upstream headers
|
|
* list interrupts for FIVE, and indeed those all seem to be present
|
|
* and working.
|
|
*
|
|
* In practice: The first device (mbox0) is for IPC commands in both
|
|
* directions. The cmd register is written with a 1 ("IPI_OP_REQ")
|
|
* and the command is placed in shared DRAM. The message registers
|
|
* are ignored. The second device (mbox1) is for responses to IPC
|
|
* commands, writing a 2 (IPI_OP_RSP) to the command register. (Yes,
|
|
* this is redundant, and the actual value is ignored by the ISRs on
|
|
* both sides).
|
|
*/
|
|
|
|
struct mtk_mbox {
|
|
uint32_t in_cmd;
|
|
uint32_t in_cmd_clr;
|
|
uint32_t in_msg[5];
|
|
uint32_t out_cmd;
|
|
uint32_t out_cmd_clr;
|
|
uint32_t out_msg[5];
|
|
};
|
|
|
|
struct mbox_cfg {
|
|
volatile struct mtk_mbox *mbox;
|
|
uint32_t irq;
|
|
};
|
|
|
|
struct mbox_data {
|
|
mtk_adsp_mbox_handler_t handlers[MTK_ADSP_MBOX_CHANNELS];
|
|
void *handler_arg[MTK_ADSP_MBOX_CHANNELS];
|
|
};
|
|
|
|
void mtk_adsp_mbox_set_handler(const struct device *mbox, uint32_t chan,
|
|
mtk_adsp_mbox_handler_t handler, void *arg)
|
|
{
|
|
struct mbox_data *data = ((struct device *)mbox)->data;
|
|
|
|
if (chan < MTK_ADSP_MBOX_CHANNELS) {
|
|
data->handlers[chan] = handler;
|
|
data->handler_arg[chan] = arg;
|
|
}
|
|
}
|
|
|
|
void mtk_adsp_mbox_set_msg(const struct device *mbox, uint32_t idx, uint32_t val)
|
|
{
|
|
const struct mbox_cfg *cfg = ((struct device *)mbox)->config;
|
|
|
|
if (idx < MTK_ADSP_MBOX_MSG_WORDS) {
|
|
cfg->mbox->out_msg[idx] = val;
|
|
}
|
|
}
|
|
|
|
uint32_t mtk_adsp_mbox_get_msg(const struct device *mbox, uint32_t idx)
|
|
{
|
|
const struct mbox_cfg *cfg = ((struct device *)mbox)->config;
|
|
|
|
if (idx < MTK_ADSP_MBOX_MSG_WORDS) {
|
|
return cfg->mbox->in_msg[idx];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void mtk_adsp_mbox_signal(const struct device *mbox, uint32_t chan)
|
|
{
|
|
const struct mbox_cfg *cfg = ((struct device *)mbox)->config;
|
|
|
|
if (chan < MTK_ADSP_MBOX_CHANNELS) {
|
|
cfg->mbox->out_cmd |= BIT(chan);
|
|
}
|
|
}
|
|
|
|
static void mbox_isr(const void *arg)
|
|
{
|
|
const struct mbox_cfg *cfg = ((struct device *)arg)->config;
|
|
struct mbox_data *data = ((struct device *)arg)->data;
|
|
|
|
for (int i = 0; i < MTK_ADSP_MBOX_CHANNELS; i++) {
|
|
if (cfg->mbox->in_cmd & BIT(i)) {
|
|
if (data->handlers[i] != NULL) {
|
|
data->handlers[i](arg, data->handler_arg[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
cfg->mbox->in_cmd_clr = cfg->mbox->in_cmd; /* ACK */
|
|
}
|
|
|
|
#define DEF_IRQ(N) \
|
|
IRQ_CONNECT(DT_INST_IRQN(N), 0, mbox_isr, DEVICE_DT_INST_GET(N), 0);
|
|
|
|
static int mbox_init(void)
|
|
{
|
|
DT_INST_FOREACH_STATUS_OKAY(DEF_IRQ);
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(mbox_init, POST_KERNEL, 0);
|
|
|
|
#define DEF_DEV(N) \
|
|
static struct mbox_data dev_data##N; \
|
|
static const struct mbox_cfg dev_cfg##N = \
|
|
{ .irq = DT_INST_IRQN(N), .mbox = (void *)DT_INST_REG_ADDR(N), }; \
|
|
DEVICE_DT_INST_DEFINE(N, NULL, NULL, &dev_data##N, &dev_cfg##N, \
|
|
POST_KERNEL, 0, NULL);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DEF_DEV)
|