1 /* Copyright 2023 The ChromiumOS Authors
2  * SPDX-License-Identifier: Apache-2.0
3  */
4 
5 #include <zephyr/device.h>
6 #include <zephyr/irq.h>
7 #include <zephyr/devicetree.h>
8 #include <soc.h>
9 
10 #define DT_DRV_COMPAT mediatek_mbox
11 
12 /* Mailbox: a simple interrupt source.  Each direction has a 5-bit
13  * command register and will latch an interrupt if any of the bits are
14  * non-zero.  The interrupt bits get cleared/acknowledged by writing
15  * ones to the corresponding bits of "cmd_clear".  There are five
16  * scratch registers for use as message data in each direction.
17  *
18  * The same device is mapped at the same address by the host and DSP,
19  * and the naming is from the perspective of the DSP: the "in"
20  * registers control interrupts on the DSP, the "out" registers are
21  * for transmitting data to the host.
22  *
23  * There is an array of the devices.  Linux's device-tree defines two.
24  * SOF uses those for IPC, but also implements platform_trace_point()
25  * using the third (no linux driver though?).  The upstream headers
26  * list interrupts for FIVE, and indeed those all seem to be present
27  * and working.
28  *
29  * In practice: The first device (mbox0) is for IPC commands in both
30  * directions.  The cmd register is written with a 1 ("IPI_OP_REQ")
31  * and the command is placed in shared DRAM.
32  *
33  * Note that the 8195 has ten "msg" registers.  These only exist on
34  * this version of the hardware, and act as uninspected r/w scratch
35  * registers to both the host and DSP, with no other behavior.  They
36  * aren't used by the Linux kernel at all (which has a single driver
37  * for all these DSPs), so are described here for completeness but
38  * otherwise ignored.  In practice they don't do anything that simple
39  * shared memory can't.
40  */
41 
42 struct mtk_mbox {
43 #ifdef SOC_SERIES_MT8195
44 	uint32_t in_cmd;
45 	uint32_t in_cmd_clr;
46 	uint32_t in_msg[5];
47 	uint32_t out_cmd;
48 	uint32_t out_cmd_clr;
49 	uint32_t out_msg[5];
50 #else
51 	uint32_t in_cmd;
52 	uint32_t out_cmd;
53 	uint32_t in_cmd_clr;
54 	uint32_t out_cmd_clr;
55 #endif
56 };
57 
58 struct mbox_cfg {
59 	volatile struct mtk_mbox *mbox;
60 };
61 
62 struct mbox_data {
63 	mtk_adsp_mbox_handler_t handlers[MTK_ADSP_MBOX_CHANNELS];
64 	void *handler_arg[MTK_ADSP_MBOX_CHANNELS];
65 };
66 
mtk_adsp_mbox_set_handler(const struct device * mbox,uint32_t chan,mtk_adsp_mbox_handler_t handler,void * arg)67 void mtk_adsp_mbox_set_handler(const struct device *mbox, uint32_t chan,
68 			       mtk_adsp_mbox_handler_t handler, void *arg)
69 {
70 	struct mbox_data *data = ((struct device *)mbox)->data;
71 
72 	if (chan < MTK_ADSP_MBOX_CHANNELS) {
73 		data->handlers[chan] = handler;
74 		data->handler_arg[chan] = arg;
75 	}
76 }
77 
mtk_adsp_mbox_signal(const struct device * mbox,uint32_t chan)78 void mtk_adsp_mbox_signal(const struct device *mbox, uint32_t chan)
79 {
80 	const struct mbox_cfg *cfg = ((struct device *)mbox)->config;
81 
82 	if (chan < MTK_ADSP_MBOX_CHANNELS) {
83 		cfg->mbox->out_cmd |= BIT(chan);
84 	}
85 }
86 
87 #define DEF_DEVPTR(N) DEVICE_DT_INST_GET(N),
88 const struct device * const mbox_devs[] = {
89 	DT_INST_FOREACH_STATUS_OKAY(DEF_DEVPTR)
90 };
91 
mbox_handle(const void * arg)92 static void mbox_handle(const void *arg)
93 {
94 	const struct mbox_cfg *cfg = ((struct device *)arg)->config;
95 	struct mbox_data *data = ((struct device *)arg)->data;
96 
97 	for (int i = 0; i < MTK_ADSP_MBOX_CHANNELS; i++) {
98 		if (cfg->mbox->in_cmd & BIT(i)) {
99 			if (data->handlers[i] != NULL) {
100 				data->handlers[i](arg, data->handler_arg[i]);
101 			}
102 		}
103 	}
104 
105 	cfg->mbox->in_cmd_clr = cfg->mbox->in_cmd; /* ACK */
106 }
107 
mbox_isr(const void * arg)108 static void mbox_isr(const void *arg)
109 {
110 	for (int i = 0; i < ARRAY_SIZE(mbox_devs); i++) {
111 		mbox_handle(mbox_devs[i]);
112 	}
113 }
114 
115 #define DEF_IRQ(N)							\
116 	{ IRQ_CONNECT(DT_INST_IRQN(N), 0, mbox_isr, DEVICE_DT_INST_GET(N), 0); \
117 	  irq_enable(DT_INST_IRQN(N)); }
118 
mbox_init(void)119 static int mbox_init(void)
120 {
121 	DT_INST_FOREACH_STATUS_OKAY(DEF_IRQ);
122 	return 0;
123 }
124 
125 SYS_INIT(mbox_init, POST_KERNEL, 0);
126 
127 #define DEF_DEV(N)							\
128 	static struct mbox_data dev_data##N;				\
129 	static const struct mbox_cfg dev_cfg##N =			\
130 		{ .mbox = (void *)DT_INST_REG_ADDR(N), };		\
131 	DEVICE_DT_INST_DEFINE(N, NULL, NULL, &dev_data##N, &dev_cfg##N,	\
132 			      POST_KERNEL, 0, NULL);
133 
134 DT_INST_FOREACH_STATUS_OKAY(DEF_DEV)
135