xref: /linux/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1*1f95f618SAnoop Vijay // SPDX-License-Identifier: MIT
2*1f95f618SAnoop Vijay /*
3*1f95f618SAnoop Vijay  * Copyright © 2026 Intel Corporation
4*1f95f618SAnoop Vijay  */
5*1f95f618SAnoop Vijay 
6*1f95f618SAnoop Vijay #include <linux/bitfield.h>
7*1f95f618SAnoop Vijay #include <linux/cleanup.h>
8*1f95f618SAnoop Vijay #include <linux/minmax.h>
9*1f95f618SAnoop Vijay #include <linux/slab.h>
10*1f95f618SAnoop Vijay #include <linux/string.h>
11*1f95f618SAnoop Vijay 
12*1f95f618SAnoop Vijay #include "regs/xe_sysctrl_regs.h"
13*1f95f618SAnoop Vijay #include "xe_device.h"
14*1f95f618SAnoop Vijay #include "xe_mmio.h"
15*1f95f618SAnoop Vijay #include "xe_pm.h"
16*1f95f618SAnoop Vijay #include "xe_printk.h"
17*1f95f618SAnoop Vijay #include "xe_sysctrl.h"
18*1f95f618SAnoop Vijay #include "xe_sysctrl_mailbox.h"
19*1f95f618SAnoop Vijay #include "xe_sysctrl_mailbox_types.h"
20*1f95f618SAnoop Vijay 
21*1f95f618SAnoop Vijay struct xe_sysctrl_mailbox_msg_hdr {
22*1f95f618SAnoop Vijay 	__le32 data;
23*1f95f618SAnoop Vijay } __packed;
24*1f95f618SAnoop Vijay 
25*1f95f618SAnoop Vijay #define XE_SYSCTRL_HDR_GROUP_ID(hdr) \
26*1f95f618SAnoop Vijay 	FIELD_GET(SYSCTRL_HDR_GROUP_ID_MASK, le32_to_cpu((hdr)->data))
27*1f95f618SAnoop Vijay 
28*1f95f618SAnoop Vijay #define XE_SYSCTRL_HDR_COMMAND(hdr) \
29*1f95f618SAnoop Vijay 	FIELD_GET(SYSCTRL_HDR_COMMAND_MASK, le32_to_cpu((hdr)->data))
30*1f95f618SAnoop Vijay 
31*1f95f618SAnoop Vijay #define XE_SYSCTRL_HDR_IS_RESPONSE(hdr) \
32*1f95f618SAnoop Vijay 	FIELD_GET(SYSCTRL_HDR_IS_RESPONSE, le32_to_cpu((hdr)->data))
33*1f95f618SAnoop Vijay 
34*1f95f618SAnoop Vijay #define XE_SYSCTRL_HDR_RESULT(hdr) \
35*1f95f618SAnoop Vijay 	FIELD_GET(SYSCTRL_HDR_RESULT_MASK, le32_to_cpu((hdr)->data))
36*1f95f618SAnoop Vijay 
37*1f95f618SAnoop Vijay static bool sysctrl_wait_bit_clear(struct xe_sysctrl *sc, u32 bit_mask,
38*1f95f618SAnoop Vijay 				   unsigned int timeout_ms)
39*1f95f618SAnoop Vijay {
40*1f95f618SAnoop Vijay 	int ret;
41*1f95f618SAnoop Vijay 
42*1f95f618SAnoop Vijay 	ret = xe_mmio_wait32_not(sc->mmio, SYSCTRL_MB_CTRL, bit_mask, bit_mask,
43*1f95f618SAnoop Vijay 				 timeout_ms * 1000, NULL, false);
44*1f95f618SAnoop Vijay 
45*1f95f618SAnoop Vijay 	return ret == 0;
46*1f95f618SAnoop Vijay }
47*1f95f618SAnoop Vijay 
48*1f95f618SAnoop Vijay static bool sysctrl_wait_bit_set(struct xe_sysctrl *sc, u32 bit_mask,
49*1f95f618SAnoop Vijay 				 unsigned int timeout_ms)
50*1f95f618SAnoop Vijay {
51*1f95f618SAnoop Vijay 	int ret;
52*1f95f618SAnoop Vijay 
53*1f95f618SAnoop Vijay 	ret = xe_mmio_wait32(sc->mmio, SYSCTRL_MB_CTRL, bit_mask, bit_mask,
54*1f95f618SAnoop Vijay 			     timeout_ms * 1000, NULL, false);
55*1f95f618SAnoop Vijay 
56*1f95f618SAnoop Vijay 	return ret == 0;
57*1f95f618SAnoop Vijay }
58*1f95f618SAnoop Vijay 
59*1f95f618SAnoop Vijay static int sysctrl_write_frame(struct xe_sysctrl *sc, const void *frame,
60*1f95f618SAnoop Vijay 			       size_t len)
61*1f95f618SAnoop Vijay {
62*1f95f618SAnoop Vijay 	static const struct xe_reg regs[] = {
63*1f95f618SAnoop Vijay 		SYSCTRL_MB_DATA0, SYSCTRL_MB_DATA1, SYSCTRL_MB_DATA2, SYSCTRL_MB_DATA3
64*1f95f618SAnoop Vijay 	};
65*1f95f618SAnoop Vijay 	struct xe_device *xe = sc_to_xe(sc);
66*1f95f618SAnoop Vijay 	u32 val[XE_SYSCTRL_MB_FRAME_SIZE / sizeof(u32)] = {0};
67*1f95f618SAnoop Vijay 	u32 dw = DIV_ROUND_UP(len, sizeof(u32));
68*1f95f618SAnoop Vijay 	u32 i;
69*1f95f618SAnoop Vijay 
70*1f95f618SAnoop Vijay 	xe_assert(xe, len > 0 && len <= XE_SYSCTRL_MB_FRAME_SIZE);
71*1f95f618SAnoop Vijay 
72*1f95f618SAnoop Vijay 	memcpy(val, frame, len);
73*1f95f618SAnoop Vijay 
74*1f95f618SAnoop Vijay 	for (i = 0; i < dw; i++)
75*1f95f618SAnoop Vijay 		xe_mmio_write32(sc->mmio, regs[i], val[i]);
76*1f95f618SAnoop Vijay 
77*1f95f618SAnoop Vijay 	return 0;
78*1f95f618SAnoop Vijay }
79*1f95f618SAnoop Vijay 
80*1f95f618SAnoop Vijay static int sysctrl_read_frame(struct xe_sysctrl *sc, void *frame,
81*1f95f618SAnoop Vijay 			      size_t len)
82*1f95f618SAnoop Vijay {
83*1f95f618SAnoop Vijay 	static const struct xe_reg regs[] = {
84*1f95f618SAnoop Vijay 		SYSCTRL_MB_DATA0, SYSCTRL_MB_DATA1, SYSCTRL_MB_DATA2, SYSCTRL_MB_DATA3
85*1f95f618SAnoop Vijay 	};
86*1f95f618SAnoop Vijay 	struct xe_device *xe = sc_to_xe(sc);
87*1f95f618SAnoop Vijay 	u32 val[XE_SYSCTRL_MB_FRAME_SIZE / sizeof(u32)] = {0};
88*1f95f618SAnoop Vijay 	u32 dw = DIV_ROUND_UP(len, sizeof(u32));
89*1f95f618SAnoop Vijay 	u32 i;
90*1f95f618SAnoop Vijay 
91*1f95f618SAnoop Vijay 	xe_assert(xe, len > 0 && len <= XE_SYSCTRL_MB_FRAME_SIZE);
92*1f95f618SAnoop Vijay 
93*1f95f618SAnoop Vijay 	for (i = 0; i < dw; i++)
94*1f95f618SAnoop Vijay 		val[i] = xe_mmio_read32(sc->mmio, regs[i]);
95*1f95f618SAnoop Vijay 
96*1f95f618SAnoop Vijay 	memcpy(frame, val, len);
97*1f95f618SAnoop Vijay 
98*1f95f618SAnoop Vijay 	return 0;
99*1f95f618SAnoop Vijay }
100*1f95f618SAnoop Vijay 
101*1f95f618SAnoop Vijay static void sysctrl_clear_response(struct xe_sysctrl *sc)
102*1f95f618SAnoop Vijay {
103*1f95f618SAnoop Vijay 	xe_mmio_rmw32(sc->mmio, SYSCTRL_MB_CTRL, SYSCTRL_MB_CTRL_RUN_BUSY_OUT, 0);
104*1f95f618SAnoop Vijay }
105*1f95f618SAnoop Vijay 
106*1f95f618SAnoop Vijay static int sysctrl_prepare_command(struct xe_device *xe,
107*1f95f618SAnoop Vijay 				   u8 group_id, u8 command,
108*1f95f618SAnoop Vijay 				   const void *data_in, size_t data_in_len,
109*1f95f618SAnoop Vijay 				   u8 **mbox_cmd, size_t *cmd_size)
110*1f95f618SAnoop Vijay {
111*1f95f618SAnoop Vijay 	struct xe_sysctrl_mailbox_msg_hdr *hdr;
112*1f95f618SAnoop Vijay 	size_t size;
113*1f95f618SAnoop Vijay 	u8 *buffer;
114*1f95f618SAnoop Vijay 
115*1f95f618SAnoop Vijay 	xe_assert(xe, command <= SYSCTRL_HDR_COMMAND_MAX);
116*1f95f618SAnoop Vijay 
117*1f95f618SAnoop Vijay 	if (data_in_len > XE_SYSCTRL_MB_MAX_MESSAGE_SIZE - sizeof(*hdr)) {
118*1f95f618SAnoop Vijay 		xe_err(xe, "sysctrl: Input data too large: %zu bytes\n", data_in_len);
119*1f95f618SAnoop Vijay 		return -EINVAL;
120*1f95f618SAnoop Vijay 	}
121*1f95f618SAnoop Vijay 
122*1f95f618SAnoop Vijay 	size = sizeof(*hdr) + data_in_len;
123*1f95f618SAnoop Vijay 
124*1f95f618SAnoop Vijay 	buffer = kmalloc(size, GFP_KERNEL);
125*1f95f618SAnoop Vijay 	if (!buffer)
126*1f95f618SAnoop Vijay 		return -ENOMEM;
127*1f95f618SAnoop Vijay 
128*1f95f618SAnoop Vijay 	hdr = (struct xe_sysctrl_mailbox_msg_hdr *)buffer;
129*1f95f618SAnoop Vijay 	hdr->data = cpu_to_le32(FIELD_PREP(SYSCTRL_HDR_GROUP_ID_MASK, group_id) |
130*1f95f618SAnoop Vijay 				     FIELD_PREP(SYSCTRL_HDR_COMMAND_MASK, command));
131*1f95f618SAnoop Vijay 
132*1f95f618SAnoop Vijay 	if (data_in && data_in_len)
133*1f95f618SAnoop Vijay 		memcpy(buffer + sizeof(*hdr), data_in, data_in_len);
134*1f95f618SAnoop Vijay 
135*1f95f618SAnoop Vijay 	*mbox_cmd = buffer;
136*1f95f618SAnoop Vijay 	*cmd_size = size;
137*1f95f618SAnoop Vijay 
138*1f95f618SAnoop Vijay 	return 0;
139*1f95f618SAnoop Vijay }
140*1f95f618SAnoop Vijay 
141*1f95f618SAnoop Vijay static int sysctrl_send_frames(struct xe_sysctrl *sc,
142*1f95f618SAnoop Vijay 			       const u8 *mbox_cmd,
143*1f95f618SAnoop Vijay 			       size_t cmd_size, unsigned int timeout_ms)
144*1f95f618SAnoop Vijay {
145*1f95f618SAnoop Vijay 	struct xe_device *xe = sc_to_xe(sc);
146*1f95f618SAnoop Vijay 	u32 ctrl_reg, total_frames, frame;
147*1f95f618SAnoop Vijay 	size_t bytes_sent, frame_size;
148*1f95f618SAnoop Vijay 
149*1f95f618SAnoop Vijay 	total_frames = DIV_ROUND_UP(cmd_size, XE_SYSCTRL_MB_FRAME_SIZE);
150*1f95f618SAnoop Vijay 
151*1f95f618SAnoop Vijay 	if (!sysctrl_wait_bit_clear(sc, SYSCTRL_MB_CTRL_RUN_BUSY, timeout_ms)) {
152*1f95f618SAnoop Vijay 		xe_err(xe, "sysctrl: Mailbox busy\n");
153*1f95f618SAnoop Vijay 		return -EBUSY;
154*1f95f618SAnoop Vijay 	}
155*1f95f618SAnoop Vijay 
156*1f95f618SAnoop Vijay 	sc->phase_bit ^= 1;
157*1f95f618SAnoop Vijay 	bytes_sent = 0;
158*1f95f618SAnoop Vijay 
159*1f95f618SAnoop Vijay 	for (frame = 0; frame < total_frames; frame++) {
160*1f95f618SAnoop Vijay 		frame_size = min_t(size_t, cmd_size - bytes_sent, XE_SYSCTRL_MB_FRAME_SIZE);
161*1f95f618SAnoop Vijay 
162*1f95f618SAnoop Vijay 		if (sysctrl_write_frame(sc, mbox_cmd + bytes_sent, frame_size)) {
163*1f95f618SAnoop Vijay 			xe_err(xe, "sysctrl: Failed to write frame %u\n", frame);
164*1f95f618SAnoop Vijay 			sc->phase_bit = 0;
165*1f95f618SAnoop Vijay 			return -EIO;
166*1f95f618SAnoop Vijay 		}
167*1f95f618SAnoop Vijay 
168*1f95f618SAnoop Vijay 		ctrl_reg = SYSCTRL_MB_CTRL_RUN_BUSY |
169*1f95f618SAnoop Vijay 			   REG_FIELD_PREP(SYSCTRL_FRAME_CURRENT_MASK, frame) |
170*1f95f618SAnoop Vijay 			   REG_FIELD_PREP(SYSCTRL_FRAME_TOTAL_MASK, total_frames - 1) |
171*1f95f618SAnoop Vijay 			   SYSCTRL_MB_CTRL_CMD |
172*1f95f618SAnoop Vijay 			   (sc->phase_bit ? SYSCTRL_FRAME_PHASE : 0);
173*1f95f618SAnoop Vijay 
174*1f95f618SAnoop Vijay 		xe_mmio_write32(sc->mmio, SYSCTRL_MB_CTRL, ctrl_reg);
175*1f95f618SAnoop Vijay 
176*1f95f618SAnoop Vijay 		if (!sysctrl_wait_bit_clear(sc, SYSCTRL_MB_CTRL_RUN_BUSY, timeout_ms)) {
177*1f95f618SAnoop Vijay 			xe_err(xe, "sysctrl: Frame %u acknowledgment timeout\n", frame);
178*1f95f618SAnoop Vijay 			sc->phase_bit = 0;
179*1f95f618SAnoop Vijay 			return -ETIMEDOUT;
180*1f95f618SAnoop Vijay 		}
181*1f95f618SAnoop Vijay 
182*1f95f618SAnoop Vijay 		bytes_sent += frame_size;
183*1f95f618SAnoop Vijay 	}
184*1f95f618SAnoop Vijay 
185*1f95f618SAnoop Vijay 	return 0;
186*1f95f618SAnoop Vijay }
187*1f95f618SAnoop Vijay 
188*1f95f618SAnoop Vijay static int sysctrl_process_frame(struct xe_sysctrl *sc, void *out,
189*1f95f618SAnoop Vijay 				 size_t frame_size, unsigned int timeout_ms,
190*1f95f618SAnoop Vijay 				 bool *done)
191*1f95f618SAnoop Vijay {
192*1f95f618SAnoop Vijay 	u32 curr_frame, total_frames, ctrl_reg;
193*1f95f618SAnoop Vijay 	struct xe_device *xe = sc_to_xe(sc);
194*1f95f618SAnoop Vijay 	int ret;
195*1f95f618SAnoop Vijay 
196*1f95f618SAnoop Vijay 	if (!sysctrl_wait_bit_set(sc, SYSCTRL_MB_CTRL_RUN_BUSY_OUT, timeout_ms)) {
197*1f95f618SAnoop Vijay 		xe_err(xe, "sysctrl: Response frame timeout\n");
198*1f95f618SAnoop Vijay 		return -ETIMEDOUT;
199*1f95f618SAnoop Vijay 	}
200*1f95f618SAnoop Vijay 
201*1f95f618SAnoop Vijay 	ctrl_reg = xe_mmio_read32(sc->mmio, SYSCTRL_MB_CTRL);
202*1f95f618SAnoop Vijay 	total_frames = FIELD_GET(SYSCTRL_FRAME_TOTAL_MASK, ctrl_reg);
203*1f95f618SAnoop Vijay 	curr_frame = FIELD_GET(SYSCTRL_FRAME_CURRENT_MASK, ctrl_reg);
204*1f95f618SAnoop Vijay 
205*1f95f618SAnoop Vijay 	ret = sysctrl_read_frame(sc, out, frame_size);
206*1f95f618SAnoop Vijay 	if (ret)
207*1f95f618SAnoop Vijay 		return ret;
208*1f95f618SAnoop Vijay 
209*1f95f618SAnoop Vijay 	sysctrl_clear_response(sc);
210*1f95f618SAnoop Vijay 
211*1f95f618SAnoop Vijay 	if (curr_frame == total_frames)
212*1f95f618SAnoop Vijay 		*done = true;
213*1f95f618SAnoop Vijay 
214*1f95f618SAnoop Vijay 	return 0;
215*1f95f618SAnoop Vijay }
216*1f95f618SAnoop Vijay 
217*1f95f618SAnoop Vijay static int sysctrl_receive_frames(struct xe_sysctrl *sc,
218*1f95f618SAnoop Vijay 				  const struct xe_sysctrl_mailbox_msg_hdr *req,
219*1f95f618SAnoop Vijay 				  void *data_out, size_t data_out_len,
220*1f95f618SAnoop Vijay 				  size_t *rdata_len, unsigned int timeout_ms)
221*1f95f618SAnoop Vijay {
222*1f95f618SAnoop Vijay 	struct xe_sysctrl_mailbox_msg_hdr *hdr;
223*1f95f618SAnoop Vijay 	struct xe_device *xe = sc_to_xe(sc);
224*1f95f618SAnoop Vijay 	size_t remain = sizeof(*hdr) + data_out_len;
225*1f95f618SAnoop Vijay 	u8 *buffer __free(kfree) = kzalloc(remain, GFP_KERNEL);
226*1f95f618SAnoop Vijay 	size_t frame_size;
227*1f95f618SAnoop Vijay 	bool done = false;
228*1f95f618SAnoop Vijay 	int ret = 0;
229*1f95f618SAnoop Vijay 	u8 *out;
230*1f95f618SAnoop Vijay 
231*1f95f618SAnoop Vijay 	if (!buffer)
232*1f95f618SAnoop Vijay 		return -ENOMEM;
233*1f95f618SAnoop Vijay 
234*1f95f618SAnoop Vijay 	out = buffer;
235*1f95f618SAnoop Vijay 	while (!done && remain) {
236*1f95f618SAnoop Vijay 		frame_size = min_t(size_t, remain, XE_SYSCTRL_MB_FRAME_SIZE);
237*1f95f618SAnoop Vijay 
238*1f95f618SAnoop Vijay 		ret = sysctrl_process_frame(sc, out, frame_size, timeout_ms,
239*1f95f618SAnoop Vijay 					    &done);
240*1f95f618SAnoop Vijay 		if (ret)
241*1f95f618SAnoop Vijay 			return ret;
242*1f95f618SAnoop Vijay 
243*1f95f618SAnoop Vijay 		remain -= frame_size;
244*1f95f618SAnoop Vijay 		out += frame_size;
245*1f95f618SAnoop Vijay 	}
246*1f95f618SAnoop Vijay 
247*1f95f618SAnoop Vijay 	hdr = (struct xe_sysctrl_mailbox_msg_hdr *)buffer;
248*1f95f618SAnoop Vijay 
249*1f95f618SAnoop Vijay 	if (!XE_SYSCTRL_HDR_IS_RESPONSE(hdr) ||
250*1f95f618SAnoop Vijay 	    XE_SYSCTRL_HDR_GROUP_ID(hdr) != XE_SYSCTRL_HDR_GROUP_ID(req) ||
251*1f95f618SAnoop Vijay 	    XE_SYSCTRL_HDR_COMMAND(hdr) != XE_SYSCTRL_HDR_COMMAND(req)) {
252*1f95f618SAnoop Vijay 		xe_err(xe, "sysctrl: Response header mismatch\n");
253*1f95f618SAnoop Vijay 		return -EPROTO;
254*1f95f618SAnoop Vijay 	}
255*1f95f618SAnoop Vijay 
256*1f95f618SAnoop Vijay 	if (XE_SYSCTRL_HDR_RESULT(hdr) != 0) {
257*1f95f618SAnoop Vijay 		xe_err(xe, "sysctrl: Firmware error: 0x%02lx\n",
258*1f95f618SAnoop Vijay 		       XE_SYSCTRL_HDR_RESULT(hdr));
259*1f95f618SAnoop Vijay 		return -EIO;
260*1f95f618SAnoop Vijay 	}
261*1f95f618SAnoop Vijay 
262*1f95f618SAnoop Vijay 	memcpy(data_out, hdr + 1, data_out_len);
263*1f95f618SAnoop Vijay 	*rdata_len = out - buffer - sizeof(*hdr);
264*1f95f618SAnoop Vijay 
265*1f95f618SAnoop Vijay 	return 0;
266*1f95f618SAnoop Vijay }
267*1f95f618SAnoop Vijay 
268*1f95f618SAnoop Vijay static int sysctrl_send_command(struct xe_sysctrl *sc,
269*1f95f618SAnoop Vijay 				const u8 *mbox_cmd, size_t cmd_size,
270*1f95f618SAnoop Vijay 				void *data_out, size_t data_out_len,
271*1f95f618SAnoop Vijay 				size_t *rdata_len, unsigned int timeout_ms)
272*1f95f618SAnoop Vijay {
273*1f95f618SAnoop Vijay 	const struct xe_sysctrl_mailbox_msg_hdr *hdr;
274*1f95f618SAnoop Vijay 	size_t received;
275*1f95f618SAnoop Vijay 	int ret;
276*1f95f618SAnoop Vijay 
277*1f95f618SAnoop Vijay 	ret = sysctrl_send_frames(sc, mbox_cmd, cmd_size, timeout_ms);
278*1f95f618SAnoop Vijay 	if (ret)
279*1f95f618SAnoop Vijay 		return ret;
280*1f95f618SAnoop Vijay 
281*1f95f618SAnoop Vijay 	if (!data_out || !rdata_len)
282*1f95f618SAnoop Vijay 		return 0;
283*1f95f618SAnoop Vijay 
284*1f95f618SAnoop Vijay 	hdr = (const struct xe_sysctrl_mailbox_msg_hdr *)mbox_cmd;
285*1f95f618SAnoop Vijay 
286*1f95f618SAnoop Vijay 	ret = sysctrl_receive_frames(sc, hdr, data_out, data_out_len,
287*1f95f618SAnoop Vijay 				     &received, timeout_ms);
288*1f95f618SAnoop Vijay 	if (ret)
289*1f95f618SAnoop Vijay 		return ret;
290*1f95f618SAnoop Vijay 
291*1f95f618SAnoop Vijay 	*rdata_len = received;
292*1f95f618SAnoop Vijay 
293*1f95f618SAnoop Vijay 	return 0;
294*1f95f618SAnoop Vijay }
295*1f95f618SAnoop Vijay 
296*1f95f618SAnoop Vijay /**
297*1f95f618SAnoop Vijay  * xe_sysctrl_mailbox_init - Initialize System Controller mailbox interface
298*1f95f618SAnoop Vijay  * @sc: System controller structure
299*1f95f618SAnoop Vijay  *
300*1f95f618SAnoop Vijay  * Initialize system controller mailbox interface for communication.
301*1f95f618SAnoop Vijay  */
302*1f95f618SAnoop Vijay void xe_sysctrl_mailbox_init(struct xe_sysctrl *sc)
303*1f95f618SAnoop Vijay {
304*1f95f618SAnoop Vijay 	u32 ctrl_reg;
305*1f95f618SAnoop Vijay 
306*1f95f618SAnoop Vijay 	ctrl_reg = xe_mmio_read32(sc->mmio, SYSCTRL_MB_CTRL);
307*1f95f618SAnoop Vijay 	sc->phase_bit = (ctrl_reg & SYSCTRL_FRAME_PHASE) ? 1 : 0;
308*1f95f618SAnoop Vijay }
309*1f95f618SAnoop Vijay 
310*1f95f618SAnoop Vijay /**
311*1f95f618SAnoop Vijay  * xe_sysctrl_send_command() - Send mailbox command to System Controller
312*1f95f618SAnoop Vijay  * @sc: System Controller instance
313*1f95f618SAnoop Vijay  * @cmd: Command descriptor containing request header and payload buffers
314*1f95f618SAnoop Vijay  * @rdata_len: Pointer to store actual response data length
315*1f95f618SAnoop Vijay  *
316*1f95f618SAnoop Vijay  * Sends a mailbox command to System Controller firmware using
317*1f95f618SAnoop Vijay  * System Controller mailbox and waits for a response.
318*1f95f618SAnoop Vijay  *
319*1f95f618SAnoop Vijay  * Request payload is provided via @cmd->data_in and @cmd->data_in_len.
320*1f95f618SAnoop Vijay  * If a response is expected, @cmd->data_out must point to a buffer of
321*1f95f618SAnoop Vijay  * size @cmd->data_out_len supplied by caller.
322*1f95f618SAnoop Vijay  *
323*1f95f618SAnoop Vijay  * On success, @rdata_len is updated with number of valid response bytes
324*1f95f618SAnoop Vijay  * returned by firmware, bounded by @cmd->data_out_len.
325*1f95f618SAnoop Vijay  *
326*1f95f618SAnoop Vijay  * Return: 0 on success, or negative errno on failure.
327*1f95f618SAnoop Vijay  */
328*1f95f618SAnoop Vijay int xe_sysctrl_send_command(struct xe_sysctrl *sc,
329*1f95f618SAnoop Vijay 			    struct xe_sysctrl_mailbox_command *cmd,
330*1f95f618SAnoop Vijay 			    size_t *rdata_len)
331*1f95f618SAnoop Vijay {
332*1f95f618SAnoop Vijay 	struct xe_device *xe = sc_to_xe(sc);
333*1f95f618SAnoop Vijay 	u8 group_id, command_code;
334*1f95f618SAnoop Vijay 	u8 *mbox_cmd = NULL;
335*1f95f618SAnoop Vijay 	size_t cmd_size = 0;
336*1f95f618SAnoop Vijay 	int ret;
337*1f95f618SAnoop Vijay 
338*1f95f618SAnoop Vijay 	guard(xe_pm_runtime_noresume)(xe);
339*1f95f618SAnoop Vijay 
340*1f95f618SAnoop Vijay 	if (!xe->info.has_sysctrl)
341*1f95f618SAnoop Vijay 		return -ENODEV;
342*1f95f618SAnoop Vijay 
343*1f95f618SAnoop Vijay 	xe_assert(xe, cmd->data_in || cmd->data_out);
344*1f95f618SAnoop Vijay 	xe_assert(xe, !cmd->data_in || cmd->data_in_len);
345*1f95f618SAnoop Vijay 	xe_assert(xe, !cmd->data_out || cmd->data_out_len);
346*1f95f618SAnoop Vijay 
347*1f95f618SAnoop Vijay 	group_id = XE_SYSCTRL_APP_HDR_GROUP_ID(&cmd->header);
348*1f95f618SAnoop Vijay 	command_code = XE_SYSCTRL_APP_HDR_COMMAND(&cmd->header);
349*1f95f618SAnoop Vijay 
350*1f95f618SAnoop Vijay 	might_sleep();
351*1f95f618SAnoop Vijay 
352*1f95f618SAnoop Vijay 	ret = sysctrl_prepare_command(xe, group_id, command_code,
353*1f95f618SAnoop Vijay 				      cmd->data_in, cmd->data_in_len,
354*1f95f618SAnoop Vijay 				      &mbox_cmd, &cmd_size);
355*1f95f618SAnoop Vijay 	if (ret) {
356*1f95f618SAnoop Vijay 		xe_err(xe, "sysctrl: Failed to prepare command: %pe\n", ERR_PTR(ret));
357*1f95f618SAnoop Vijay 		return ret;
358*1f95f618SAnoop Vijay 	}
359*1f95f618SAnoop Vijay 
360*1f95f618SAnoop Vijay 	guard(mutex)(&sc->cmd_lock);
361*1f95f618SAnoop Vijay 
362*1f95f618SAnoop Vijay 	ret = sysctrl_send_command(sc, mbox_cmd, cmd_size,
363*1f95f618SAnoop Vijay 				   cmd->data_out, cmd->data_out_len, rdata_len,
364*1f95f618SAnoop Vijay 				   XE_SYSCTRL_MB_DEFAULT_TIMEOUT_MS);
365*1f95f618SAnoop Vijay 	if (ret)
366*1f95f618SAnoop Vijay 		xe_err(xe, "sysctrl: Mailbox command failed: %pe\n", ERR_PTR(ret));
367*1f95f618SAnoop Vijay 
368*1f95f618SAnoop Vijay 	kfree(mbox_cmd);
369*1f95f618SAnoop Vijay 
370*1f95f618SAnoop Vijay 	return ret;
371*1f95f618SAnoop Vijay }
372