13bbfe987SSudeep Holla // SPDX-License-Identifier: GPL-2.0-only
23bbfe987SSudeep Holla /*
33bbfe987SSudeep Holla  * Arm Firmware Framework for ARMv8-A(FFA) interface driver
43bbfe987SSudeep Holla  *
53bbfe987SSudeep Holla  * The Arm FFA specification[1] describes a software architecture to
63bbfe987SSudeep Holla  * leverages the virtualization extension to isolate software images
73bbfe987SSudeep Holla  * provided by an ecosystem of vendors from each other and describes
83bbfe987SSudeep Holla  * interfaces that standardize communication between the various software
93bbfe987SSudeep Holla  * images including communication between images in the Secure world and
103bbfe987SSudeep Holla  * Normal world. Any Hypervisor could use the FFA interfaces to enable
113bbfe987SSudeep Holla  * communication between VMs it manages.
123bbfe987SSudeep Holla  *
133bbfe987SSudeep Holla  * The Hypervisor a.k.a Partition managers in FFA terminology can assign
143bbfe987SSudeep Holla  * system resources(Memory regions, Devices, CPU cycles) to the partitions
153bbfe987SSudeep Holla  * and manage isolation amongst them.
163bbfe987SSudeep Holla  *
173bbfe987SSudeep Holla  * [1] https://developer.arm.com/docs/den0077/latest
183bbfe987SSudeep Holla  *
193bbfe987SSudeep Holla  * Copyright (C) 2021 ARM Ltd.
203bbfe987SSudeep Holla  */
213bbfe987SSudeep Holla 
223bbfe987SSudeep Holla #define DRIVER_NAME "ARM FF-A"
233bbfe987SSudeep Holla #define pr_fmt(fmt) DRIVER_NAME ": " fmt
243bbfe987SSudeep Holla 
253bbfe987SSudeep Holla #include <linux/arm_ffa.h>
263bbfe987SSudeep Holla #include <linux/bitfield.h>
27d0c0bce8SSudeep Holla #include <linux/device.h>
283bbfe987SSudeep Holla #include <linux/io.h>
29d0c0bce8SSudeep Holla #include <linux/kernel.h>
303bbfe987SSudeep Holla #include <linux/module.h>
31cc2195feSSudeep Holla #include <linux/mm.h>
32cc2195feSSudeep Holla #include <linux/scatterlist.h>
333bbfe987SSudeep Holla #include <linux/slab.h>
34d0c0bce8SSudeep Holla #include <linux/uuid.h>
353bbfe987SSudeep Holla 
363bbfe987SSudeep Holla #include "common.h"
373bbfe987SSudeep Holla 
383bbfe987SSudeep Holla #define FFA_DRIVER_VERSION	FFA_VERSION_1_0
393bbfe987SSudeep Holla #define FFA_MIN_VERSION		FFA_VERSION_1_0
403bbfe987SSudeep Holla 
413bbfe987SSudeep Holla #define SENDER_ID_MASK		GENMASK(31, 16)
423bbfe987SSudeep Holla #define RECEIVER_ID_MASK	GENMASK(15, 0)
433bbfe987SSudeep Holla #define SENDER_ID(x)		((u16)(FIELD_GET(SENDER_ID_MASK, (x))))
443bbfe987SSudeep Holla #define RECEIVER_ID(x)		((u16)(FIELD_GET(RECEIVER_ID_MASK, (x))))
453bbfe987SSudeep Holla #define PACK_TARGET_INFO(s, r)		\
463bbfe987SSudeep Holla 	(FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r)))
473bbfe987SSudeep Holla 
48ba684a31SSudeep Holla /*
493bbfe987SSudeep Holla  * Keeping RX TX buffer size as 4K for now
503bbfe987SSudeep Holla  * 64K may be preferred to keep it min a page in 64K PAGE_SIZE config
513bbfe987SSudeep Holla  */
523bbfe987SSudeep Holla #define RXTX_BUFFER_SIZE	SZ_4K
533bbfe987SSudeep Holla 
543bbfe987SSudeep Holla static ffa_fn *invoke_ffa_fn;
553bbfe987SSudeep Holla 
563bbfe987SSudeep Holla static const int ffa_linux_errmap[] = {
573bbfe987SSudeep Holla 	/* better than switch case as long as return value is continuous */
583bbfe987SSudeep Holla 	0,		/* FFA_RET_SUCCESS */
593bbfe987SSudeep Holla 	-EOPNOTSUPP,	/* FFA_RET_NOT_SUPPORTED */
603bbfe987SSudeep Holla 	-EINVAL,	/* FFA_RET_INVALID_PARAMETERS */
613bbfe987SSudeep Holla 	-ENOMEM,	/* FFA_RET_NO_MEMORY */
623bbfe987SSudeep Holla 	-EBUSY,		/* FFA_RET_BUSY */
633bbfe987SSudeep Holla 	-EINTR,		/* FFA_RET_INTERRUPTED */
643bbfe987SSudeep Holla 	-EACCES,	/* FFA_RET_DENIED */
653bbfe987SSudeep Holla 	-EAGAIN,	/* FFA_RET_RETRY */
663bbfe987SSudeep Holla 	-ECANCELED,	/* FFA_RET_ABORTED */
673bbfe987SSudeep Holla };
683bbfe987SSudeep Holla 
ffa_to_linux_errno(int errno)693bbfe987SSudeep Holla static inline int ffa_to_linux_errno(int errno)
703bbfe987SSudeep Holla {
71dd925db6SSudeep Holla 	int err_idx = -errno;
72dd925db6SSudeep Holla 
73dd925db6SSudeep Holla 	if (err_idx >= 0 && err_idx < ARRAY_SIZE(ffa_linux_errmap))
74dd925db6SSudeep Holla 		return ffa_linux_errmap[err_idx];
753bbfe987SSudeep Holla 	return -EINVAL;
763bbfe987SSudeep Holla }
773bbfe987SSudeep Holla 
783bbfe987SSudeep Holla struct ffa_drv_info {
793bbfe987SSudeep Holla 	u32 version;
803bbfe987SSudeep Holla 	u16 vm_id;
813bbfe987SSudeep Holla 	struct mutex rx_lock; /* lock to protect Rx buffer */
823bbfe987SSudeep Holla 	struct mutex tx_lock; /* lock to protect Tx buffer */
833bbfe987SSudeep Holla 	void *rx_buffer;
843bbfe987SSudeep Holla 	void *tx_buffer;
85e57fba91SSudeep Holla 	bool mem_ops_native;
863bbfe987SSudeep Holla };
873bbfe987SSudeep Holla 
883bbfe987SSudeep Holla static struct ffa_drv_info *drv_info;
893bbfe987SSudeep Holla 
908e3f9da6SSudeep Holla /*
918e3f9da6SSudeep Holla  * The driver must be able to support all the versions from the earliest
928e3f9da6SSudeep Holla  * supported FFA_MIN_VERSION to the latest supported FFA_DRIVER_VERSION.
938e3f9da6SSudeep Holla  * The specification states that if firmware supports a FFA implementation
948e3f9da6SSudeep Holla  * that is incompatible with and at a greater version number than specified
958e3f9da6SSudeep Holla  * by the caller(FFA_DRIVER_VERSION passed as parameter to FFA_VERSION),
968e3f9da6SSudeep Holla  * it must return the NOT_SUPPORTED error code.
978e3f9da6SSudeep Holla  */
ffa_compatible_version_find(u32 version)988e3f9da6SSudeep Holla static u32 ffa_compatible_version_find(u32 version)
998e3f9da6SSudeep Holla {
100229d58e3SWill Deacon 	u16 major = FFA_MAJOR_VERSION(version), minor = FFA_MINOR_VERSION(version);
101229d58e3SWill Deacon 	u16 drv_major = FFA_MAJOR_VERSION(FFA_DRIVER_VERSION);
102229d58e3SWill Deacon 	u16 drv_minor = FFA_MINOR_VERSION(FFA_DRIVER_VERSION);
1038e3f9da6SSudeep Holla 
1048e3f9da6SSudeep Holla 	if ((major < drv_major) || (major == drv_major && minor <= drv_minor))
1058e3f9da6SSudeep Holla 		return version;
1068e3f9da6SSudeep Holla 
1078e3f9da6SSudeep Holla 	pr_info("Firmware version higher than driver version, downgrading\n");
1088e3f9da6SSudeep Holla 	return FFA_DRIVER_VERSION;
1098e3f9da6SSudeep Holla }
1108e3f9da6SSudeep Holla 
ffa_version_check(u32 * version)1113bbfe987SSudeep Holla static int ffa_version_check(u32 *version)
1123bbfe987SSudeep Holla {
1133bbfe987SSudeep Holla 	ffa_value_t ver;
1143bbfe987SSudeep Holla 
1153bbfe987SSudeep Holla 	invoke_ffa_fn((ffa_value_t){
1163bbfe987SSudeep Holla 		      .a0 = FFA_VERSION, .a1 = FFA_DRIVER_VERSION,
1173bbfe987SSudeep Holla 		      }, &ver);
1183bbfe987SSudeep Holla 
1193bbfe987SSudeep Holla 	if (ver.a0 == FFA_RET_NOT_SUPPORTED) {
1203bbfe987SSudeep Holla 		pr_info("FFA_VERSION returned not supported\n");
1213bbfe987SSudeep Holla 		return -EOPNOTSUPP;
1223bbfe987SSudeep Holla 	}
1233bbfe987SSudeep Holla 
1248e3f9da6SSudeep Holla 	if (ver.a0 < FFA_MIN_VERSION) {
1258e3f9da6SSudeep Holla 		pr_err("Incompatible v%d.%d! Earliest supported v%d.%d\n",
126229d58e3SWill Deacon 		       FFA_MAJOR_VERSION(ver.a0), FFA_MINOR_VERSION(ver.a0),
127229d58e3SWill Deacon 		       FFA_MAJOR_VERSION(FFA_MIN_VERSION),
128229d58e3SWill Deacon 		       FFA_MINOR_VERSION(FFA_MIN_VERSION));
1293bbfe987SSudeep Holla 		return -EINVAL;
1303bbfe987SSudeep Holla 	}
1313bbfe987SSudeep Holla 
132229d58e3SWill Deacon 	pr_info("Driver version %d.%d\n", FFA_MAJOR_VERSION(FFA_DRIVER_VERSION),
133229d58e3SWill Deacon 		FFA_MINOR_VERSION(FFA_DRIVER_VERSION));
134229d58e3SWill Deacon 	pr_info("Firmware version %d.%d found\n", FFA_MAJOR_VERSION(ver.a0),
135229d58e3SWill Deacon 		FFA_MINOR_VERSION(ver.a0));
1368e3f9da6SSudeep Holla 	*version = ffa_compatible_version_find(ver.a0);
1378e3f9da6SSudeep Holla 
1383bbfe987SSudeep Holla 	return 0;
1393bbfe987SSudeep Holla }
1403bbfe987SSudeep Holla 
ffa_rx_release(void)141d0c0bce8SSudeep Holla static int ffa_rx_release(void)
142d0c0bce8SSudeep Holla {
143d0c0bce8SSudeep Holla 	ffa_value_t ret;
144d0c0bce8SSudeep Holla 
145d0c0bce8SSudeep Holla 	invoke_ffa_fn((ffa_value_t){
146d0c0bce8SSudeep Holla 		      .a0 = FFA_RX_RELEASE,
147d0c0bce8SSudeep Holla 		      }, &ret);
148d0c0bce8SSudeep Holla 
149d0c0bce8SSudeep Holla 	if (ret.a0 == FFA_ERROR)
150d0c0bce8SSudeep Holla 		return ffa_to_linux_errno((int)ret.a2);
151d0c0bce8SSudeep Holla 
152d0c0bce8SSudeep Holla 	/* check for ret.a0 == FFA_RX_RELEASE ? */
153d0c0bce8SSudeep Holla 
154d0c0bce8SSudeep Holla 	return 0;
155d0c0bce8SSudeep Holla }
156d0c0bce8SSudeep Holla 
ffa_rxtx_map(phys_addr_t tx_buf,phys_addr_t rx_buf,u32 pg_cnt)1573bbfe987SSudeep Holla static int ffa_rxtx_map(phys_addr_t tx_buf, phys_addr_t rx_buf, u32 pg_cnt)
1583bbfe987SSudeep Holla {
1593bbfe987SSudeep Holla 	ffa_value_t ret;
1603bbfe987SSudeep Holla 
1613bbfe987SSudeep Holla 	invoke_ffa_fn((ffa_value_t){
1623bbfe987SSudeep Holla 		      .a0 = FFA_FN_NATIVE(RXTX_MAP),
1633bbfe987SSudeep Holla 		      .a1 = tx_buf, .a2 = rx_buf, .a3 = pg_cnt,
1643bbfe987SSudeep Holla 		      }, &ret);
1653bbfe987SSudeep Holla 
1663bbfe987SSudeep Holla 	if (ret.a0 == FFA_ERROR)
1673bbfe987SSudeep Holla 		return ffa_to_linux_errno((int)ret.a2);
1683bbfe987SSudeep Holla 
1693bbfe987SSudeep Holla 	return 0;
1703bbfe987SSudeep Holla }
1713bbfe987SSudeep Holla 
ffa_rxtx_unmap(u16 vm_id)1723bbfe987SSudeep Holla static int ffa_rxtx_unmap(u16 vm_id)
1733bbfe987SSudeep Holla {
1743bbfe987SSudeep Holla 	ffa_value_t ret;
1753bbfe987SSudeep Holla 
1763bbfe987SSudeep Holla 	invoke_ffa_fn((ffa_value_t){
1773bbfe987SSudeep Holla 		      .a0 = FFA_RXTX_UNMAP, .a1 = PACK_TARGET_INFO(vm_id, 0),
1783bbfe987SSudeep Holla 		      }, &ret);
1793bbfe987SSudeep Holla 
1803bbfe987SSudeep Holla 	if (ret.a0 == FFA_ERROR)
1813bbfe987SSudeep Holla 		return ffa_to_linux_errno((int)ret.a2);
1823bbfe987SSudeep Holla 
1833bbfe987SSudeep Holla 	return 0;
1843bbfe987SSudeep Holla }
1853bbfe987SSudeep Holla 
186bb1be749SSudeep Holla #define PARTITION_INFO_GET_RETURN_COUNT_ONLY	BIT(0)
187bb1be749SSudeep Holla 
188d0c0bce8SSudeep Holla /* buffer must be sizeof(struct ffa_partition_info) * num_partitions */
189d0c0bce8SSudeep Holla static int
__ffa_partition_info_get(u32 uuid0,u32 uuid1,u32 uuid2,u32 uuid3,struct ffa_partition_info * buffer,int num_partitions)190d0c0bce8SSudeep Holla __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
191d0c0bce8SSudeep Holla 			 struct ffa_partition_info *buffer, int num_partitions)
192d0c0bce8SSudeep Holla {
193bb1be749SSudeep Holla 	int idx, count, flags = 0, sz, buf_sz;
194d0c0bce8SSudeep Holla 	ffa_value_t partition_info;
195d0c0bce8SSudeep Holla 
196c6e04536SSudeep Holla 	if (drv_info->version > FFA_VERSION_1_0 &&
197c6e04536SSudeep Holla 	    (!buffer || !num_partitions)) /* Just get the count for now */
198bb1be749SSudeep Holla 		flags = PARTITION_INFO_GET_RETURN_COUNT_ONLY;
199bb1be749SSudeep Holla 
200d0c0bce8SSudeep Holla 	mutex_lock(&drv_info->rx_lock);
201d0c0bce8SSudeep Holla 	invoke_ffa_fn((ffa_value_t){
202d0c0bce8SSudeep Holla 		      .a0 = FFA_PARTITION_INFO_GET,
203d0c0bce8SSudeep Holla 		      .a1 = uuid0, .a2 = uuid1, .a3 = uuid2, .a4 = uuid3,
204bb1be749SSudeep Holla 		      .a5 = flags,
205d0c0bce8SSudeep Holla 		      }, &partition_info);
206d0c0bce8SSudeep Holla 
207d0c0bce8SSudeep Holla 	if (partition_info.a0 == FFA_ERROR) {
208d0c0bce8SSudeep Holla 		mutex_unlock(&drv_info->rx_lock);
209d0c0bce8SSudeep Holla 		return ffa_to_linux_errno((int)partition_info.a2);
210d0c0bce8SSudeep Holla 	}
211d0c0bce8SSudeep Holla 
212d0c0bce8SSudeep Holla 	count = partition_info.a2;
213d0c0bce8SSudeep Holla 
214bb1be749SSudeep Holla 	if (drv_info->version > FFA_VERSION_1_0) {
215bb1be749SSudeep Holla 		buf_sz = sz = partition_info.a3;
216bb1be749SSudeep Holla 		if (sz > sizeof(*buffer))
217bb1be749SSudeep Holla 			buf_sz = sizeof(*buffer);
218bb1be749SSudeep Holla 	} else {
219bb1be749SSudeep Holla 		/* FFA_VERSION_1_0 lacks size in the response */
220bb1be749SSudeep Holla 		buf_sz = sz = 8;
221bb1be749SSudeep Holla 	}
222bb1be749SSudeep Holla 
223d0c0bce8SSudeep Holla 	if (buffer && count <= num_partitions)
224bb1be749SSudeep Holla 		for (idx = 0; idx < count; idx++)
225bb1be749SSudeep Holla 			memcpy(buffer + idx, drv_info->rx_buffer + idx * sz,
226bb1be749SSudeep Holla 			       buf_sz);
227d0c0bce8SSudeep Holla 
228d0c0bce8SSudeep Holla 	ffa_rx_release();
229d0c0bce8SSudeep Holla 
230d0c0bce8SSudeep Holla 	mutex_unlock(&drv_info->rx_lock);
231d0c0bce8SSudeep Holla 
232d0c0bce8SSudeep Holla 	return count;
233d0c0bce8SSudeep Holla }
234d0c0bce8SSudeep Holla 
235d0c0bce8SSudeep Holla /* buffer is allocated and caller must free the same if returned count > 0 */
236d0c0bce8SSudeep Holla static int
ffa_partition_probe(const uuid_t * uuid,struct ffa_partition_info ** buffer)237d0c0bce8SSudeep Holla ffa_partition_probe(const uuid_t *uuid, struct ffa_partition_info **buffer)
238d0c0bce8SSudeep Holla {
239d0c0bce8SSudeep Holla 	int count;
240d0c0bce8SSudeep Holla 	u32 uuid0_4[4];
241d0c0bce8SSudeep Holla 	struct ffa_partition_info *pbuf;
242d0c0bce8SSudeep Holla 
243d0c0bce8SSudeep Holla 	export_uuid((u8 *)uuid0_4, uuid);
244d0c0bce8SSudeep Holla 	count = __ffa_partition_info_get(uuid0_4[0], uuid0_4[1], uuid0_4[2],
245d0c0bce8SSudeep Holla 					 uuid0_4[3], NULL, 0);
246d0c0bce8SSudeep Holla 	if (count <= 0)
247d0c0bce8SSudeep Holla 		return count;
248d0c0bce8SSudeep Holla 
249d0c0bce8SSudeep Holla 	pbuf = kcalloc(count, sizeof(*pbuf), GFP_KERNEL);
250d0c0bce8SSudeep Holla 	if (!pbuf)
251d0c0bce8SSudeep Holla 		return -ENOMEM;
252d0c0bce8SSudeep Holla 
253d0c0bce8SSudeep Holla 	count = __ffa_partition_info_get(uuid0_4[0], uuid0_4[1], uuid0_4[2],
254d0c0bce8SSudeep Holla 					 uuid0_4[3], pbuf, count);
255d0c0bce8SSudeep Holla 	if (count <= 0)
256d0c0bce8SSudeep Holla 		kfree(pbuf);
257d0c0bce8SSudeep Holla 	else
258d0c0bce8SSudeep Holla 		*buffer = pbuf;
259d0c0bce8SSudeep Holla 
260d0c0bce8SSudeep Holla 	return count;
261d0c0bce8SSudeep Holla }
262d0c0bce8SSudeep Holla 
2633bbfe987SSudeep Holla #define VM_ID_MASK	GENMASK(15, 0)
ffa_id_get(u16 * vm_id)2643bbfe987SSudeep Holla static int ffa_id_get(u16 *vm_id)
2653bbfe987SSudeep Holla {
2663bbfe987SSudeep Holla 	ffa_value_t id;
2673bbfe987SSudeep Holla 
2683bbfe987SSudeep Holla 	invoke_ffa_fn((ffa_value_t){
2693bbfe987SSudeep Holla 		      .a0 = FFA_ID_GET,
2703bbfe987SSudeep Holla 		      }, &id);
2713bbfe987SSudeep Holla 
2723bbfe987SSudeep Holla 	if (id.a0 == FFA_ERROR)
2733bbfe987SSudeep Holla 		return ffa_to_linux_errno((int)id.a2);
2743bbfe987SSudeep Holla 
2753bbfe987SSudeep Holla 	*vm_id = FIELD_GET(VM_ID_MASK, (id.a2));
2763bbfe987SSudeep Holla 
2773bbfe987SSudeep Holla 	return 0;
2783bbfe987SSudeep Holla }
2793bbfe987SSudeep Holla 
ffa_msg_send_direct_req(u16 src_id,u16 dst_id,bool mode_32bit,struct ffa_send_direct_data * data)280d0c0bce8SSudeep Holla static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
281d0c0bce8SSudeep Holla 				   struct ffa_send_direct_data *data)
282d0c0bce8SSudeep Holla {
283d0c0bce8SSudeep Holla 	u32 req_id, resp_id, src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
284d0c0bce8SSudeep Holla 	ffa_value_t ret;
285d0c0bce8SSudeep Holla 
286d0c0bce8SSudeep Holla 	if (mode_32bit) {
287d0c0bce8SSudeep Holla 		req_id = FFA_MSG_SEND_DIRECT_REQ;
288d0c0bce8SSudeep Holla 		resp_id = FFA_MSG_SEND_DIRECT_RESP;
289d0c0bce8SSudeep Holla 	} else {
290d0c0bce8SSudeep Holla 		req_id = FFA_FN_NATIVE(MSG_SEND_DIRECT_REQ);
291d0c0bce8SSudeep Holla 		resp_id = FFA_FN_NATIVE(MSG_SEND_DIRECT_RESP);
292d0c0bce8SSudeep Holla 	}
293d0c0bce8SSudeep Holla 
294d0c0bce8SSudeep Holla 	invoke_ffa_fn((ffa_value_t){
295d0c0bce8SSudeep Holla 		      .a0 = req_id, .a1 = src_dst_ids, .a2 = 0,
296d0c0bce8SSudeep Holla 		      .a3 = data->data0, .a4 = data->data1, .a5 = data->data2,
297d0c0bce8SSudeep Holla 		      .a6 = data->data3, .a7 = data->data4,
298d0c0bce8SSudeep Holla 		      }, &ret);
299d0c0bce8SSudeep Holla 
300d0c0bce8SSudeep Holla 	while (ret.a0 == FFA_INTERRUPT)
301d0c0bce8SSudeep Holla 		invoke_ffa_fn((ffa_value_t){
302d0c0bce8SSudeep Holla 			      .a0 = FFA_RUN, .a1 = ret.a1,
303d0c0bce8SSudeep Holla 			      }, &ret);
304d0c0bce8SSudeep Holla 
305d0c0bce8SSudeep Holla 	if (ret.a0 == FFA_ERROR)
306d0c0bce8SSudeep Holla 		return ffa_to_linux_errno((int)ret.a2);
307d0c0bce8SSudeep Holla 
308d0c0bce8SSudeep Holla 	if (ret.a0 == resp_id) {
309d0c0bce8SSudeep Holla 		data->data0 = ret.a3;
310d0c0bce8SSudeep Holla 		data->data1 = ret.a4;
311d0c0bce8SSudeep Holla 		data->data2 = ret.a5;
312d0c0bce8SSudeep Holla 		data->data3 = ret.a6;
313d0c0bce8SSudeep Holla 		data->data4 = ret.a7;
314d0c0bce8SSudeep Holla 		return 0;
315d0c0bce8SSudeep Holla 	}
316d0c0bce8SSudeep Holla 
317d0c0bce8SSudeep Holla 	return -EINVAL;
318d0c0bce8SSudeep Holla }
319d0c0bce8SSudeep Holla 
ffa_mem_first_frag(u32 func_id,phys_addr_t buf,u32 buf_sz,u32 frag_len,u32 len,u64 * handle)320cc2195feSSudeep Holla static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
321cc2195feSSudeep Holla 			      u32 frag_len, u32 len, u64 *handle)
322cc2195feSSudeep Holla {
323cc2195feSSudeep Holla 	ffa_value_t ret;
324cc2195feSSudeep Holla 
325cc2195feSSudeep Holla 	invoke_ffa_fn((ffa_value_t){
326cc2195feSSudeep Holla 		      .a0 = func_id, .a1 = len, .a2 = frag_len,
327cc2195feSSudeep Holla 		      .a3 = buf, .a4 = buf_sz,
328cc2195feSSudeep Holla 		      }, &ret);
329cc2195feSSudeep Holla 
330cc2195feSSudeep Holla 	while (ret.a0 == FFA_MEM_OP_PAUSE)
331cc2195feSSudeep Holla 		invoke_ffa_fn((ffa_value_t){
332cc2195feSSudeep Holla 			      .a0 = FFA_MEM_OP_RESUME,
333cc2195feSSudeep Holla 			      .a1 = ret.a1, .a2 = ret.a2,
334cc2195feSSudeep Holla 			      }, &ret);
335cc2195feSSudeep Holla 
336cc2195feSSudeep Holla 	if (ret.a0 == FFA_ERROR)
337cc2195feSSudeep Holla 		return ffa_to_linux_errno((int)ret.a2);
338cc2195feSSudeep Holla 
339987756f6SMarc Bonnici 	if (ret.a0 == FFA_SUCCESS) {
340cc2195feSSudeep Holla 		if (handle)
341cc2195feSSudeep Holla 			*handle = PACK_HANDLE(ret.a2, ret.a3);
342987756f6SMarc Bonnici 	} else if (ret.a0 == FFA_MEM_FRAG_RX) {
343987756f6SMarc Bonnici 		if (handle)
344987756f6SMarc Bonnici 			*handle = PACK_HANDLE(ret.a1, ret.a2);
345987756f6SMarc Bonnici 	} else {
346987756f6SMarc Bonnici 		return -EOPNOTSUPP;
347987756f6SMarc Bonnici 	}
348cc2195feSSudeep Holla 
349cc2195feSSudeep Holla 	return frag_len;
350cc2195feSSudeep Holla }
351cc2195feSSudeep Holla 
ffa_mem_next_frag(u64 handle,u32 frag_len)352cc2195feSSudeep Holla static int ffa_mem_next_frag(u64 handle, u32 frag_len)
353cc2195feSSudeep Holla {
354cc2195feSSudeep Holla 	ffa_value_t ret;
355cc2195feSSudeep Holla 
356cc2195feSSudeep Holla 	invoke_ffa_fn((ffa_value_t){
357cc2195feSSudeep Holla 		      .a0 = FFA_MEM_FRAG_TX,
358cc2195feSSudeep Holla 		      .a1 = HANDLE_LOW(handle), .a2 = HANDLE_HIGH(handle),
359cc2195feSSudeep Holla 		      .a3 = frag_len,
360cc2195feSSudeep Holla 		      }, &ret);
361cc2195feSSudeep Holla 
362cc2195feSSudeep Holla 	while (ret.a0 == FFA_MEM_OP_PAUSE)
363cc2195feSSudeep Holla 		invoke_ffa_fn((ffa_value_t){
364cc2195feSSudeep Holla 			      .a0 = FFA_MEM_OP_RESUME,
365cc2195feSSudeep Holla 			      .a1 = ret.a1, .a2 = ret.a2,
366cc2195feSSudeep Holla 			      }, &ret);
367cc2195feSSudeep Holla 
368cc2195feSSudeep Holla 	if (ret.a0 == FFA_ERROR)
369cc2195feSSudeep Holla 		return ffa_to_linux_errno((int)ret.a2);
370cc2195feSSudeep Holla 
371987756f6SMarc Bonnici 	if (ret.a0 == FFA_MEM_FRAG_RX)
372cc2195feSSudeep Holla 		return ret.a3;
373987756f6SMarc Bonnici 	else if (ret.a0 == FFA_SUCCESS)
374987756f6SMarc Bonnici 		return 0;
375987756f6SMarc Bonnici 
376987756f6SMarc Bonnici 	return -EOPNOTSUPP;
377cc2195feSSudeep Holla }
378cc2195feSSudeep Holla 
379cc2195feSSudeep Holla static int
ffa_transmit_fragment(u32 func_id,phys_addr_t buf,u32 buf_sz,u32 frag_len,u32 len,u64 * handle,bool first)380cc2195feSSudeep Holla ffa_transmit_fragment(u32 func_id, phys_addr_t buf, u32 buf_sz, u32 frag_len,
381cc2195feSSudeep Holla 		      u32 len, u64 *handle, bool first)
382cc2195feSSudeep Holla {
383cc2195feSSudeep Holla 	if (!first)
384cc2195feSSudeep Holla 		return ffa_mem_next_frag(*handle, frag_len);
385cc2195feSSudeep Holla 
386cc2195feSSudeep Holla 	return ffa_mem_first_frag(func_id, buf, buf_sz, frag_len, len, handle);
387cc2195feSSudeep Holla }
388cc2195feSSudeep Holla 
ffa_get_num_pages_sg(struct scatterlist * sg)389cc2195feSSudeep Holla static u32 ffa_get_num_pages_sg(struct scatterlist *sg)
390cc2195feSSudeep Holla {
391cc2195feSSudeep Holla 	u32 num_pages = 0;
392cc2195feSSudeep Holla 
393cc2195feSSudeep Holla 	do {
394cc2195feSSudeep Holla 		num_pages += sg->length / FFA_PAGE_SIZE;
395cc2195feSSudeep Holla 	} while ((sg = sg_next(sg)));
396cc2195feSSudeep Holla 
397cc2195feSSudeep Holla 	return num_pages;
398cc2195feSSudeep Holla }
399cc2195feSSudeep Holla 
ffa_memory_attributes_get(u32 func_id)4009dda1178SSudeep Holla static u8 ffa_memory_attributes_get(u32 func_id)
4019dda1178SSudeep Holla {
4029dda1178SSudeep Holla 	/*
4039dda1178SSudeep Holla 	 * For the memory lend or donate operation, if the receiver is a PE or
4049dda1178SSudeep Holla 	 * a proxy endpoint, the owner/sender must not specify the attributes
4059dda1178SSudeep Holla 	 */
4069dda1178SSudeep Holla 	if (func_id == FFA_FN_NATIVE(MEM_LEND) ||
4079dda1178SSudeep Holla 	    func_id == FFA_MEM_LEND)
4089dda1178SSudeep Holla 		return 0;
4099dda1178SSudeep Holla 
4109dda1178SSudeep Holla 	return FFA_MEM_NORMAL | FFA_MEM_WRITE_BACK | FFA_MEM_INNER_SHAREABLE;
4119dda1178SSudeep Holla }
4129dda1178SSudeep Holla 
413cc2195feSSudeep Holla static int
ffa_setup_and_transmit(u32 func_id,void * buffer,u32 max_fragsize,struct ffa_mem_ops_args * args)414cc2195feSSudeep Holla ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
415cc2195feSSudeep Holla 		       struct ffa_mem_ops_args *args)
416cc2195feSSudeep Holla {
417cc2195feSSudeep Holla 	int rc = 0;
418cc2195feSSudeep Holla 	bool first = true;
419cc2195feSSudeep Holla 	phys_addr_t addr = 0;
420cc2195feSSudeep Holla 	struct ffa_composite_mem_region *composite;
421cc2195feSSudeep Holla 	struct ffa_mem_region_addr_range *constituents;
422cc2195feSSudeep Holla 	struct ffa_mem_region_attributes *ep_mem_access;
423cc2195feSSudeep Holla 	struct ffa_mem_region *mem_region = buffer;
424cc2195feSSudeep Holla 	u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg);
425cc2195feSSudeep Holla 
426cc2195feSSudeep Holla 	mem_region->tag = args->tag;
427cc2195feSSudeep Holla 	mem_region->flags = args->flags;
428cc2195feSSudeep Holla 	mem_region->sender_id = drv_info->vm_id;
4299dda1178SSudeep Holla 	mem_region->attributes = ffa_memory_attributes_get(func_id);
430cc2195feSSudeep Holla 	ep_mem_access = &mem_region->ep_mem_access[0];
431cc2195feSSudeep Holla 
432cc2195feSSudeep Holla 	for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) {
433cc2195feSSudeep Holla 		ep_mem_access->receiver = args->attrs[idx].receiver;
434cc2195feSSudeep Holla 		ep_mem_access->attrs = args->attrs[idx].attrs;
435cc2195feSSudeep Holla 		ep_mem_access->composite_off = COMPOSITE_OFFSET(args->nattrs);
436111a833dSSudeep Holla 		ep_mem_access->flag = 0;
437111a833dSSudeep Holla 		ep_mem_access->reserved = 0;
438cc2195feSSudeep Holla 	}
4393aa0519aSBalint Dobszay 	mem_region->handle = 0;
440111a833dSSudeep Holla 	mem_region->reserved_0 = 0;
441111a833dSSudeep Holla 	mem_region->reserved_1 = 0;
442cc2195feSSudeep Holla 	mem_region->ep_count = args->nattrs;
443cc2195feSSudeep Holla 
444cc2195feSSudeep Holla 	composite = buffer + COMPOSITE_OFFSET(args->nattrs);
445cc2195feSSudeep Holla 	composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg);
446cc2195feSSudeep Holla 	composite->addr_range_cnt = num_entries;
447111a833dSSudeep Holla 	composite->reserved = 0;
448cc2195feSSudeep Holla 
449cc2195feSSudeep Holla 	length = COMPOSITE_CONSTITUENTS_OFFSET(args->nattrs, num_entries);
450cc2195feSSudeep Holla 	frag_len = COMPOSITE_CONSTITUENTS_OFFSET(args->nattrs, 0);
451cc2195feSSudeep Holla 	if (frag_len > max_fragsize)
452cc2195feSSudeep Holla 		return -ENXIO;
453cc2195feSSudeep Holla 
454cc2195feSSudeep Holla 	if (!args->use_txbuf) {
455cc2195feSSudeep Holla 		addr = virt_to_phys(buffer);
456cc2195feSSudeep Holla 		buf_sz = max_fragsize / FFA_PAGE_SIZE;
457cc2195feSSudeep Holla 	}
458cc2195feSSudeep Holla 
459cc2195feSSudeep Holla 	constituents = buffer + frag_len;
460cc2195feSSudeep Holla 	idx = 0;
461cc2195feSSudeep Holla 	do {
462cc2195feSSudeep Holla 		if (frag_len == max_fragsize) {
463cc2195feSSudeep Holla 			rc = ffa_transmit_fragment(func_id, addr, buf_sz,
464cc2195feSSudeep Holla 						   frag_len, length,
465cc2195feSSudeep Holla 						   &args->g_handle, first);
466cc2195feSSudeep Holla 			if (rc < 0)
467cc2195feSSudeep Holla 				return -ENXIO;
468cc2195feSSudeep Holla 
469cc2195feSSudeep Holla 			first = false;
470cc2195feSSudeep Holla 			idx = 0;
471cc2195feSSudeep Holla 			frag_len = 0;
472cc2195feSSudeep Holla 			constituents = buffer;
473cc2195feSSudeep Holla 		}
474cc2195feSSudeep Holla 
475cc2195feSSudeep Holla 		if ((void *)constituents - buffer > max_fragsize) {
476cc2195feSSudeep Holla 			pr_err("Memory Region Fragment > Tx Buffer size\n");
477cc2195feSSudeep Holla 			return -EFAULT;
478cc2195feSSudeep Holla 		}
479cc2195feSSudeep Holla 
480cc2195feSSudeep Holla 		constituents->address = sg_phys(args->sg);
481cc2195feSSudeep Holla 		constituents->pg_cnt = args->sg->length / FFA_PAGE_SIZE;
482111a833dSSudeep Holla 		constituents->reserved = 0;
483cc2195feSSudeep Holla 		constituents++;
484cc2195feSSudeep Holla 		frag_len += sizeof(struct ffa_mem_region_addr_range);
485cc2195feSSudeep Holla 	} while ((args->sg = sg_next(args->sg)));
486cc2195feSSudeep Holla 
487cc2195feSSudeep Holla 	return ffa_transmit_fragment(func_id, addr, buf_sz, frag_len,
488cc2195feSSudeep Holla 				     length, &args->g_handle, first);
489cc2195feSSudeep Holla }
490cc2195feSSudeep Holla 
ffa_memory_ops(u32 func_id,struct ffa_mem_ops_args * args)491cc2195feSSudeep Holla static int ffa_memory_ops(u32 func_id, struct ffa_mem_ops_args *args)
492cc2195feSSudeep Holla {
493cc2195feSSudeep Holla 	int ret;
494cc2195feSSudeep Holla 	void *buffer;
495cc2195feSSudeep Holla 
496cc2195feSSudeep Holla 	if (!args->use_txbuf) {
497cc2195feSSudeep Holla 		buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL);
498cc2195feSSudeep Holla 		if (!buffer)
499cc2195feSSudeep Holla 			return -ENOMEM;
500cc2195feSSudeep Holla 	} else {
501cc2195feSSudeep Holla 		buffer = drv_info->tx_buffer;
502cc2195feSSudeep Holla 		mutex_lock(&drv_info->tx_lock);
503cc2195feSSudeep Holla 	}
504cc2195feSSudeep Holla 
505cc2195feSSudeep Holla 	ret = ffa_setup_and_transmit(func_id, buffer, RXTX_BUFFER_SIZE, args);
506cc2195feSSudeep Holla 
507cc2195feSSudeep Holla 	if (args->use_txbuf)
508cc2195feSSudeep Holla 		mutex_unlock(&drv_info->tx_lock);
509cc2195feSSudeep Holla 	else
510cc2195feSSudeep Holla 		free_pages_exact(buffer, RXTX_BUFFER_SIZE);
511cc2195feSSudeep Holla 
512cc2195feSSudeep Holla 	return ret < 0 ? ret : 0;
513cc2195feSSudeep Holla }
514cc2195feSSudeep Holla 
ffa_memory_reclaim(u64 g_handle,u32 flags)515cc2195feSSudeep Holla static int ffa_memory_reclaim(u64 g_handle, u32 flags)
516cc2195feSSudeep Holla {
517cc2195feSSudeep Holla 	ffa_value_t ret;
518cc2195feSSudeep Holla 
519cc2195feSSudeep Holla 	invoke_ffa_fn((ffa_value_t){
520cc2195feSSudeep Holla 		      .a0 = FFA_MEM_RECLAIM,
521cc2195feSSudeep Holla 		      .a1 = HANDLE_LOW(g_handle), .a2 = HANDLE_HIGH(g_handle),
522cc2195feSSudeep Holla 		      .a3 = flags,
523cc2195feSSudeep Holla 		      }, &ret);
524cc2195feSSudeep Holla 
525cc2195feSSudeep Holla 	if (ret.a0 == FFA_ERROR)
526cc2195feSSudeep Holla 		return ffa_to_linux_errno((int)ret.a2);
527cc2195feSSudeep Holla 
528cc2195feSSudeep Holla 	return 0;
529cc2195feSSudeep Holla }
530cc2195feSSudeep Holla 
ffa_features(u32 func_feat_id,u32 input_props,u32 * if_props_1,u32 * if_props_2)531cb1f4c2cSSudeep Holla static int ffa_features(u32 func_feat_id, u32 input_props,
532cb1f4c2cSSudeep Holla 			u32 *if_props_1, u32 *if_props_2)
533cb1f4c2cSSudeep Holla {
534cb1f4c2cSSudeep Holla 	ffa_value_t id;
535cb1f4c2cSSudeep Holla 
536cb1f4c2cSSudeep Holla 	if (!ARM_SMCCC_IS_FAST_CALL(func_feat_id) && input_props) {
537cb1f4c2cSSudeep Holla 		pr_err("%s: Invalid Parameters: %x, %x", __func__,
538cb1f4c2cSSudeep Holla 		       func_feat_id, input_props);
539cb1f4c2cSSudeep Holla 		return ffa_to_linux_errno(FFA_RET_INVALID_PARAMETERS);
540cb1f4c2cSSudeep Holla 	}
541cb1f4c2cSSudeep Holla 
542cb1f4c2cSSudeep Holla 	invoke_ffa_fn((ffa_value_t){
543cb1f4c2cSSudeep Holla 		.a0 = FFA_FEATURES, .a1 = func_feat_id, .a2 = input_props,
544cb1f4c2cSSudeep Holla 		}, &id);
545cb1f4c2cSSudeep Holla 
546cb1f4c2cSSudeep Holla 	if (id.a0 == FFA_ERROR)
547cb1f4c2cSSudeep Holla 		return ffa_to_linux_errno((int)id.a2);
548cb1f4c2cSSudeep Holla 
549cb1f4c2cSSudeep Holla 	if (if_props_1)
550cb1f4c2cSSudeep Holla 		*if_props_1 = id.a2;
551cb1f4c2cSSudeep Holla 	if (if_props_2)
552cb1f4c2cSSudeep Holla 		*if_props_2 = id.a3;
553cb1f4c2cSSudeep Holla 
554cb1f4c2cSSudeep Holla 	return 0;
555cb1f4c2cSSudeep Holla }
556cb1f4c2cSSudeep Holla 
ffa_set_up_mem_ops_native_flag(void)557e57fba91SSudeep Holla static void ffa_set_up_mem_ops_native_flag(void)
558e57fba91SSudeep Holla {
559e57fba91SSudeep Holla 	if (!ffa_features(FFA_FN_NATIVE(MEM_LEND), 0, NULL, NULL) ||
560e57fba91SSudeep Holla 	    !ffa_features(FFA_FN_NATIVE(MEM_SHARE), 0, NULL, NULL))
561e57fba91SSudeep Holla 		drv_info->mem_ops_native = true;
562e57fba91SSudeep Holla }
563e57fba91SSudeep Holla 
ffa_api_version_get(void)564d0c0bce8SSudeep Holla static u32 ffa_api_version_get(void)
565d0c0bce8SSudeep Holla {
566d0c0bce8SSudeep Holla 	return drv_info->version;
567d0c0bce8SSudeep Holla }
568d0c0bce8SSudeep Holla 
ffa_partition_info_get(const char * uuid_str,struct ffa_partition_info * buffer)569d0c0bce8SSudeep Holla static int ffa_partition_info_get(const char *uuid_str,
570d0c0bce8SSudeep Holla 				  struct ffa_partition_info *buffer)
571d0c0bce8SSudeep Holla {
572d0c0bce8SSudeep Holla 	int count;
573d0c0bce8SSudeep Holla 	uuid_t uuid;
574d0c0bce8SSudeep Holla 	struct ffa_partition_info *pbuf;
575d0c0bce8SSudeep Holla 
576d0c0bce8SSudeep Holla 	if (uuid_parse(uuid_str, &uuid)) {
577d0c0bce8SSudeep Holla 		pr_err("invalid uuid (%s)\n", uuid_str);
578d0c0bce8SSudeep Holla 		return -ENODEV;
579d0c0bce8SSudeep Holla 	}
580d0c0bce8SSudeep Holla 
581f3c45c04SSudeep Holla 	count = ffa_partition_probe(&uuid, &pbuf);
582d0c0bce8SSudeep Holla 	if (count <= 0)
583d0c0bce8SSudeep Holla 		return -ENOENT;
584d0c0bce8SSudeep Holla 
585d0c0bce8SSudeep Holla 	memcpy(buffer, pbuf, sizeof(*pbuf) * count);
586d0c0bce8SSudeep Holla 	kfree(pbuf);
587d0c0bce8SSudeep Holla 	return 0;
588d0c0bce8SSudeep Holla }
589d0c0bce8SSudeep Holla 
ffa_mode_32bit_set(struct ffa_device * dev)590106b11b1SSudeep Holla static void ffa_mode_32bit_set(struct ffa_device *dev)
591106b11b1SSudeep Holla {
592*38dd0c29SSudeep Holla 	dev->mode_32bit = true;
593106b11b1SSudeep Holla }
594106b11b1SSudeep Holla 
ffa_sync_send_receive(struct ffa_device * dev,struct ffa_send_direct_data * data)595d0c0bce8SSudeep Holla static int ffa_sync_send_receive(struct ffa_device *dev,
596d0c0bce8SSudeep Holla 				 struct ffa_send_direct_data *data)
597d0c0bce8SSudeep Holla {
598d0c0bce8SSudeep Holla 	return ffa_msg_send_direct_req(drv_info->vm_id, dev->vm_id,
599d0c0bce8SSudeep Holla 				       dev->mode_32bit, data);
600d0c0bce8SSudeep Holla }
601d0c0bce8SSudeep Holla 
ffa_memory_share(struct ffa_mem_ops_args * args)6028c3812c8SSudeep Holla static int ffa_memory_share(struct ffa_mem_ops_args *args)
603cc2195feSSudeep Holla {
604e57fba91SSudeep Holla 	if (drv_info->mem_ops_native)
605cc2195feSSudeep Holla 		return ffa_memory_ops(FFA_FN_NATIVE(MEM_SHARE), args);
606e57fba91SSudeep Holla 
607e57fba91SSudeep Holla 	return ffa_memory_ops(FFA_MEM_SHARE, args);
608cc2195feSSudeep Holla }
609cc2195feSSudeep Holla 
ffa_memory_lend(struct ffa_mem_ops_args * args)6108c3812c8SSudeep Holla static int ffa_memory_lend(struct ffa_mem_ops_args *args)
61182a8daaeSMarc Bonnici {
61282a8daaeSMarc Bonnici 	/* Note that upon a successful MEM_LEND request the caller
61382a8daaeSMarc Bonnici 	 * must ensure that the memory region specified is not accessed
61482a8daaeSMarc Bonnici 	 * until a successful MEM_RECALIM call has been made.
61582a8daaeSMarc Bonnici 	 * On systems with a hypervisor present this will been enforced,
61682a8daaeSMarc Bonnici 	 * however on systems without a hypervisor the responsibility
61782a8daaeSMarc Bonnici 	 * falls to the calling kernel driver to prevent access.
61882a8daaeSMarc Bonnici 	 */
619e57fba91SSudeep Holla 	if (drv_info->mem_ops_native)
62082a8daaeSMarc Bonnici 		return ffa_memory_ops(FFA_FN_NATIVE(MEM_LEND), args);
621e57fba91SSudeep Holla 
622e57fba91SSudeep Holla 	return ffa_memory_ops(FFA_MEM_LEND, args);
62382a8daaeSMarc Bonnici }
62482a8daaeSMarc Bonnici 
6255b0c6328SSudeep Holla static const struct ffa_info_ops ffa_drv_info_ops = {
626d0c0bce8SSudeep Holla 	.api_version_get = ffa_api_version_get,
627d0c0bce8SSudeep Holla 	.partition_info_get = ffa_partition_info_get,
6285b0c6328SSudeep Holla };
6295b0c6328SSudeep Holla 
6305b0c6328SSudeep Holla static const struct ffa_msg_ops ffa_drv_msg_ops = {
631d0c0bce8SSudeep Holla 	.mode_32bit_set = ffa_mode_32bit_set,
632d0c0bce8SSudeep Holla 	.sync_send_receive = ffa_sync_send_receive,
6335b0c6328SSudeep Holla };
6345b0c6328SSudeep Holla 
6355b0c6328SSudeep Holla static const struct ffa_mem_ops ffa_drv_mem_ops = {
636cc2195feSSudeep Holla 	.memory_reclaim = ffa_memory_reclaim,
637cc2195feSSudeep Holla 	.memory_share = ffa_memory_share,
63882a8daaeSMarc Bonnici 	.memory_lend = ffa_memory_lend,
639d0c0bce8SSudeep Holla };
640d0c0bce8SSudeep Holla 
6415b0c6328SSudeep Holla static const struct ffa_ops ffa_drv_ops = {
6425b0c6328SSudeep Holla 	.info_ops = &ffa_drv_info_ops,
6435b0c6328SSudeep Holla 	.msg_ops = &ffa_drv_msg_ops,
6445b0c6328SSudeep Holla 	.mem_ops = &ffa_drv_mem_ops,
6455b0c6328SSudeep Holla };
6465b0c6328SSudeep Holla 
ffa_device_match_uuid(struct ffa_device * ffa_dev,const uuid_t * uuid)647d0c0bce8SSudeep Holla void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid)
648d0c0bce8SSudeep Holla {
649d0c0bce8SSudeep Holla 	int count, idx;
650d0c0bce8SSudeep Holla 	struct ffa_partition_info *pbuf, *tpbuf;
651d0c0bce8SSudeep Holla 
652bb1be749SSudeep Holla 	/*
653bb1be749SSudeep Holla 	 * FF-A v1.1 provides UUID for each partition as part of the discovery
654bb1be749SSudeep Holla 	 * API, the discovered UUID must be populated in the device's UUID and
655bb1be749SSudeep Holla 	 * there is no need to copy the same from the driver table.
656bb1be749SSudeep Holla 	 */
657bb1be749SSudeep Holla 	if (drv_info->version > FFA_VERSION_1_0)
658bb1be749SSudeep Holla 		return;
659bb1be749SSudeep Holla 
660d0c0bce8SSudeep Holla 	count = ffa_partition_probe(uuid, &pbuf);
661d0c0bce8SSudeep Holla 	if (count <= 0)
662d0c0bce8SSudeep Holla 		return;
663d0c0bce8SSudeep Holla 
664d0c0bce8SSudeep Holla 	for (idx = 0, tpbuf = pbuf; idx < count; idx++, tpbuf++)
665d0c0bce8SSudeep Holla 		if (tpbuf->id == ffa_dev->vm_id)
666d0c0bce8SSudeep Holla 			uuid_copy(&ffa_dev->uuid, uuid);
667d0c0bce8SSudeep Holla 	kfree(pbuf);
668d0c0bce8SSudeep Holla }
669d0c0bce8SSudeep Holla 
ffa_setup_partitions(void)670d0c0bce8SSudeep Holla static void ffa_setup_partitions(void)
671d0c0bce8SSudeep Holla {
672d0c0bce8SSudeep Holla 	int count, idx;
673bb1be749SSudeep Holla 	uuid_t uuid;
674d0c0bce8SSudeep Holla 	struct ffa_device *ffa_dev;
675d0c0bce8SSudeep Holla 	struct ffa_partition_info *pbuf, *tpbuf;
676d0c0bce8SSudeep Holla 
677d0c0bce8SSudeep Holla 	count = ffa_partition_probe(&uuid_null, &pbuf);
678d0c0bce8SSudeep Holla 	if (count <= 0) {
679d0c0bce8SSudeep Holla 		pr_info("%s: No partitions found, error %d\n", __func__, count);
680d0c0bce8SSudeep Holla 		return;
681d0c0bce8SSudeep Holla 	}
682d0c0bce8SSudeep Holla 
683d0c0bce8SSudeep Holla 	for (idx = 0, tpbuf = pbuf; idx < count; idx++, tpbuf++) {
684bb1be749SSudeep Holla 		import_uuid(&uuid, (u8 *)tpbuf->uuid);
685bb1be749SSudeep Holla 
686bb1be749SSudeep Holla 		/* Note that if the UUID will be uuid_null, that will require
687d0c0bce8SSudeep Holla 		 * ffa_device_match() to find the UUID of this partition id
688bb1be749SSudeep Holla 		 * with help of ffa_device_match_uuid(). FF-A v1.1 and above
689bb1be749SSudeep Holla 		 * provides UUID here for each partition as part of the
690bb1be749SSudeep Holla 		 * discovery API and the same is passed.
691d0c0bce8SSudeep Holla 		 */
6925b0c6328SSudeep Holla 		ffa_dev = ffa_device_register(&uuid, tpbuf->id, &ffa_drv_ops);
693d0c0bce8SSudeep Holla 		if (!ffa_dev) {
694d0c0bce8SSudeep Holla 			pr_err("%s: failed to register partition ID 0x%x\n",
695d0c0bce8SSudeep Holla 			       __func__, tpbuf->id);
696d0c0bce8SSudeep Holla 			continue;
697d0c0bce8SSudeep Holla 		}
698106b11b1SSudeep Holla 
699106b11b1SSudeep Holla 		if (drv_info->version > FFA_VERSION_1_0 &&
700106b11b1SSudeep Holla 		    !(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
701*38dd0c29SSudeep Holla 			ffa_mode_32bit_set(ffa_dev);
702d0c0bce8SSudeep Holla 	}
703d0c0bce8SSudeep Holla 	kfree(pbuf);
704d0c0bce8SSudeep Holla }
705d0c0bce8SSudeep Holla 
ffa_init(void)7063bbfe987SSudeep Holla static int __init ffa_init(void)
7073bbfe987SSudeep Holla {
7083bbfe987SSudeep Holla 	int ret;
7093bbfe987SSudeep Holla 
7103bbfe987SSudeep Holla 	ret = ffa_transport_init(&invoke_ffa_fn);
7113bbfe987SSudeep Holla 	if (ret)
7123bbfe987SSudeep Holla 		return ret;
7133bbfe987SSudeep Holla 
7143bbfe987SSudeep Holla 	ret = arm_ffa_bus_init();
7153bbfe987SSudeep Holla 	if (ret)
7163bbfe987SSudeep Holla 		return ret;
7173bbfe987SSudeep Holla 
7183bbfe987SSudeep Holla 	drv_info = kzalloc(sizeof(*drv_info), GFP_KERNEL);
7193bbfe987SSudeep Holla 	if (!drv_info) {
7203bbfe987SSudeep Holla 		ret = -ENOMEM;
7213bbfe987SSudeep Holla 		goto ffa_bus_exit;
7223bbfe987SSudeep Holla 	}
7233bbfe987SSudeep Holla 
7243bbfe987SSudeep Holla 	ret = ffa_version_check(&drv_info->version);
7253bbfe987SSudeep Holla 	if (ret)
7263bbfe987SSudeep Holla 		goto free_drv_info;
7273bbfe987SSudeep Holla 
7283bbfe987SSudeep Holla 	if (ffa_id_get(&drv_info->vm_id)) {
7293bbfe987SSudeep Holla 		pr_err("failed to obtain VM id for self\n");
7303bbfe987SSudeep Holla 		ret = -ENODEV;
7313bbfe987SSudeep Holla 		goto free_drv_info;
7323bbfe987SSudeep Holla 	}
7333bbfe987SSudeep Holla 
7343bbfe987SSudeep Holla 	drv_info->rx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL);
7353bbfe987SSudeep Holla 	if (!drv_info->rx_buffer) {
7363bbfe987SSudeep Holla 		ret = -ENOMEM;
7373bbfe987SSudeep Holla 		goto free_pages;
7383bbfe987SSudeep Holla 	}
7393bbfe987SSudeep Holla 
7403bbfe987SSudeep Holla 	drv_info->tx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL);
7413bbfe987SSudeep Holla 	if (!drv_info->tx_buffer) {
7423bbfe987SSudeep Holla 		ret = -ENOMEM;
7433bbfe987SSudeep Holla 		goto free_pages;
7443bbfe987SSudeep Holla 	}
7453bbfe987SSudeep Holla 
7463bbfe987SSudeep Holla 	ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
7473bbfe987SSudeep Holla 			   virt_to_phys(drv_info->rx_buffer),
7483bbfe987SSudeep Holla 			   RXTX_BUFFER_SIZE / FFA_PAGE_SIZE);
7493bbfe987SSudeep Holla 	if (ret) {
7503bbfe987SSudeep Holla 		pr_err("failed to register FFA RxTx buffers\n");
7513bbfe987SSudeep Holla 		goto free_pages;
7523bbfe987SSudeep Holla 	}
7533bbfe987SSudeep Holla 
7543bbfe987SSudeep Holla 	mutex_init(&drv_info->rx_lock);
7553bbfe987SSudeep Holla 	mutex_init(&drv_info->tx_lock);
7563bbfe987SSudeep Holla 
757d0c0bce8SSudeep Holla 	ffa_setup_partitions();
758d0c0bce8SSudeep Holla 
759e57fba91SSudeep Holla 	ffa_set_up_mem_ops_native_flag();
760e57fba91SSudeep Holla 
7613bbfe987SSudeep Holla 	return 0;
7623bbfe987SSudeep Holla free_pages:
7633bbfe987SSudeep Holla 	if (drv_info->tx_buffer)
7643bbfe987SSudeep Holla 		free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE);
7653bbfe987SSudeep Holla 	free_pages_exact(drv_info->rx_buffer, RXTX_BUFFER_SIZE);
7663bbfe987SSudeep Holla free_drv_info:
7673bbfe987SSudeep Holla 	kfree(drv_info);
7683bbfe987SSudeep Holla ffa_bus_exit:
7693bbfe987SSudeep Holla 	arm_ffa_bus_exit();
7703bbfe987SSudeep Holla 	return ret;
7713bbfe987SSudeep Holla }
7723bbfe987SSudeep Holla subsys_initcall(ffa_init);
7733bbfe987SSudeep Holla 
ffa_exit(void)7743bbfe987SSudeep Holla static void __exit ffa_exit(void)
7753bbfe987SSudeep Holla {
7763bbfe987SSudeep Holla 	ffa_rxtx_unmap(drv_info->vm_id);
7773bbfe987SSudeep Holla 	free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE);
7783bbfe987SSudeep Holla 	free_pages_exact(drv_info->rx_buffer, RXTX_BUFFER_SIZE);
7793bbfe987SSudeep Holla 	kfree(drv_info);
7803bbfe987SSudeep Holla 	arm_ffa_bus_exit();
7813bbfe987SSudeep Holla }
7823bbfe987SSudeep Holla module_exit(ffa_exit);
7833bbfe987SSudeep Holla 
7843bbfe987SSudeep Holla MODULE_ALIAS("arm-ffa");
7853bbfe987SSudeep Holla MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
7863bbfe987SSudeep Holla MODULE_DESCRIPTION("Arm FF-A interface driver");
7873bbfe987SSudeep Holla MODULE_LICENSE("GPL v2");
788