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
88static 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
105static char *control_adapter_op_names[] = {
106	"UNKNOWN_OP",		/* 0 undefined */
107	"RESET",		/* 1 */
108	"COLLECT_VPD",		/* 2 */
109};
110
111static char *download_op_names[] = {
112	"UNKNOWN_OP",		/* 0 undefined */
113	"DOWNLOAD",		/* 1 */
114	"VALIDATE",		/* 2 */
115};
116
117static 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
131long 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 */
174long 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 */
205static 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 */
244long 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 */
257long 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 */
270long 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 */
283long 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 */
298long 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 */
318long 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 */
332long 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 */
349long 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 */
361long 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 */
373long 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 */
385long 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 */
397long 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 */
438long 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 */
478static 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 */
515long 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 */
530long 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 */
558static 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 */
623long 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 */
636long 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