1b87f920bSLizhi Hou // SPDX-License-Identifier: GPL-2.0
2b87f920bSLizhi Hou /*
3b87f920bSLizhi Hou * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
4b87f920bSLizhi Hou */
5b87f920bSLizhi Hou
6b87f920bSLizhi Hou #include <drm/drm_device.h>
7b87f920bSLizhi Hou #include <drm/drm_managed.h>
8b87f920bSLizhi Hou #include <linux/bitfield.h>
9d4089168SMike Lothian #include <linux/interrupt.h>
10b87f920bSLizhi Hou #include <linux/iopoll.h>
11*838c17fdSSu Hui #include <linux/slab.h>
12a37d7847SLizhi Hou #include <linux/xarray.h>
13b87f920bSLizhi Hou
14b87f920bSLizhi Hou #define CREATE_TRACE_POINTS
15b87f920bSLizhi Hou #include <trace/events/amdxdna.h>
16b87f920bSLizhi Hou
17b87f920bSLizhi Hou #include "amdxdna_mailbox.h"
18b87f920bSLizhi Hou
19b87f920bSLizhi Hou #define MB_ERR(chann, fmt, args...) \
20b87f920bSLizhi Hou ({ \
21b87f920bSLizhi Hou typeof(chann) _chann = chann; \
22b87f920bSLizhi Hou dev_err((_chann)->mb->dev, "xdna_mailbox.%d: "fmt, \
23b87f920bSLizhi Hou (_chann)->msix_irq, ##args); \
24b87f920bSLizhi Hou })
25b87f920bSLizhi Hou #define MB_DBG(chann, fmt, args...) \
26b87f920bSLizhi Hou ({ \
27b87f920bSLizhi Hou typeof(chann) _chann = chann; \
28b87f920bSLizhi Hou dev_dbg((_chann)->mb->dev, "xdna_mailbox.%d: "fmt, \
29b87f920bSLizhi Hou (_chann)->msix_irq, ##args); \
30b87f920bSLizhi Hou })
31b87f920bSLizhi Hou #define MB_WARN_ONCE(chann, fmt, args...) \
32b87f920bSLizhi Hou ({ \
33b87f920bSLizhi Hou typeof(chann) _chann = chann; \
34b87f920bSLizhi Hou dev_warn_once((_chann)->mb->dev, "xdna_mailbox.%d: "fmt, \
35b87f920bSLizhi Hou (_chann)->msix_irq, ##args); \
36b87f920bSLizhi Hou })
37b87f920bSLizhi Hou
38b87f920bSLizhi Hou #define MAGIC_VAL 0x1D000000U
39b87f920bSLizhi Hou #define MAGIC_VAL_MASK 0xFF000000
40b87f920bSLizhi Hou #define MAX_MSG_ID_ENTRIES 256
41b87f920bSLizhi Hou #define MSG_RX_TIMER 200 /* milliseconds */
42b87f920bSLizhi Hou #define MAILBOX_NAME "xdna_mailbox"
43b87f920bSLizhi Hou
44b87f920bSLizhi Hou enum channel_res_type {
45b87f920bSLizhi Hou CHAN_RES_X2I,
46b87f920bSLizhi Hou CHAN_RES_I2X,
47b87f920bSLizhi Hou CHAN_RES_NUM
48b87f920bSLizhi Hou };
49b87f920bSLizhi Hou
50b87f920bSLizhi Hou struct mailbox {
51b87f920bSLizhi Hou struct device *dev;
52b87f920bSLizhi Hou struct xdna_mailbox_res res;
53b87f920bSLizhi Hou };
54b87f920bSLizhi Hou
55b87f920bSLizhi Hou struct mailbox_channel {
56b87f920bSLizhi Hou struct mailbox *mb;
57b87f920bSLizhi Hou struct xdna_mailbox_chann_res res[CHAN_RES_NUM];
58b87f920bSLizhi Hou int msix_irq;
59b87f920bSLizhi Hou u32 iohub_int_addr;
60a37d7847SLizhi Hou struct xarray chan_xa;
61a37d7847SLizhi Hou u32 next_msgid;
62b87f920bSLizhi Hou u32 x2i_tail;
63b87f920bSLizhi Hou
64b87f920bSLizhi Hou /* Received msg related fields */
65b87f920bSLizhi Hou struct workqueue_struct *work_q;
66b87f920bSLizhi Hou struct work_struct rx_work;
67b87f920bSLizhi Hou u32 i2x_head;
68b87f920bSLizhi Hou bool bad_state;
69b87f920bSLizhi Hou };
70b87f920bSLizhi Hou
71b87f920bSLizhi Hou #define MSG_BODY_SZ GENMASK(10, 0)
72b87f920bSLizhi Hou #define MSG_PROTO_VER GENMASK(23, 16)
73b87f920bSLizhi Hou struct xdna_msg_header {
74b87f920bSLizhi Hou __u32 total_size;
75b87f920bSLizhi Hou __u32 sz_ver;
76b87f920bSLizhi Hou __u32 id;
77b87f920bSLizhi Hou __u32 opcode;
78b87f920bSLizhi Hou } __packed;
79b87f920bSLizhi Hou
80b87f920bSLizhi Hou static_assert(sizeof(struct xdna_msg_header) == 16);
81b87f920bSLizhi Hou
82b87f920bSLizhi Hou struct mailbox_pkg {
83b87f920bSLizhi Hou struct xdna_msg_header header;
84b87f920bSLizhi Hou __u32 payload[];
85b87f920bSLizhi Hou };
86b87f920bSLizhi Hou
87b87f920bSLizhi Hou /* The protocol version. */
88b87f920bSLizhi Hou #define MSG_PROTOCOL_VERSION 0x1
89b87f920bSLizhi Hou /* The tombstone value. */
90b87f920bSLizhi Hou #define TOMBSTONE 0xDEADFACE
91b87f920bSLizhi Hou
92b87f920bSLizhi Hou struct mailbox_msg {
93b87f920bSLizhi Hou void *handle;
94b87f920bSLizhi Hou int (*notify_cb)(void *handle, const u32 *data, size_t size);
95b87f920bSLizhi Hou size_t pkg_size; /* package size in bytes */
96b87f920bSLizhi Hou struct mailbox_pkg pkg;
97b87f920bSLizhi Hou };
98b87f920bSLizhi Hou
mailbox_reg_write(struct mailbox_channel * mb_chann,u32 mbox_reg,u32 data)99b87f920bSLizhi Hou static void mailbox_reg_write(struct mailbox_channel *mb_chann, u32 mbox_reg, u32 data)
100b87f920bSLizhi Hou {
101b87f920bSLizhi Hou struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
1023c8cfec3SLizhi Hou void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
103b87f920bSLizhi Hou
1043c8cfec3SLizhi Hou writel(data, ringbuf_addr);
105b87f920bSLizhi Hou }
106b87f920bSLizhi Hou
mailbox_reg_read(struct mailbox_channel * mb_chann,u32 mbox_reg)107b87f920bSLizhi Hou static u32 mailbox_reg_read(struct mailbox_channel *mb_chann, u32 mbox_reg)
108b87f920bSLizhi Hou {
109b87f920bSLizhi Hou struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
1103c8cfec3SLizhi Hou void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
111b87f920bSLizhi Hou
1123c8cfec3SLizhi Hou return readl(ringbuf_addr);
113b87f920bSLizhi Hou }
114b87f920bSLizhi Hou
mailbox_reg_read_non_zero(struct mailbox_channel * mb_chann,u32 mbox_reg,u32 * val)115b87f920bSLizhi Hou static int mailbox_reg_read_non_zero(struct mailbox_channel *mb_chann, u32 mbox_reg, u32 *val)
116b87f920bSLizhi Hou {
117b87f920bSLizhi Hou struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
1183c8cfec3SLizhi Hou void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
119b87f920bSLizhi Hou int ret, value;
120b87f920bSLizhi Hou
121b87f920bSLizhi Hou /* Poll till value is not zero */
1223c8cfec3SLizhi Hou ret = readx_poll_timeout(readl, ringbuf_addr, value,
123b87f920bSLizhi Hou value, 1 /* us */, 100);
124b87f920bSLizhi Hou if (ret < 0)
125b87f920bSLizhi Hou return ret;
126b87f920bSLizhi Hou
127b87f920bSLizhi Hou *val = value;
128b87f920bSLizhi Hou return 0;
129b87f920bSLizhi Hou }
130b87f920bSLizhi Hou
131b87f920bSLizhi Hou static inline void
mailbox_set_headptr(struct mailbox_channel * mb_chann,u32 headptr_val)132b87f920bSLizhi Hou mailbox_set_headptr(struct mailbox_channel *mb_chann, u32 headptr_val)
133b87f920bSLizhi Hou {
134b87f920bSLizhi Hou mailbox_reg_write(mb_chann, mb_chann->res[CHAN_RES_I2X].mb_head_ptr_reg, headptr_val);
135b87f920bSLizhi Hou mb_chann->i2x_head = headptr_val;
136b87f920bSLizhi Hou }
137b87f920bSLizhi Hou
138b87f920bSLizhi Hou static inline void
mailbox_set_tailptr(struct mailbox_channel * mb_chann,u32 tailptr_val)139b87f920bSLizhi Hou mailbox_set_tailptr(struct mailbox_channel *mb_chann, u32 tailptr_val)
140b87f920bSLizhi Hou {
141b87f920bSLizhi Hou mailbox_reg_write(mb_chann, mb_chann->res[CHAN_RES_X2I].mb_tail_ptr_reg, tailptr_val);
142b87f920bSLizhi Hou mb_chann->x2i_tail = tailptr_val;
143b87f920bSLizhi Hou }
144b87f920bSLizhi Hou
145b87f920bSLizhi Hou static inline u32
mailbox_get_headptr(struct mailbox_channel * mb_chann,enum channel_res_type type)146b87f920bSLizhi Hou mailbox_get_headptr(struct mailbox_channel *mb_chann, enum channel_res_type type)
147b87f920bSLizhi Hou {
148b87f920bSLizhi Hou return mailbox_reg_read(mb_chann, mb_chann->res[type].mb_head_ptr_reg);
149b87f920bSLizhi Hou }
150b87f920bSLizhi Hou
151b87f920bSLizhi Hou static inline u32
mailbox_get_tailptr(struct mailbox_channel * mb_chann,enum channel_res_type type)152b87f920bSLizhi Hou mailbox_get_tailptr(struct mailbox_channel *mb_chann, enum channel_res_type type)
153b87f920bSLizhi Hou {
154b87f920bSLizhi Hou return mailbox_reg_read(mb_chann, mb_chann->res[type].mb_tail_ptr_reg);
155b87f920bSLizhi Hou }
156b87f920bSLizhi Hou
157b87f920bSLizhi Hou static inline u32
mailbox_get_ringbuf_size(struct mailbox_channel * mb_chann,enum channel_res_type type)158b87f920bSLizhi Hou mailbox_get_ringbuf_size(struct mailbox_channel *mb_chann, enum channel_res_type type)
159b87f920bSLizhi Hou {
160b87f920bSLizhi Hou return mb_chann->res[type].rb_size;
161b87f920bSLizhi Hou }
162b87f920bSLizhi Hou
mailbox_validate_msgid(int msg_id)163b87f920bSLizhi Hou static inline int mailbox_validate_msgid(int msg_id)
164b87f920bSLizhi Hou {
165b87f920bSLizhi Hou return (msg_id & MAGIC_VAL_MASK) == MAGIC_VAL;
166b87f920bSLizhi Hou }
167b87f920bSLizhi Hou
mailbox_acquire_msgid(struct mailbox_channel * mb_chann,struct mailbox_msg * mb_msg)168b87f920bSLizhi Hou static int mailbox_acquire_msgid(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
169b87f920bSLizhi Hou {
170a37d7847SLizhi Hou u32 msg_id;
171a37d7847SLizhi Hou int ret;
172b87f920bSLizhi Hou
173a37d7847SLizhi Hou ret = xa_alloc_cyclic_irq(&mb_chann->chan_xa, &msg_id, mb_msg,
174a37d7847SLizhi Hou XA_LIMIT(0, MAX_MSG_ID_ENTRIES - 1),
175a37d7847SLizhi Hou &mb_chann->next_msgid, GFP_NOWAIT);
176a37d7847SLizhi Hou if (ret < 0)
177a37d7847SLizhi Hou return ret;
178b87f920bSLizhi Hou
179b87f920bSLizhi Hou /*
180a37d7847SLizhi Hou * Add MAGIC_VAL to the higher bits.
181b87f920bSLizhi Hou */
182b87f920bSLizhi Hou msg_id |= MAGIC_VAL;
183b87f920bSLizhi Hou return msg_id;
184b87f920bSLizhi Hou }
185b87f920bSLizhi Hou
mailbox_release_msgid(struct mailbox_channel * mb_chann,int msg_id)186b87f920bSLizhi Hou static void mailbox_release_msgid(struct mailbox_channel *mb_chann, int msg_id)
187b87f920bSLizhi Hou {
188b87f920bSLizhi Hou msg_id &= ~MAGIC_VAL_MASK;
189a37d7847SLizhi Hou xa_erase_irq(&mb_chann->chan_xa, msg_id);
190b87f920bSLizhi Hou }
191b87f920bSLizhi Hou
mailbox_release_msg(struct mailbox_channel * mb_chann,struct mailbox_msg * mb_msg)192a37d7847SLizhi Hou static void mailbox_release_msg(struct mailbox_channel *mb_chann,
193a37d7847SLizhi Hou struct mailbox_msg *mb_msg)
194b87f920bSLizhi Hou {
195b87f920bSLizhi Hou MB_DBG(mb_chann, "msg_id 0x%x msg opcode 0x%x",
196b87f920bSLizhi Hou mb_msg->pkg.header.id, mb_msg->pkg.header.opcode);
197b87f920bSLizhi Hou mb_msg->notify_cb(mb_msg->handle, NULL, 0);
198b87f920bSLizhi Hou kfree(mb_msg);
199b87f920bSLizhi Hou }
200b87f920bSLizhi Hou
201b87f920bSLizhi Hou static int
mailbox_send_msg(struct mailbox_channel * mb_chann,struct mailbox_msg * mb_msg)202b87f920bSLizhi Hou mailbox_send_msg(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
203b87f920bSLizhi Hou {
2043c8cfec3SLizhi Hou void __iomem *write_addr;
205b87f920bSLizhi Hou u32 ringbuf_size;
206b87f920bSLizhi Hou u32 head, tail;
207b87f920bSLizhi Hou u32 start_addr;
208b87f920bSLizhi Hou u32 tmp_tail;
209b87f920bSLizhi Hou
210b87f920bSLizhi Hou head = mailbox_get_headptr(mb_chann, CHAN_RES_X2I);
211b87f920bSLizhi Hou tail = mb_chann->x2i_tail;
212b87f920bSLizhi Hou ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_X2I);
213b87f920bSLizhi Hou start_addr = mb_chann->res[CHAN_RES_X2I].rb_start_addr;
214b87f920bSLizhi Hou tmp_tail = tail + mb_msg->pkg_size;
215b87f920bSLizhi Hou
216b87f920bSLizhi Hou if (tail < head && tmp_tail >= head)
217b87f920bSLizhi Hou goto no_space;
218b87f920bSLizhi Hou
219b87f920bSLizhi Hou if (tail >= head && (tmp_tail > ringbuf_size - sizeof(u32) &&
220b87f920bSLizhi Hou mb_msg->pkg_size >= head))
221b87f920bSLizhi Hou goto no_space;
222b87f920bSLizhi Hou
223b87f920bSLizhi Hou if (tail >= head && tmp_tail > ringbuf_size - sizeof(u32)) {
224b87f920bSLizhi Hou write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
2253c8cfec3SLizhi Hou writel(TOMBSTONE, write_addr);
226b87f920bSLizhi Hou
227b87f920bSLizhi Hou /* tombstone is set. Write from the start of the ringbuf */
228b87f920bSLizhi Hou tail = 0;
229b87f920bSLizhi Hou }
230b87f920bSLizhi Hou
231b87f920bSLizhi Hou write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
2323c8cfec3SLizhi Hou memcpy_toio(write_addr, &mb_msg->pkg, mb_msg->pkg_size);
233b87f920bSLizhi Hou mailbox_set_tailptr(mb_chann, tail + mb_msg->pkg_size);
234b87f920bSLizhi Hou
235b87f920bSLizhi Hou trace_mbox_set_tail(MAILBOX_NAME, mb_chann->msix_irq,
236b87f920bSLizhi Hou mb_msg->pkg.header.opcode,
237b87f920bSLizhi Hou mb_msg->pkg.header.id);
238b87f920bSLizhi Hou
239b87f920bSLizhi Hou return 0;
240b87f920bSLizhi Hou
241b87f920bSLizhi Hou no_space:
242b87f920bSLizhi Hou return -ENOSPC;
243b87f920bSLizhi Hou }
244b87f920bSLizhi Hou
245b87f920bSLizhi Hou static int
mailbox_get_resp(struct mailbox_channel * mb_chann,struct xdna_msg_header * header,void * data)246b87f920bSLizhi Hou mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *header,
247b87f920bSLizhi Hou void *data)
248b87f920bSLizhi Hou {
249b87f920bSLizhi Hou struct mailbox_msg *mb_msg;
250b87f920bSLizhi Hou int msg_id;
251b87f920bSLizhi Hou int ret;
252b87f920bSLizhi Hou
253b87f920bSLizhi Hou msg_id = header->id;
254b87f920bSLizhi Hou if (!mailbox_validate_msgid(msg_id)) {
255b87f920bSLizhi Hou MB_ERR(mb_chann, "Bad message ID 0x%x", msg_id);
256b87f920bSLizhi Hou return -EINVAL;
257b87f920bSLizhi Hou }
258b87f920bSLizhi Hou
259b87f920bSLizhi Hou msg_id &= ~MAGIC_VAL_MASK;
260a37d7847SLizhi Hou mb_msg = xa_erase_irq(&mb_chann->chan_xa, msg_id);
261b87f920bSLizhi Hou if (!mb_msg) {
262b87f920bSLizhi Hou MB_ERR(mb_chann, "Cannot find msg 0x%x", msg_id);
263b87f920bSLizhi Hou return -EINVAL;
264b87f920bSLizhi Hou }
265b87f920bSLizhi Hou
266b87f920bSLizhi Hou MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x",
267b87f920bSLizhi Hou header->opcode, header->total_size, header->id);
268b87f920bSLizhi Hou ret = mb_msg->notify_cb(mb_msg->handle, data, header->total_size);
269b87f920bSLizhi Hou if (unlikely(ret))
270b87f920bSLizhi Hou MB_ERR(mb_chann, "Message callback ret %d", ret);
271b87f920bSLizhi Hou
272b87f920bSLizhi Hou kfree(mb_msg);
273b87f920bSLizhi Hou return ret;
274b87f920bSLizhi Hou }
275b87f920bSLizhi Hou
mailbox_get_msg(struct mailbox_channel * mb_chann)276b87f920bSLizhi Hou static int mailbox_get_msg(struct mailbox_channel *mb_chann)
277b87f920bSLizhi Hou {
278b87f920bSLizhi Hou struct xdna_msg_header header;
2793c8cfec3SLizhi Hou void __iomem *read_addr;
280b87f920bSLizhi Hou u32 msg_size, rest;
281b87f920bSLizhi Hou u32 ringbuf_size;
282b87f920bSLizhi Hou u32 head, tail;
283b87f920bSLizhi Hou u32 start_addr;
284b87f920bSLizhi Hou int ret;
285b87f920bSLizhi Hou
286b87f920bSLizhi Hou if (mailbox_reg_read_non_zero(mb_chann, mb_chann->res[CHAN_RES_I2X].mb_tail_ptr_reg, &tail))
287b87f920bSLizhi Hou return -EINVAL;
288b87f920bSLizhi Hou head = mb_chann->i2x_head;
289b87f920bSLizhi Hou ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_I2X);
290b87f920bSLizhi Hou start_addr = mb_chann->res[CHAN_RES_I2X].rb_start_addr;
291b87f920bSLizhi Hou
292b87f920bSLizhi Hou if (unlikely(tail > ringbuf_size || !IS_ALIGNED(tail, 4))) {
293b87f920bSLizhi Hou MB_WARN_ONCE(mb_chann, "Invalid tail 0x%x", tail);
294b87f920bSLizhi Hou return -EINVAL;
295b87f920bSLizhi Hou }
296b87f920bSLizhi Hou
297b87f920bSLizhi Hou /* ringbuf empty */
298b87f920bSLizhi Hou if (head == tail)
299b87f920bSLizhi Hou return -ENOENT;
300b87f920bSLizhi Hou
301b87f920bSLizhi Hou if (head == ringbuf_size)
302b87f920bSLizhi Hou head = 0;
303b87f920bSLizhi Hou
304b87f920bSLizhi Hou /* Peek size of the message or TOMBSTONE */
305b87f920bSLizhi Hou read_addr = mb_chann->mb->res.ringbuf_base + start_addr + head;
3063c8cfec3SLizhi Hou header.total_size = readl(read_addr);
307b87f920bSLizhi Hou /* size is TOMBSTONE, set next read from 0 */
308b87f920bSLizhi Hou if (header.total_size == TOMBSTONE) {
309b87f920bSLizhi Hou if (head < tail) {
310b87f920bSLizhi Hou MB_WARN_ONCE(mb_chann, "Tombstone, head 0x%x tail 0x%x",
311b87f920bSLizhi Hou head, tail);
312b87f920bSLizhi Hou return -EINVAL;
313b87f920bSLizhi Hou }
314b87f920bSLizhi Hou mailbox_set_headptr(mb_chann, 0);
315b87f920bSLizhi Hou return 0;
316b87f920bSLizhi Hou }
317b87f920bSLizhi Hou
318b87f920bSLizhi Hou if (unlikely(!header.total_size || !IS_ALIGNED(header.total_size, 4))) {
319b87f920bSLizhi Hou MB_WARN_ONCE(mb_chann, "Invalid total size 0x%x", header.total_size);
320b87f920bSLizhi Hou return -EINVAL;
321b87f920bSLizhi Hou }
322b87f920bSLizhi Hou msg_size = sizeof(header) + header.total_size;
323b87f920bSLizhi Hou
324b87f920bSLizhi Hou if (msg_size > ringbuf_size - head || msg_size > tail - head) {
325b87f920bSLizhi Hou MB_WARN_ONCE(mb_chann, "Invalid message size %d, tail %d, head %d",
326b87f920bSLizhi Hou msg_size, tail, head);
327b87f920bSLizhi Hou return -EINVAL;
328b87f920bSLizhi Hou }
329b87f920bSLizhi Hou
330b87f920bSLizhi Hou rest = sizeof(header) - sizeof(u32);
331b87f920bSLizhi Hou read_addr += sizeof(u32);
3323c8cfec3SLizhi Hou memcpy_fromio((u32 *)&header + 1, read_addr, rest);
333b87f920bSLizhi Hou read_addr += rest;
334b87f920bSLizhi Hou
335b87f920bSLizhi Hou ret = mailbox_get_resp(mb_chann, &header, (u32 *)read_addr);
336b87f920bSLizhi Hou
337b87f920bSLizhi Hou mailbox_set_headptr(mb_chann, head + msg_size);
338b87f920bSLizhi Hou /* After update head, it can equal to ringbuf_size. This is expected. */
339b87f920bSLizhi Hou trace_mbox_set_head(MAILBOX_NAME, mb_chann->msix_irq,
340b87f920bSLizhi Hou header.opcode, header.id);
341b87f920bSLizhi Hou
342b87f920bSLizhi Hou return ret;
343b87f920bSLizhi Hou }
344b87f920bSLizhi Hou
mailbox_irq_handler(int irq,void * p)345b87f920bSLizhi Hou static irqreturn_t mailbox_irq_handler(int irq, void *p)
346b87f920bSLizhi Hou {
347b87f920bSLizhi Hou struct mailbox_channel *mb_chann = p;
348b87f920bSLizhi Hou
349b87f920bSLizhi Hou trace_mbox_irq_handle(MAILBOX_NAME, irq);
350b87f920bSLizhi Hou /* Schedule a rx_work to call the callback functions */
351b87f920bSLizhi Hou queue_work(mb_chann->work_q, &mb_chann->rx_work);
352b87f920bSLizhi Hou /* Clear IOHUB register */
353b87f920bSLizhi Hou mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
354b87f920bSLizhi Hou
355b87f920bSLizhi Hou return IRQ_HANDLED;
356b87f920bSLizhi Hou }
357b87f920bSLizhi Hou
mailbox_rx_worker(struct work_struct * rx_work)358b87f920bSLizhi Hou static void mailbox_rx_worker(struct work_struct *rx_work)
359b87f920bSLizhi Hou {
360b87f920bSLizhi Hou struct mailbox_channel *mb_chann;
361b87f920bSLizhi Hou int ret;
362b87f920bSLizhi Hou
363b87f920bSLizhi Hou mb_chann = container_of(rx_work, struct mailbox_channel, rx_work);
364b87f920bSLizhi Hou
365b87f920bSLizhi Hou if (READ_ONCE(mb_chann->bad_state)) {
366b87f920bSLizhi Hou MB_ERR(mb_chann, "Channel in bad state, work aborted");
367b87f920bSLizhi Hou return;
368b87f920bSLizhi Hou }
369b87f920bSLizhi Hou
370b87f920bSLizhi Hou while (1) {
371b87f920bSLizhi Hou /*
372b87f920bSLizhi Hou * If return is 0, keep consuming next message, until there is
373b87f920bSLizhi Hou * no messages or an error happened.
374b87f920bSLizhi Hou */
375b87f920bSLizhi Hou ret = mailbox_get_msg(mb_chann);
376b87f920bSLizhi Hou if (ret == -ENOENT)
377b87f920bSLizhi Hou break;
378b87f920bSLizhi Hou
379b87f920bSLizhi Hou /* Other error means device doesn't look good, disable irq. */
380b87f920bSLizhi Hou if (unlikely(ret)) {
381b87f920bSLizhi Hou MB_ERR(mb_chann, "Unexpected ret %d, disable irq", ret);
382b87f920bSLizhi Hou WRITE_ONCE(mb_chann->bad_state, true);
383b87f920bSLizhi Hou disable_irq(mb_chann->msix_irq);
384b87f920bSLizhi Hou break;
385b87f920bSLizhi Hou }
386b87f920bSLizhi Hou }
387b87f920bSLizhi Hou }
388b87f920bSLizhi Hou
xdna_mailbox_send_msg(struct mailbox_channel * mb_chann,const struct xdna_mailbox_msg * msg,u64 tx_timeout)389b87f920bSLizhi Hou int xdna_mailbox_send_msg(struct mailbox_channel *mb_chann,
390b87f920bSLizhi Hou const struct xdna_mailbox_msg *msg, u64 tx_timeout)
391b87f920bSLizhi Hou {
392b87f920bSLizhi Hou struct xdna_msg_header *header;
393b87f920bSLizhi Hou struct mailbox_msg *mb_msg;
394b87f920bSLizhi Hou size_t pkg_size;
395b87f920bSLizhi Hou int ret;
396b87f920bSLizhi Hou
397b87f920bSLizhi Hou pkg_size = sizeof(*header) + msg->send_size;
398b87f920bSLizhi Hou if (pkg_size > mailbox_get_ringbuf_size(mb_chann, CHAN_RES_X2I)) {
399b87f920bSLizhi Hou MB_ERR(mb_chann, "Message size larger than ringbuf size");
400b87f920bSLizhi Hou return -EINVAL;
401b87f920bSLizhi Hou }
402b87f920bSLizhi Hou
403b87f920bSLizhi Hou if (unlikely(!IS_ALIGNED(msg->send_size, 4))) {
404b87f920bSLizhi Hou MB_ERR(mb_chann, "Message must be 4 bytes align");
405b87f920bSLizhi Hou return -EINVAL;
406b87f920bSLizhi Hou }
407b87f920bSLizhi Hou
408b87f920bSLizhi Hou /* The fist word in payload can NOT be TOMBSTONE */
409b87f920bSLizhi Hou if (unlikely(((u32 *)msg->send_data)[0] == TOMBSTONE)) {
410b87f920bSLizhi Hou MB_ERR(mb_chann, "Tomb stone in data");
411b87f920bSLizhi Hou return -EINVAL;
412b87f920bSLizhi Hou }
413b87f920bSLizhi Hou
414b87f920bSLizhi Hou if (READ_ONCE(mb_chann->bad_state)) {
415b87f920bSLizhi Hou MB_ERR(mb_chann, "Channel in bad state");
416b87f920bSLizhi Hou return -EPIPE;
417b87f920bSLizhi Hou }
418b87f920bSLizhi Hou
419b87f920bSLizhi Hou mb_msg = kzalloc(sizeof(*mb_msg) + pkg_size, GFP_KERNEL);
420b87f920bSLizhi Hou if (!mb_msg)
421b87f920bSLizhi Hou return -ENOMEM;
422b87f920bSLizhi Hou
423b87f920bSLizhi Hou mb_msg->handle = msg->handle;
424b87f920bSLizhi Hou mb_msg->notify_cb = msg->notify_cb;
425b87f920bSLizhi Hou mb_msg->pkg_size = pkg_size;
426b87f920bSLizhi Hou
427b87f920bSLizhi Hou header = &mb_msg->pkg.header;
428b87f920bSLizhi Hou /*
429b87f920bSLizhi Hou * Hardware use total_size and size to split huge message.
430b87f920bSLizhi Hou * We do not support it here. Thus the values are the same.
431b87f920bSLizhi Hou */
432b87f920bSLizhi Hou header->total_size = msg->send_size;
433b87f920bSLizhi Hou header->sz_ver = FIELD_PREP(MSG_BODY_SZ, msg->send_size) |
434b87f920bSLizhi Hou FIELD_PREP(MSG_PROTO_VER, MSG_PROTOCOL_VERSION);
435b87f920bSLizhi Hou header->opcode = msg->opcode;
436b87f920bSLizhi Hou memcpy(mb_msg->pkg.payload, msg->send_data, msg->send_size);
437b87f920bSLizhi Hou
438b87f920bSLizhi Hou ret = mailbox_acquire_msgid(mb_chann, mb_msg);
439b87f920bSLizhi Hou if (unlikely(ret < 0)) {
440b87f920bSLizhi Hou MB_ERR(mb_chann, "mailbox_acquire_msgid failed");
441b87f920bSLizhi Hou goto msg_id_failed;
442b87f920bSLizhi Hou }
443b87f920bSLizhi Hou header->id = ret;
444b87f920bSLizhi Hou
445b87f920bSLizhi Hou MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x",
446b87f920bSLizhi Hou header->opcode, header->total_size, header->id);
447b87f920bSLizhi Hou
448b87f920bSLizhi Hou ret = mailbox_send_msg(mb_chann, mb_msg);
449b87f920bSLizhi Hou if (ret) {
450b87f920bSLizhi Hou MB_DBG(mb_chann, "Error in mailbox send msg, ret %d", ret);
451b87f920bSLizhi Hou goto release_id;
452b87f920bSLizhi Hou }
453b87f920bSLizhi Hou
454b87f920bSLizhi Hou return 0;
455b87f920bSLizhi Hou
456b87f920bSLizhi Hou release_id:
457b87f920bSLizhi Hou mailbox_release_msgid(mb_chann, header->id);
458b87f920bSLizhi Hou msg_id_failed:
459b87f920bSLizhi Hou kfree(mb_msg);
460b87f920bSLizhi Hou return ret;
461b87f920bSLizhi Hou }
462b87f920bSLizhi Hou
463b87f920bSLizhi Hou struct mailbox_channel *
xdna_mailbox_create_channel(struct mailbox * mb,const struct xdna_mailbox_chann_res * x2i,const struct xdna_mailbox_chann_res * i2x,u32 iohub_int_addr,int mb_irq)464b87f920bSLizhi Hou xdna_mailbox_create_channel(struct mailbox *mb,
465b87f920bSLizhi Hou const struct xdna_mailbox_chann_res *x2i,
466b87f920bSLizhi Hou const struct xdna_mailbox_chann_res *i2x,
467b87f920bSLizhi Hou u32 iohub_int_addr,
468b87f920bSLizhi Hou int mb_irq)
469b87f920bSLizhi Hou {
470b87f920bSLizhi Hou struct mailbox_channel *mb_chann;
471b87f920bSLizhi Hou int ret;
472b87f920bSLizhi Hou
473b87f920bSLizhi Hou if (!is_power_of_2(x2i->rb_size) || !is_power_of_2(i2x->rb_size)) {
474b87f920bSLizhi Hou pr_err("Ring buf size must be power of 2");
475b87f920bSLizhi Hou return NULL;
476b87f920bSLizhi Hou }
477b87f920bSLizhi Hou
478b87f920bSLizhi Hou mb_chann = kzalloc(sizeof(*mb_chann), GFP_KERNEL);
479b87f920bSLizhi Hou if (!mb_chann)
480b87f920bSLizhi Hou return NULL;
481b87f920bSLizhi Hou
482b87f920bSLizhi Hou mb_chann->mb = mb;
483b87f920bSLizhi Hou mb_chann->msix_irq = mb_irq;
484b87f920bSLizhi Hou mb_chann->iohub_int_addr = iohub_int_addr;
485b87f920bSLizhi Hou memcpy(&mb_chann->res[CHAN_RES_X2I], x2i, sizeof(*x2i));
486b87f920bSLizhi Hou memcpy(&mb_chann->res[CHAN_RES_I2X], i2x, sizeof(*i2x));
487b87f920bSLizhi Hou
488a37d7847SLizhi Hou xa_init_flags(&mb_chann->chan_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
489b87f920bSLizhi Hou mb_chann->x2i_tail = mailbox_get_tailptr(mb_chann, CHAN_RES_X2I);
490b87f920bSLizhi Hou mb_chann->i2x_head = mailbox_get_headptr(mb_chann, CHAN_RES_I2X);
491b87f920bSLizhi Hou
492b87f920bSLizhi Hou INIT_WORK(&mb_chann->rx_work, mailbox_rx_worker);
493b87f920bSLizhi Hou mb_chann->work_q = create_singlethread_workqueue(MAILBOX_NAME);
494b87f920bSLizhi Hou if (!mb_chann->work_q) {
495b87f920bSLizhi Hou MB_ERR(mb_chann, "Create workqueue failed");
496b87f920bSLizhi Hou goto free_and_out;
497b87f920bSLizhi Hou }
498b87f920bSLizhi Hou
499b87f920bSLizhi Hou /* Everything look good. Time to enable irq handler */
500b87f920bSLizhi Hou ret = request_irq(mb_irq, mailbox_irq_handler, 0, MAILBOX_NAME, mb_chann);
501b87f920bSLizhi Hou if (ret) {
502b87f920bSLizhi Hou MB_ERR(mb_chann, "Failed to request irq %d ret %d", mb_irq, ret);
503b87f920bSLizhi Hou goto destroy_wq;
504b87f920bSLizhi Hou }
505b87f920bSLizhi Hou
506b87f920bSLizhi Hou mb_chann->bad_state = false;
507b87f920bSLizhi Hou
508b87f920bSLizhi Hou MB_DBG(mb_chann, "Mailbox channel created (irq: %d)", mb_chann->msix_irq);
509b87f920bSLizhi Hou return mb_chann;
510b87f920bSLizhi Hou
511b87f920bSLizhi Hou destroy_wq:
512b87f920bSLizhi Hou destroy_workqueue(mb_chann->work_q);
513b87f920bSLizhi Hou free_and_out:
514b87f920bSLizhi Hou kfree(mb_chann);
515b87f920bSLizhi Hou return NULL;
516b87f920bSLizhi Hou }
517b87f920bSLizhi Hou
xdna_mailbox_destroy_channel(struct mailbox_channel * mb_chann)518b87f920bSLizhi Hou int xdna_mailbox_destroy_channel(struct mailbox_channel *mb_chann)
519b87f920bSLizhi Hou {
520a37d7847SLizhi Hou struct mailbox_msg *mb_msg;
521a37d7847SLizhi Hou unsigned long msg_id;
522a37d7847SLizhi Hou
523b87f920bSLizhi Hou MB_DBG(mb_chann, "IRQ disabled and RX work cancelled");
524b87f920bSLizhi Hou free_irq(mb_chann->msix_irq, mb_chann);
525b87f920bSLizhi Hou destroy_workqueue(mb_chann->work_q);
526b87f920bSLizhi Hou /* We can clean up and release resources */
527b87f920bSLizhi Hou
528a37d7847SLizhi Hou xa_for_each(&mb_chann->chan_xa, msg_id, mb_msg)
529a37d7847SLizhi Hou mailbox_release_msg(mb_chann, mb_msg);
530a37d7847SLizhi Hou
531a37d7847SLizhi Hou xa_destroy(&mb_chann->chan_xa);
532b87f920bSLizhi Hou
533b87f920bSLizhi Hou MB_DBG(mb_chann, "Mailbox channel destroyed, irq: %d", mb_chann->msix_irq);
534b87f920bSLizhi Hou kfree(mb_chann);
535b87f920bSLizhi Hou return 0;
536b87f920bSLizhi Hou }
537b87f920bSLizhi Hou
xdna_mailbox_stop_channel(struct mailbox_channel * mb_chann)538b87f920bSLizhi Hou void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann)
539b87f920bSLizhi Hou {
540b87f920bSLizhi Hou /* Disable an irq and wait. This might sleep. */
541b87f920bSLizhi Hou disable_irq(mb_chann->msix_irq);
542b87f920bSLizhi Hou
543b87f920bSLizhi Hou /* Cancel RX work and wait for it to finish */
544b87f920bSLizhi Hou cancel_work_sync(&mb_chann->rx_work);
545b87f920bSLizhi Hou MB_DBG(mb_chann, "IRQ disabled and RX work cancelled");
546b87f920bSLizhi Hou }
547b87f920bSLizhi Hou
xdnam_mailbox_create(struct drm_device * ddev,const struct xdna_mailbox_res * res)548b87f920bSLizhi Hou struct mailbox *xdnam_mailbox_create(struct drm_device *ddev,
549b87f920bSLizhi Hou const struct xdna_mailbox_res *res)
550b87f920bSLizhi Hou {
551b87f920bSLizhi Hou struct mailbox *mb;
552b87f920bSLizhi Hou
553b87f920bSLizhi Hou mb = drmm_kzalloc(ddev, sizeof(*mb), GFP_KERNEL);
554b87f920bSLizhi Hou if (!mb)
555b87f920bSLizhi Hou return NULL;
556b87f920bSLizhi Hou mb->dev = ddev->dev;
557b87f920bSLizhi Hou
558b87f920bSLizhi Hou /* mailbox and ring buf base and size information */
559b87f920bSLizhi Hou memcpy(&mb->res, res, sizeof(*res));
560b87f920bSLizhi Hou
561b87f920bSLizhi Hou return mb;
562b87f920bSLizhi Hou }
563