xref: /linux/drivers/misc/cxl/hcalls.c (revision 906fd46a65383cd639e5eec72a047efc33045d86)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright 2015 IBM Corp.
4  */
5 
6 
7 #include <linux/compiler.h>
8 #include <linux/types.h>
9 #include <linux/delay.h>
10 #include <asm/byteorder.h>
11 #include "hcalls.h"
12 #include "trace.h"
13 
14 #define CXL_HCALL_TIMEOUT 60000
15 #define CXL_HCALL_TIMEOUT_DOWNLOAD 120000
16 
17 #define H_ATTACH_CA_PROCESS    0x344
18 #define H_CONTROL_CA_FUNCTION  0x348
19 #define H_DETACH_CA_PROCESS    0x34C
20 #define H_COLLECT_CA_INT_INFO  0x350
21 #define H_CONTROL_CA_FAULTS    0x354
22 #define H_DOWNLOAD_CA_FUNCTION 0x35C
23 #define H_DOWNLOAD_CA_FACILITY 0x364
24 #define H_CONTROL_CA_FACILITY  0x368
25 
26 #define H_CONTROL_CA_FUNCTION_RESET                   1 /* perform a reset */
27 #define H_CONTROL_CA_FUNCTION_SUSPEND_PROCESS         2 /* suspend a process from being executed */
28 #define H_CONTROL_CA_FUNCTION_RESUME_PROCESS          3 /* resume a process to be executed */
29 #define H_CONTROL_CA_FUNCTION_READ_ERR_STATE          4 /* read the error state */
30 #define H_CONTROL_CA_FUNCTION_GET_AFU_ERR             5 /* collect the AFU error buffer */
31 #define H_CONTROL_CA_FUNCTION_GET_CONFIG              6 /* collect configuration record */
32 #define H_CONTROL_CA_FUNCTION_GET_DOWNLOAD_STATE      7 /* query to return download status */
33 #define H_CONTROL_CA_FUNCTION_TERMINATE_PROCESS       8 /* terminate the process before completion */
34 #define H_CONTROL_CA_FUNCTION_COLLECT_VPD             9 /* collect VPD */
35 #define H_CONTROL_CA_FUNCTION_GET_FUNCTION_ERR_INT   11 /* read the function-wide error data based on an interrupt */
36 #define H_CONTROL_CA_FUNCTION_ACK_FUNCTION_ERR_INT   12 /* acknowledge function-wide error data based on an interrupt */
37 #define H_CONTROL_CA_FUNCTION_GET_ERROR_LOG          13 /* retrieve the Platform Log ID (PLID) of an error log */
38 
39 #define H_CONTROL_CA_FAULTS_RESPOND_PSL         1
40 #define H_CONTROL_CA_FAULTS_RESPOND_AFU         2
41 
42 #define H_CONTROL_CA_FACILITY_RESET             1 /* perform a reset */
43 #define H_CONTROL_CA_FACILITY_COLLECT_VPD       2 /* collect VPD */
44 
45 #define H_DOWNLOAD_CA_FACILITY_DOWNLOAD         1 /* download adapter image */
46 #define H_DOWNLOAD_CA_FACILITY_VALIDATE         2 /* validate adapter image */
47 
48 
49 #define _CXL_LOOP_HCALL(call, rc, retbuf, fn, ...)			\
50 	{								\
51 		unsigned int delay, total_delay = 0;			\
52 		u64 token = 0;						\
53 									\
54 		memset(retbuf, 0, sizeof(retbuf));			\
55 		while (1) {						\
56 			rc = call(fn, retbuf, __VA_ARGS__, token);	\
57 			token = retbuf[0];				\
58 			if (rc != H_BUSY && !H_IS_LONG_BUSY(rc))	\
59 				break;					\
60 									\
61 			if (rc == H_BUSY)				\
62 				delay = 10;				\
63 			else						\
64 				delay = get_longbusy_msecs(rc);		\
65 									\
66 			total_delay += delay;				\
67 			if (total_delay > CXL_HCALL_TIMEOUT) {		\
68 				WARN(1, "Warning: Giving up waiting for CXL hcall " \
69 					"%#x after %u msec\n", fn, total_delay); \
70 				rc = H_BUSY;				\
71 				break;					\
72 			}						\
73 			msleep(delay);					\
74 		}							\
75 	}
76 #define CXL_H_WAIT_UNTIL_DONE(...)  _CXL_LOOP_HCALL(plpar_hcall, __VA_ARGS__)
77 #define CXL_H9_WAIT_UNTIL_DONE(...) _CXL_LOOP_HCALL(plpar_hcall9, __VA_ARGS__)
78 
79 #define _PRINT_MSG(rc, format, ...)					\
80 	{								\
81 		if ((rc != H_SUCCESS) && (rc != H_CONTINUE))		\
82 			pr_err(format, __VA_ARGS__);			\
83 		else							\
84 			pr_devel(format, __VA_ARGS__);			\
85 	}								\
86 
87 
88 static char *afu_op_names[] = {
89 	"UNKNOWN_OP",		/* 0 undefined */
90 	"RESET",		/* 1 */
91 	"SUSPEND_PROCESS",	/* 2 */
92 	"RESUME_PROCESS",	/* 3 */
93 	"READ_ERR_STATE",	/* 4 */
94 	"GET_AFU_ERR",		/* 5 */
95 	"GET_CONFIG",		/* 6 */
96 	"GET_DOWNLOAD_STATE",	/* 7 */
97 	"TERMINATE_PROCESS",	/* 8 */
98 	"COLLECT_VPD",		/* 9 */
99 	"UNKNOWN_OP",		/* 10 undefined */
100 	"GET_FUNCTION_ERR_INT",	/* 11 */
101 	"ACK_FUNCTION_ERR_INT",	/* 12 */
102 	"GET_ERROR_LOG",	/* 13 */
103 };
104 
105 static char *control_adapter_op_names[] = {
106 	"UNKNOWN_OP",		/* 0 undefined */
107 	"RESET",		/* 1 */
108 	"COLLECT_VPD",		/* 2 */
109 };
110 
111 static char *download_op_names[] = {
112 	"UNKNOWN_OP",		/* 0 undefined */
113 	"DOWNLOAD",		/* 1 */
114 	"VALIDATE",		/* 2 */
115 };
116 
117 static char *op_str(unsigned int op, char *name_array[], int array_len)
118 {
119 	if (op >= array_len)
120 		return "UNKNOWN_OP";
121 	return name_array[op];
122 }
123 
124 #define OP_STR(op, name_array)      op_str(op, name_array, ARRAY_SIZE(name_array))
125 
126 #define OP_STR_AFU(op)              OP_STR(op, afu_op_names)
127 #define OP_STR_CONTROL_ADAPTER(op)  OP_STR(op, control_adapter_op_names)
128 #define OP_STR_DOWNLOAD_ADAPTER(op) OP_STR(op, download_op_names)
129 
130 
131 long cxl_h_attach_process(u64 unit_address,
132 			struct cxl_process_element_hcall *element,
133 			u64 *process_token, u64 *mmio_addr, u64 *mmio_size)
134 {
135 	unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
136 	long rc;
137 
138 	CXL_H_WAIT_UNTIL_DONE(rc, retbuf, H_ATTACH_CA_PROCESS, unit_address, virt_to_phys(element));
139 	_PRINT_MSG(rc, "cxl_h_attach_process(%#.16llx, %#.16lx): %li\n",
140 		unit_address, virt_to_phys(element), rc);
141 	trace_cxl_hcall_attach(unit_address, virt_to_phys(element), retbuf[0], retbuf[1], retbuf[2], rc);
142 
143 	pr_devel("token: 0x%.8lx mmio_addr: 0x%lx mmio_size: 0x%lx\nProcess Element Structure:\n",
144 		retbuf[0], retbuf[1], retbuf[2]);
145 	cxl_dump_debug_buffer(element, sizeof(*element));
146 
147 	switch (rc) {
148 	case H_SUCCESS:       /* The process info is attached to the coherent platform function */
149 		*process_token = retbuf[0];
150 		if (mmio_addr)
151 			*mmio_addr = retbuf[1];
152 		if (mmio_size)
153 			*mmio_size = retbuf[2];
154 		return 0;
155 	case H_PARAMETER:     /* An incorrect parameter was supplied. */
156 	case H_FUNCTION:      /* The function is not supported. */
157 		return -EINVAL;
158 	case H_AUTHORITY:     /* The partition does not have authority to perform this hcall */
159 	case H_RESOURCE:      /* The coherent platform function does not have enough additional resource to attach the process */
160 	case H_HARDWARE:      /* A hardware event prevented the attach operation */
161 	case H_STATE:         /* The coherent platform function is not in a valid state */
162 	case H_BUSY:
163 		return -EBUSY;
164 	default:
165 		WARN(1, "Unexpected return code: %lx", rc);
166 		return -EINVAL;
167 	}
168 }
169 
170 /*
171  * cxl_h_detach_process - Detach a process element from a coherent
172  *                        platform function.
173  */
174 long cxl_h_detach_process(u64 unit_address, u64 process_token)
175 {
176 	unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
177 	long rc;
178 
179 	CXL_H_WAIT_UNTIL_DONE(rc, retbuf, H_DETACH_CA_PROCESS, unit_address, process_token);
180 	_PRINT_MSG(rc, "cxl_h_detach_process(%#.16llx, 0x%.8llx): %li\n", unit_address, process_token, rc);
181 	trace_cxl_hcall_detach(unit_address, process_token, rc);
182 
183 	switch (rc) {
184 	case H_SUCCESS:       /* The process was detached from the coherent platform function */
185 		return 0;
186 	case H_PARAMETER:     /* An incorrect parameter was supplied. */
187 		return -EINVAL;
188 	case H_AUTHORITY:     /* The partition does not have authority to perform this hcall */
189 	case H_RESOURCE:      /* The function has page table mappings for MMIO */
190 	case H_HARDWARE:      /* A hardware event prevented the detach operation */
191 	case H_STATE:         /* The coherent platform function is not in a valid state */
192 	case H_BUSY:
193 		return -EBUSY;
194 	default:
195 		WARN(1, "Unexpected return code: %lx", rc);
196 		return -EINVAL;
197 	}
198 }
199 
200 /*
201  * cxl_h_control_function - This H_CONTROL_CA_FUNCTION hypervisor call allows
202  *                          the partition to manipulate or query
203  *                          certain coherent platform function behaviors.
204  */
205 static long cxl_h_control_function(u64 unit_address, u64 op,
206 				   u64 p1, u64 p2, u64 p3, u64 p4, u64 *out)
207 {
208 	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
209 	long rc;
210 
211 	CXL_H9_WAIT_UNTIL_DONE(rc, retbuf, H_CONTROL_CA_FUNCTION, unit_address, op, p1, p2, p3, p4);
212 	_PRINT_MSG(rc, "cxl_h_control_function(%#.16llx, %s(%#llx, %#llx, %#llx, %#llx, R4: %#lx)): %li\n",
213 		unit_address, OP_STR_AFU(op), p1, p2, p3, p4, retbuf[0], rc);
214 	trace_cxl_hcall_control_function(unit_address, OP_STR_AFU(op), p1, p2, p3, p4, retbuf[0], rc);
215 
216 	switch (rc) {
217 	case H_SUCCESS:       /* The operation is completed for the coherent platform function */
218 		if ((op == H_CONTROL_CA_FUNCTION_GET_FUNCTION_ERR_INT ||
219 		     op == H_CONTROL_CA_FUNCTION_READ_ERR_STATE ||
220 		     op == H_CONTROL_CA_FUNCTION_COLLECT_VPD))
221 			*out = retbuf[0];
222 		return 0;
223 	case H_PARAMETER:     /* An incorrect parameter was supplied. */
224 	case H_FUNCTION:      /* The function is not supported. */
225 	case H_NOT_FOUND:     /* The operation supplied was not valid */
226 	case H_NOT_AVAILABLE: /* The operation cannot be performed because the AFU has not been downloaded */
227 	case H_SG_LIST:       /* An block list entry was invalid */
228 		return -EINVAL;
229 	case H_AUTHORITY:     /* The partition does not have authority to perform this hcall */
230 	case H_RESOURCE:      /* The function has page table mappings for MMIO */
231 	case H_HARDWARE:      /* A hardware event prevented the attach operation */
232 	case H_STATE:         /* The coherent platform function is not in a valid state */
233 	case H_BUSY:
234 		return -EBUSY;
235 	default:
236 		WARN(1, "Unexpected return code: %lx", rc);
237 		return -EINVAL;
238 	}
239 }
240 
241 /*
242  * cxl_h_reset_afu - Perform a reset to the coherent platform function.
243  */
244 long cxl_h_reset_afu(u64 unit_address)
245 {
246 	return cxl_h_control_function(unit_address,
247 				H_CONTROL_CA_FUNCTION_RESET,
248 				0, 0, 0, 0,
249 				NULL);
250 }
251 
252 /*
253  * cxl_h_suspend_process - Suspend a process from being executed
254  * Parameter1 = process-token as returned from H_ATTACH_CA_PROCESS when
255  *              process was attached.
256  */
257 long cxl_h_suspend_process(u64 unit_address, u64 process_token)
258 {
259 	return cxl_h_control_function(unit_address,
260 				H_CONTROL_CA_FUNCTION_SUSPEND_PROCESS,
261 				process_token, 0, 0, 0,
262 				NULL);
263 }
264 
265 /*
266  * cxl_h_resume_process - Resume a process to be executed
267  * Parameter1 = process-token as returned from H_ATTACH_CA_PROCESS when
268  *              process was attached.
269  */
270 long cxl_h_resume_process(u64 unit_address, u64 process_token)
271 {
272 	return cxl_h_control_function(unit_address,
273 				H_CONTROL_CA_FUNCTION_RESUME_PROCESS,
274 				process_token, 0, 0, 0,
275 				NULL);
276 }
277 
278 /*
279  * cxl_h_read_error_state - Checks the error state of the coherent
280  *                          platform function.
281  * R4 contains the error state
282  */
283 long cxl_h_read_error_state(u64 unit_address, u64 *state)
284 {
285 	return cxl_h_control_function(unit_address,
286 				H_CONTROL_CA_FUNCTION_READ_ERR_STATE,
287 				0, 0, 0, 0,
288 				state);
289 }
290 
291 /*
292  * cxl_h_get_afu_err - collect the AFU error buffer
293  * Parameter1 = byte offset into error buffer to retrieve, valid values
294  *              are between 0 and (ibm,error-buffer-size - 1)
295  * Parameter2 = 4K aligned real address of error buffer, to be filled in
296  * Parameter3 = length of error buffer, valid values are 4K or less
297  */
298 long cxl_h_get_afu_err(u64 unit_address, u64 offset,
299 		u64 buf_address, u64 len)
300 {
301 	return cxl_h_control_function(unit_address,
302 				H_CONTROL_CA_FUNCTION_GET_AFU_ERR,
303 				offset, buf_address, len, 0,
304 				NULL);
305 }
306 
307 /*
308  * cxl_h_get_config - collect configuration record for the
309  *                    coherent platform function
310  * Parameter1 = # of configuration record to retrieve, valid values are
311  *              between 0 and (ibm,#config-records - 1)
312  * Parameter2 = byte offset into configuration record to retrieve,
313  *              valid values are between 0 and (ibm,config-record-size - 1)
314  * Parameter3 = 4K aligned real address of configuration record buffer,
315  *              to be filled in
316  * Parameter4 = length of configuration buffer, valid values are 4K or less
317  */
318 long cxl_h_get_config(u64 unit_address, u64 cr_num, u64 offset,
319 		u64 buf_address, u64 len)
320 {
321 	return cxl_h_control_function(unit_address,
322 				H_CONTROL_CA_FUNCTION_GET_CONFIG,
323 				cr_num, offset, buf_address, len,
324 				NULL);
325 }
326 
327 /*
328  * cxl_h_terminate_process - Terminate the process before completion
329  * Parameter1 = process-token as returned from H_ATTACH_CA_PROCESS when
330  *              process was attached.
331  */
332 long cxl_h_terminate_process(u64 unit_address, u64 process_token)
333 {
334 	return cxl_h_control_function(unit_address,
335 				H_CONTROL_CA_FUNCTION_TERMINATE_PROCESS,
336 				process_token, 0, 0, 0,
337 				NULL);
338 }
339 
340 /*
341  * cxl_h_collect_vpd - Collect VPD for the coherent platform function.
342  * Parameter1 = # of VPD record to retrieve, valid values are between 0
343  *              and (ibm,#config-records - 1).
344  * Parameter2 = 4K naturally aligned real buffer containing block
345  *              list entries
346  * Parameter3 = number of block list entries in the block list, valid
347  *              values are between 0 and 256
348  */
349 long cxl_h_collect_vpd(u64 unit_address, u64 record, u64 list_address,
350 		       u64 num, u64 *out)
351 {
352 	return cxl_h_control_function(unit_address,
353 				H_CONTROL_CA_FUNCTION_COLLECT_VPD,
354 				record, list_address, num, 0,
355 				out);
356 }
357 
358 /*
359  * cxl_h_get_fn_error_interrupt - Read the function-wide error data based on an interrupt
360  */
361 long cxl_h_get_fn_error_interrupt(u64 unit_address, u64 *reg)
362 {
363 	return cxl_h_control_function(unit_address,
364 				H_CONTROL_CA_FUNCTION_GET_FUNCTION_ERR_INT,
365 				0, 0, 0, 0, reg);
366 }
367 
368 /*
369  * cxl_h_ack_fn_error_interrupt - Acknowledge function-wide error data
370  *                                based on an interrupt
371  * Parameter1 = value to write to the function-wide error interrupt register
372  */
373 long cxl_h_ack_fn_error_interrupt(u64 unit_address, u64 value)
374 {
375 	return cxl_h_control_function(unit_address,
376 				H_CONTROL_CA_FUNCTION_ACK_FUNCTION_ERR_INT,
377 				value, 0, 0, 0,
378 				NULL);
379 }
380 
381 /*
382  * cxl_h_get_error_log - Retrieve the Platform Log ID (PLID) of
383  *                       an error log
384  */
385 long cxl_h_get_error_log(u64 unit_address, u64 value)
386 {
387 	return cxl_h_control_function(unit_address,
388 				H_CONTROL_CA_FUNCTION_GET_ERROR_LOG,
389 				0, 0, 0, 0,
390 				NULL);
391 }
392 
393 /*
394  * cxl_h_collect_int_info - Collect interrupt info about a coherent
395  *                          platform function after an interrupt occurred.
396  */
397 long cxl_h_collect_int_info(u64 unit_address, u64 process_token,
398 			    struct cxl_irq_info *info)
399 {
400 	long rc;
401 
402 	BUG_ON(sizeof(*info) != sizeof(unsigned long[PLPAR_HCALL9_BUFSIZE]));
403 
404 	rc = plpar_hcall9(H_COLLECT_CA_INT_INFO, (unsigned long *) info,
405 			unit_address, process_token);
406 	_PRINT_MSG(rc, "cxl_h_collect_int_info(%#.16llx, 0x%llx): %li\n",
407 		unit_address, process_token, rc);
408 	trace_cxl_hcall_collect_int_info(unit_address, process_token, rc);
409 
410 	switch (rc) {
411 	case H_SUCCESS:     /* The interrupt info is returned in return registers. */
412 		pr_devel("dsisr:%#llx, dar:%#llx, dsr:%#llx, pid_tid:%#llx, afu_err:%#llx, errstat:%#llx\n",
413 			info->dsisr, info->dar, info->dsr, info->reserved,
414 			info->afu_err, info->errstat);
415 		return 0;
416 	case H_PARAMETER:   /* An incorrect parameter was supplied. */
417 		return -EINVAL;
418 	case H_AUTHORITY:   /* The partition does not have authority to perform this hcall. */
419 	case H_HARDWARE:    /* A hardware event prevented the collection of the interrupt info.*/
420 	case H_STATE:       /* The coherent platform function is not in a valid state to collect interrupt info. */
421 		return -EBUSY;
422 	default:
423 		WARN(1, "Unexpected return code: %lx", rc);
424 		return -EINVAL;
425 	}
426 }
427 
428 /*
429  * cxl_h_control_faults - Control the operation of a coherent platform
430  *                        function after a fault occurs.
431  *
432  * Parameters
433  *    control-mask: value to control the faults
434  *                  looks like PSL_TFC_An shifted >> 32
435  *    reset-mask: mask to control reset of function faults
436  *                Set reset_mask = 1 to reset PSL errors
437  */
438 long cxl_h_control_faults(u64 unit_address, u64 process_token,
439 			  u64 control_mask, u64 reset_mask)
440 {
441 	unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
442 	long rc;
443 
444 	memset(retbuf, 0, sizeof(retbuf));
445 
446 	rc = plpar_hcall(H_CONTROL_CA_FAULTS, retbuf, unit_address,
447 			H_CONTROL_CA_FAULTS_RESPOND_PSL, process_token,
448 			control_mask, reset_mask);
449 	_PRINT_MSG(rc, "cxl_h_control_faults(%#.16llx, 0x%llx, %#llx, %#llx): %li (%#lx)\n",
450 		unit_address, process_token, control_mask, reset_mask,
451 		rc, retbuf[0]);
452 	trace_cxl_hcall_control_faults(unit_address, process_token,
453 				control_mask, reset_mask, retbuf[0], rc);
454 
455 	switch (rc) {
456 	case H_SUCCESS:    /* Faults were successfully controlled for the function. */
457 		return 0;
458 	case H_PARAMETER:  /* An incorrect parameter was supplied. */
459 		return -EINVAL;
460 	case H_HARDWARE:   /* A hardware event prevented the control of faults. */
461 	case H_STATE:      /* The function was in an invalid state. */
462 	case H_AUTHORITY:  /* The partition does not have authority to perform this hcall; the coherent platform facilities may need to be licensed. */
463 		return -EBUSY;
464 	case H_FUNCTION:   /* The function is not supported */
465 	case H_NOT_FOUND:  /* The operation supplied was not valid */
466 		return -EINVAL;
467 	default:
468 		WARN(1, "Unexpected return code: %lx", rc);
469 		return -EINVAL;
470 	}
471 }
472 
473 /*
474  * cxl_h_control_facility - This H_CONTROL_CA_FACILITY hypervisor call
475  *                          allows the partition to manipulate or query
476  *                          certain coherent platform facility behaviors.
477  */
478 static long cxl_h_control_facility(u64 unit_address, u64 op,
479 				   u64 p1, u64 p2, u64 p3, u64 p4, u64 *out)
480 {
481 	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
482 	long rc;
483 
484 	CXL_H9_WAIT_UNTIL_DONE(rc, retbuf, H_CONTROL_CA_FACILITY, unit_address, op, p1, p2, p3, p4);
485 	_PRINT_MSG(rc, "cxl_h_control_facility(%#.16llx, %s(%#llx, %#llx, %#llx, %#llx, R4: %#lx)): %li\n",
486 		unit_address, OP_STR_CONTROL_ADAPTER(op), p1, p2, p3, p4, retbuf[0], rc);
487 	trace_cxl_hcall_control_facility(unit_address, OP_STR_CONTROL_ADAPTER(op), p1, p2, p3, p4, retbuf[0], rc);
488 
489 	switch (rc) {
490 	case H_SUCCESS:       /* The operation is completed for the coherent platform facility */
491 		if (op == H_CONTROL_CA_FACILITY_COLLECT_VPD)
492 			*out = retbuf[0];
493 		return 0;
494 	case H_PARAMETER:     /* An incorrect parameter was supplied. */
495 	case H_FUNCTION:      /* The function is not supported. */
496 	case H_NOT_FOUND:     /* The operation supplied was not valid */
497 	case H_NOT_AVAILABLE: /* The operation cannot be performed because the AFU has not been downloaded */
498 	case H_SG_LIST:       /* An block list entry was invalid */
499 		return -EINVAL;
500 	case H_AUTHORITY:     /* The partition does not have authority to perform this hcall */
501 	case H_RESOURCE:      /* The function has page table mappings for MMIO */
502 	case H_HARDWARE:      /* A hardware event prevented the attach operation */
503 	case H_STATE:         /* The coherent platform facility is not in a valid state */
504 	case H_BUSY:
505 		return -EBUSY;
506 	default:
507 		WARN(1, "Unexpected return code: %lx", rc);
508 		return -EINVAL;
509 	}
510 }
511 
512 /*
513  * cxl_h_reset_adapter - Perform a reset to the coherent platform facility.
514  */
515 long cxl_h_reset_adapter(u64 unit_address)
516 {
517 	return cxl_h_control_facility(unit_address,
518 				H_CONTROL_CA_FACILITY_RESET,
519 				0, 0, 0, 0,
520 				NULL);
521 }
522 
523 /*
524  * cxl_h_collect_vpd - Collect VPD for the coherent platform function.
525  * Parameter1 = 4K naturally aligned real buffer containing block
526  *              list entries
527  * Parameter2 = number of block list entries in the block list, valid
528  *              values are between 0 and 256
529  */
530 long cxl_h_collect_vpd_adapter(u64 unit_address, u64 list_address,
531 			       u64 num, u64 *out)
532 {
533 	return cxl_h_control_facility(unit_address,
534 				H_CONTROL_CA_FACILITY_COLLECT_VPD,
535 				list_address, num, 0, 0,
536 				out);
537 }
538 
539 /*
540  * cxl_h_download_facility - This H_DOWNLOAD_CA_FACILITY
541  *                    hypervisor call provide platform support for
542  *                    downloading a base adapter image to the coherent
543  *                    platform facility, and for validating the entire
544  *                    image after the download.
545  * Parameters
546  *    op: operation to perform to the coherent platform function
547  *      Download: operation = 1, the base image in the coherent platform
548  *                               facility is first erased, and then
549  *                               programmed using the image supplied
550  *                               in the scatter/gather list.
551  *      Validate: operation = 2, the base image in the coherent platform
552  *                               facility is compared with the image
553  *                               supplied in the scatter/gather list.
554  *    list_address: 4K naturally aligned real buffer containing
555  *                  scatter/gather list entries.
556  *    num: number of block list entries in the scatter/gather list.
557  */
558 static long cxl_h_download_facility(u64 unit_address, u64 op,
559 				    u64 list_address, u64 num,
560 				    u64 *out)
561 {
562 	unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
563 	unsigned int delay, total_delay = 0;
564 	u64 token = 0;
565 	long rc;
566 
567 	if (*out != 0)
568 		token = *out;
569 
570 	memset(retbuf, 0, sizeof(retbuf));
571 	while (1) {
572 		rc = plpar_hcall(H_DOWNLOAD_CA_FACILITY, retbuf,
573 				 unit_address, op, list_address, num,
574 				 token);
575 		token = retbuf[0];
576 		if (rc != H_BUSY && !H_IS_LONG_BUSY(rc))
577 			break;
578 
579 		if (rc != H_BUSY) {
580 			delay = get_longbusy_msecs(rc);
581 			total_delay += delay;
582 			if (total_delay > CXL_HCALL_TIMEOUT_DOWNLOAD) {
583 				WARN(1, "Warning: Giving up waiting for CXL hcall "
584 					"%#x after %u msec\n",
585 					H_DOWNLOAD_CA_FACILITY, total_delay);
586 				rc = H_BUSY;
587 				break;
588 			}
589 			msleep(delay);
590 		}
591 	}
592 	_PRINT_MSG(rc, "cxl_h_download_facility(%#.16llx, %s(%#llx, %#llx), %#lx): %li\n",
593 		 unit_address, OP_STR_DOWNLOAD_ADAPTER(op), list_address, num, retbuf[0], rc);
594 	trace_cxl_hcall_download_facility(unit_address, OP_STR_DOWNLOAD_ADAPTER(op), list_address, num, retbuf[0], rc);
595 
596 	switch (rc) {
597 	case H_SUCCESS:       /* The operation is completed for the coherent platform facility */
598 		return 0;
599 	case H_PARAMETER:     /* An incorrect parameter was supplied */
600 	case H_FUNCTION:      /* The function is not supported. */
601 	case H_SG_LIST:       /* An block list entry was invalid */
602 	case H_BAD_DATA:      /* Image verification failed */
603 		return -EINVAL;
604 	case H_AUTHORITY:     /* The partition does not have authority to perform this hcall */
605 	case H_RESOURCE:      /* The function has page table mappings for MMIO */
606 	case H_HARDWARE:      /* A hardware event prevented the attach operation */
607 	case H_STATE:         /* The coherent platform facility is not in a valid state */
608 	case H_BUSY:
609 		return -EBUSY;
610 	case H_CONTINUE:
611 		*out = retbuf[0];
612 		return 1;  /* More data is needed for the complete image */
613 	default:
614 		WARN(1, "Unexpected return code: %lx", rc);
615 		return -EINVAL;
616 	}
617 }
618 
619 /*
620  * cxl_h_download_adapter_image - Download the base image to the coherent
621  *                                platform facility.
622  */
623 long cxl_h_download_adapter_image(u64 unit_address,
624 				  u64 list_address, u64 num,
625 				  u64 *out)
626 {
627 	return cxl_h_download_facility(unit_address,
628 				       H_DOWNLOAD_CA_FACILITY_DOWNLOAD,
629 				       list_address, num, out);
630 }
631 
632 /*
633  * cxl_h_validate_adapter_image - Validate the base image in the coherent
634  *                                platform facility.
635  */
636 long cxl_h_validate_adapter_image(u64 unit_address,
637 				  u64 list_address, u64 num,
638 				  u64 *out)
639 {
640 	return cxl_h_download_facility(unit_address,
641 				       H_DOWNLOAD_CA_FACILITY_VALIDATE,
642 				       list_address, num, out);
643 }
644