xref: /linux/drivers/accel/amdxdna/amdxdna_mailbox.c (revision 3ef7acec975bde28ab9cef92af76be8fc2ce684d)
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