162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017-2018, Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/completion.h>
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/genalloc.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/kfifo.h>
1162306a36Sopenharmony_ci#include <linux/kthread.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/mutex.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/of_platform.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/firmware/intel/stratix10-smc.h>
2062306a36Sopenharmony_ci#include <linux/firmware/intel/stratix10-svc-client.h>
2162306a36Sopenharmony_ci#include <linux/types.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/**
2462306a36Sopenharmony_ci * SVC_NUM_DATA_IN_FIFO - number of struct stratix10_svc_data in the FIFO
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * SVC_NUM_CHANNEL - number of channel supported by service layer driver
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS - claim back the submitted buffer(s)
2962306a36Sopenharmony_ci * from the secure world for FPGA manager to reuse, or to free the buffer(s)
3062306a36Sopenharmony_ci * when all bit-stream data had be send.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * FPGA_CONFIG_STATUS_TIMEOUT_SEC - poll the FPGA configuration status,
3362306a36Sopenharmony_ci * service layer will return error to FPGA manager when timeout occurs,
3462306a36Sopenharmony_ci * timeout is set to 30 seconds (30 * 1000) at Intel Stratix10 SoC.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci#define SVC_NUM_DATA_IN_FIFO			32
3762306a36Sopenharmony_ci#define SVC_NUM_CHANNEL				3
3862306a36Sopenharmony_ci#define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS	200
3962306a36Sopenharmony_ci#define FPGA_CONFIG_STATUS_TIMEOUT_SEC		30
4062306a36Sopenharmony_ci#define BYTE_TO_WORD_SIZE              4
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* stratix10 service layer clients */
4362306a36Sopenharmony_ci#define STRATIX10_RSU				"stratix10-rsu"
4462306a36Sopenharmony_ci#define INTEL_FCS				"intel-fcs"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_citypedef void (svc_invoke_fn)(unsigned long, unsigned long, unsigned long,
4762306a36Sopenharmony_ci			     unsigned long, unsigned long, unsigned long,
4862306a36Sopenharmony_ci			     unsigned long, unsigned long,
4962306a36Sopenharmony_ci			     struct arm_smccc_res *);
5062306a36Sopenharmony_cistruct stratix10_svc_chan;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/**
5362306a36Sopenharmony_ci * struct stratix10_svc - svc private data
5462306a36Sopenharmony_ci * @stratix10_svc_rsu: pointer to stratix10 RSU device
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_cistruct stratix10_svc {
5762306a36Sopenharmony_ci	struct platform_device *stratix10_svc_rsu;
5862306a36Sopenharmony_ci	struct platform_device *intel_svc_fcs;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/**
6262306a36Sopenharmony_ci * struct stratix10_svc_sh_memory - service shared memory structure
6362306a36Sopenharmony_ci * @sync_complete: state for a completion
6462306a36Sopenharmony_ci * @addr: physical address of shared memory block
6562306a36Sopenharmony_ci * @size: size of shared memory block
6662306a36Sopenharmony_ci * @invoke_fn: function to issue secure monitor or hypervisor call
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * This struct is used to save physical address and size of shared memory
6962306a36Sopenharmony_ci * block. The shared memory blocked is allocated by secure monitor software
7062306a36Sopenharmony_ci * at secure world.
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Service layer driver uses the physical address and size to create a memory
7362306a36Sopenharmony_ci * pool, then allocates data buffer from that memory pool for service client.
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_cistruct stratix10_svc_sh_memory {
7662306a36Sopenharmony_ci	struct completion sync_complete;
7762306a36Sopenharmony_ci	unsigned long addr;
7862306a36Sopenharmony_ci	unsigned long size;
7962306a36Sopenharmony_ci	svc_invoke_fn *invoke_fn;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/**
8362306a36Sopenharmony_ci * struct stratix10_svc_data_mem - service memory structure
8462306a36Sopenharmony_ci * @vaddr: virtual address
8562306a36Sopenharmony_ci * @paddr: physical address
8662306a36Sopenharmony_ci * @size: size of memory
8762306a36Sopenharmony_ci * @node: link list head node
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci * This struct is used in a list that keeps track of buffers which have
9062306a36Sopenharmony_ci * been allocated or freed from the memory pool. Service layer driver also
9162306a36Sopenharmony_ci * uses this struct to transfer physical address to virtual address.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_cistruct stratix10_svc_data_mem {
9462306a36Sopenharmony_ci	void *vaddr;
9562306a36Sopenharmony_ci	phys_addr_t paddr;
9662306a36Sopenharmony_ci	size_t size;
9762306a36Sopenharmony_ci	struct list_head node;
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/**
10162306a36Sopenharmony_ci * struct stratix10_svc_data - service data structure
10262306a36Sopenharmony_ci * @chan: service channel
10362306a36Sopenharmony_ci * @paddr: physical address of to be processed payload
10462306a36Sopenharmony_ci * @size: to be processed playload size
10562306a36Sopenharmony_ci * @paddr_output: physical address of processed payload
10662306a36Sopenharmony_ci * @size_output: processed payload size
10762306a36Sopenharmony_ci * @command: service command requested by client
10862306a36Sopenharmony_ci * @flag: configuration type (full or partial)
10962306a36Sopenharmony_ci * @arg: args to be passed via registers and not physically mapped buffers
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * This struct is used in service FIFO for inter-process communication.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_cistruct stratix10_svc_data {
11462306a36Sopenharmony_ci	struct stratix10_svc_chan *chan;
11562306a36Sopenharmony_ci	phys_addr_t paddr;
11662306a36Sopenharmony_ci	size_t size;
11762306a36Sopenharmony_ci	phys_addr_t paddr_output;
11862306a36Sopenharmony_ci	size_t size_output;
11962306a36Sopenharmony_ci	u32 command;
12062306a36Sopenharmony_ci	u32 flag;
12162306a36Sopenharmony_ci	u64 arg[3];
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/**
12562306a36Sopenharmony_ci * struct stratix10_svc_controller - service controller
12662306a36Sopenharmony_ci * @dev: device
12762306a36Sopenharmony_ci * @chans: array of service channels
12862306a36Sopenharmony_ci * @num_chans: number of channels in 'chans' array
12962306a36Sopenharmony_ci * @num_active_client: number of active service client
13062306a36Sopenharmony_ci * @node: list management
13162306a36Sopenharmony_ci * @genpool: memory pool pointing to the memory region
13262306a36Sopenharmony_ci * @task: pointer to the thread task which handles SMC or HVC call
13362306a36Sopenharmony_ci * @svc_fifo: a queue for storing service message data
13462306a36Sopenharmony_ci * @complete_status: state for completion
13562306a36Sopenharmony_ci * @svc_fifo_lock: protect access to service message data queue
13662306a36Sopenharmony_ci * @invoke_fn: function to issue secure monitor call or hypervisor call
13762306a36Sopenharmony_ci *
13862306a36Sopenharmony_ci * This struct is used to create communication channels for service clients, to
13962306a36Sopenharmony_ci * handle secure monitor or hypervisor call.
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_cistruct stratix10_svc_controller {
14262306a36Sopenharmony_ci	struct device *dev;
14362306a36Sopenharmony_ci	struct stratix10_svc_chan *chans;
14462306a36Sopenharmony_ci	int num_chans;
14562306a36Sopenharmony_ci	int num_active_client;
14662306a36Sopenharmony_ci	struct list_head node;
14762306a36Sopenharmony_ci	struct gen_pool *genpool;
14862306a36Sopenharmony_ci	struct task_struct *task;
14962306a36Sopenharmony_ci	struct kfifo svc_fifo;
15062306a36Sopenharmony_ci	struct completion complete_status;
15162306a36Sopenharmony_ci	spinlock_t svc_fifo_lock;
15262306a36Sopenharmony_ci	svc_invoke_fn *invoke_fn;
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * struct stratix10_svc_chan - service communication channel
15762306a36Sopenharmony_ci * @ctrl: pointer to service controller which is the provider of this channel
15862306a36Sopenharmony_ci * @scl: pointer to service client which owns the channel
15962306a36Sopenharmony_ci * @name: service client name associated with the channel
16062306a36Sopenharmony_ci * @lock: protect access to the channel
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * This struct is used by service client to communicate with service layer, each
16362306a36Sopenharmony_ci * service client has its own channel created by service controller.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistruct stratix10_svc_chan {
16662306a36Sopenharmony_ci	struct stratix10_svc_controller *ctrl;
16762306a36Sopenharmony_ci	struct stratix10_svc_client *scl;
16862306a36Sopenharmony_ci	char *name;
16962306a36Sopenharmony_ci	spinlock_t lock;
17062306a36Sopenharmony_ci};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic LIST_HEAD(svc_ctrl);
17362306a36Sopenharmony_cistatic LIST_HEAD(svc_data_mem);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/**
17662306a36Sopenharmony_ci * svc_pa_to_va() - translate physical address to virtual address
17762306a36Sopenharmony_ci * @addr: to be translated physical address
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * Return: valid virtual address or NULL if the provided physical
18062306a36Sopenharmony_ci * address doesn't exist.
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_cistatic void *svc_pa_to_va(unsigned long addr)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct stratix10_svc_data_mem *pmem;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	pr_debug("claim back P-addr=0x%016x\n", (unsigned int)addr);
18762306a36Sopenharmony_ci	list_for_each_entry(pmem, &svc_data_mem, node)
18862306a36Sopenharmony_ci		if (pmem->paddr == addr)
18962306a36Sopenharmony_ci			return pmem->vaddr;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* physical address is not found */
19262306a36Sopenharmony_ci	return NULL;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/**
19662306a36Sopenharmony_ci * svc_thread_cmd_data_claim() - claim back buffer from the secure world
19762306a36Sopenharmony_ci * @ctrl: pointer to service layer controller
19862306a36Sopenharmony_ci * @p_data: pointer to service data structure
19962306a36Sopenharmony_ci * @cb_data: pointer to callback data structure to service client
20062306a36Sopenharmony_ci *
20162306a36Sopenharmony_ci * Claim back the submitted buffers from the secure world and pass buffer
20262306a36Sopenharmony_ci * back to service client (FPGA manager, etc) for reuse.
20362306a36Sopenharmony_ci */
20462306a36Sopenharmony_cistatic void svc_thread_cmd_data_claim(struct stratix10_svc_controller *ctrl,
20562306a36Sopenharmony_ci				      struct stratix10_svc_data *p_data,
20662306a36Sopenharmony_ci				      struct stratix10_svc_cb_data *cb_data)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct arm_smccc_res res;
20962306a36Sopenharmony_ci	unsigned long timeout;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	reinit_completion(&ctrl->complete_status);
21262306a36Sopenharmony_ci	timeout = msecs_to_jiffies(FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	pr_debug("%s: claim back the submitted buffer\n", __func__);
21562306a36Sopenharmony_ci	do {
21662306a36Sopenharmony_ci		ctrl->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE,
21762306a36Sopenharmony_ci				0, 0, 0, 0, 0, 0, 0, &res);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		if (res.a0 == INTEL_SIP_SMC_STATUS_OK) {
22062306a36Sopenharmony_ci			if (!res.a1) {
22162306a36Sopenharmony_ci				complete(&ctrl->complete_status);
22262306a36Sopenharmony_ci				break;
22362306a36Sopenharmony_ci			}
22462306a36Sopenharmony_ci			cb_data->status = BIT(SVC_STATUS_BUFFER_DONE);
22562306a36Sopenharmony_ci			cb_data->kaddr1 = svc_pa_to_va(res.a1);
22662306a36Sopenharmony_ci			cb_data->kaddr2 = (res.a2) ?
22762306a36Sopenharmony_ci					  svc_pa_to_va(res.a2) : NULL;
22862306a36Sopenharmony_ci			cb_data->kaddr3 = (res.a3) ?
22962306a36Sopenharmony_ci					  svc_pa_to_va(res.a3) : NULL;
23062306a36Sopenharmony_ci			p_data->chan->scl->receive_cb(p_data->chan->scl,
23162306a36Sopenharmony_ci						      cb_data);
23262306a36Sopenharmony_ci		} else {
23362306a36Sopenharmony_ci			pr_debug("%s: secure world busy, polling again\n",
23462306a36Sopenharmony_ci				 __func__);
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci	} while (res.a0 == INTEL_SIP_SMC_STATUS_OK ||
23762306a36Sopenharmony_ci		 res.a0 == INTEL_SIP_SMC_STATUS_BUSY ||
23862306a36Sopenharmony_ci		 wait_for_completion_timeout(&ctrl->complete_status, timeout));
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/**
24262306a36Sopenharmony_ci * svc_thread_cmd_config_status() - check configuration status
24362306a36Sopenharmony_ci * @ctrl: pointer to service layer controller
24462306a36Sopenharmony_ci * @p_data: pointer to service data structure
24562306a36Sopenharmony_ci * @cb_data: pointer to callback data structure to service client
24662306a36Sopenharmony_ci *
24762306a36Sopenharmony_ci * Check whether the secure firmware at secure world has finished the FPGA
24862306a36Sopenharmony_ci * configuration, and then inform FPGA manager the configuration status.
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_cistatic void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl,
25162306a36Sopenharmony_ci					 struct stratix10_svc_data *p_data,
25262306a36Sopenharmony_ci					 struct stratix10_svc_cb_data *cb_data)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct arm_smccc_res res;
25562306a36Sopenharmony_ci	int count_in_sec;
25662306a36Sopenharmony_ci	unsigned long a0, a1, a2;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	cb_data->kaddr1 = NULL;
25962306a36Sopenharmony_ci	cb_data->kaddr2 = NULL;
26062306a36Sopenharmony_ci	cb_data->kaddr3 = NULL;
26162306a36Sopenharmony_ci	cb_data->status = BIT(SVC_STATUS_ERROR);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	pr_debug("%s: polling config status\n", __func__);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	a0 = INTEL_SIP_SMC_FPGA_CONFIG_ISDONE;
26662306a36Sopenharmony_ci	a1 = (unsigned long)p_data->paddr;
26762306a36Sopenharmony_ci	a2 = (unsigned long)p_data->size;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (p_data->command == COMMAND_POLL_SERVICE_STATUS)
27062306a36Sopenharmony_ci		a0 = INTEL_SIP_SMC_SERVICE_COMPLETED;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	count_in_sec = FPGA_CONFIG_STATUS_TIMEOUT_SEC;
27362306a36Sopenharmony_ci	while (count_in_sec) {
27462306a36Sopenharmony_ci		ctrl->invoke_fn(a0, a1, a2, 0, 0, 0, 0, 0, &res);
27562306a36Sopenharmony_ci		if ((res.a0 == INTEL_SIP_SMC_STATUS_OK) ||
27662306a36Sopenharmony_ci		    (res.a0 == INTEL_SIP_SMC_STATUS_ERROR) ||
27762306a36Sopenharmony_ci		    (res.a0 == INTEL_SIP_SMC_STATUS_REJECTED))
27862306a36Sopenharmony_ci			break;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		/*
28162306a36Sopenharmony_ci		 * request is still in progress, wait one second then
28262306a36Sopenharmony_ci		 * poll again
28362306a36Sopenharmony_ci		 */
28462306a36Sopenharmony_ci		msleep(1000);
28562306a36Sopenharmony_ci		count_in_sec--;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (!count_in_sec) {
28962306a36Sopenharmony_ci		pr_err("%s: poll status timeout\n", __func__);
29062306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_BUSY);
29162306a36Sopenharmony_ci	} else if (res.a0 == INTEL_SIP_SMC_STATUS_OK) {
29262306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_COMPLETED);
29362306a36Sopenharmony_ci		cb_data->kaddr2 = (res.a2) ?
29462306a36Sopenharmony_ci				  svc_pa_to_va(res.a2) : NULL;
29562306a36Sopenharmony_ci		cb_data->kaddr3 = (res.a3) ? &res.a3 : NULL;
29662306a36Sopenharmony_ci	} else {
29762306a36Sopenharmony_ci		pr_err("%s: poll status error\n", __func__);
29862306a36Sopenharmony_ci		cb_data->kaddr1 = &res.a1;
29962306a36Sopenharmony_ci		cb_data->kaddr2 = (res.a2) ?
30062306a36Sopenharmony_ci				  svc_pa_to_va(res.a2) : NULL;
30162306a36Sopenharmony_ci		cb_data->kaddr3 = (res.a3) ? &res.a3 : NULL;
30262306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_ERROR);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	p_data->chan->scl->receive_cb(p_data->chan->scl, cb_data);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/**
30962306a36Sopenharmony_ci * svc_thread_recv_status_ok() - handle the successful status
31062306a36Sopenharmony_ci * @p_data: pointer to service data structure
31162306a36Sopenharmony_ci * @cb_data: pointer to callback data structure to service client
31262306a36Sopenharmony_ci * @res: result from SMC or HVC call
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * Send back the correspond status to the service clients.
31562306a36Sopenharmony_ci */
31662306a36Sopenharmony_cistatic void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
31762306a36Sopenharmony_ci				      struct stratix10_svc_cb_data *cb_data,
31862306a36Sopenharmony_ci				      struct arm_smccc_res res)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	cb_data->kaddr1 = NULL;
32162306a36Sopenharmony_ci	cb_data->kaddr2 = NULL;
32262306a36Sopenharmony_ci	cb_data->kaddr3 = NULL;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	switch (p_data->command) {
32562306a36Sopenharmony_ci	case COMMAND_RECONFIG:
32662306a36Sopenharmony_ci	case COMMAND_RSU_UPDATE:
32762306a36Sopenharmony_ci	case COMMAND_RSU_NOTIFY:
32862306a36Sopenharmony_ci	case COMMAND_FCS_REQUEST_SERVICE:
32962306a36Sopenharmony_ci	case COMMAND_FCS_SEND_CERTIFICATE:
33062306a36Sopenharmony_ci	case COMMAND_FCS_DATA_ENCRYPTION:
33162306a36Sopenharmony_ci	case COMMAND_FCS_DATA_DECRYPTION:
33262306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_OK);
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	case COMMAND_RECONFIG_DATA_SUBMIT:
33562306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_BUFFER_SUBMITTED);
33662306a36Sopenharmony_ci		break;
33762306a36Sopenharmony_ci	case COMMAND_RECONFIG_STATUS:
33862306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_COMPLETED);
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case COMMAND_RSU_RETRY:
34162306a36Sopenharmony_ci	case COMMAND_RSU_MAX_RETRY:
34262306a36Sopenharmony_ci	case COMMAND_RSU_DCMF_STATUS:
34362306a36Sopenharmony_ci	case COMMAND_FIRMWARE_VERSION:
34462306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_OK);
34562306a36Sopenharmony_ci		cb_data->kaddr1 = &res.a1;
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	case COMMAND_SMC_SVC_VERSION:
34862306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_OK);
34962306a36Sopenharmony_ci		cb_data->kaddr1 = &res.a1;
35062306a36Sopenharmony_ci		cb_data->kaddr2 = &res.a2;
35162306a36Sopenharmony_ci		break;
35262306a36Sopenharmony_ci	case COMMAND_RSU_DCMF_VERSION:
35362306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_OK);
35462306a36Sopenharmony_ci		cb_data->kaddr1 = &res.a1;
35562306a36Sopenharmony_ci		cb_data->kaddr2 = &res.a2;
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	case COMMAND_FCS_RANDOM_NUMBER_GEN:
35862306a36Sopenharmony_ci	case COMMAND_FCS_GET_PROVISION_DATA:
35962306a36Sopenharmony_ci	case COMMAND_POLL_SERVICE_STATUS:
36062306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_OK);
36162306a36Sopenharmony_ci		cb_data->kaddr1 = &res.a1;
36262306a36Sopenharmony_ci		cb_data->kaddr2 = svc_pa_to_va(res.a2);
36362306a36Sopenharmony_ci		cb_data->kaddr3 = &res.a3;
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	case COMMAND_MBOX_SEND_CMD:
36662306a36Sopenharmony_ci		cb_data->status = BIT(SVC_STATUS_OK);
36762306a36Sopenharmony_ci		cb_data->kaddr1 = &res.a1;
36862306a36Sopenharmony_ci		/* SDM return size in u8. Convert size to u32 word */
36962306a36Sopenharmony_ci		res.a2 = res.a2 * BYTE_TO_WORD_SIZE;
37062306a36Sopenharmony_ci		cb_data->kaddr2 = &res.a2;
37162306a36Sopenharmony_ci		break;
37262306a36Sopenharmony_ci	default:
37362306a36Sopenharmony_ci		pr_warn("it shouldn't happen\n");
37462306a36Sopenharmony_ci		break;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	pr_debug("%s: call receive_cb\n", __func__);
37862306a36Sopenharmony_ci	p_data->chan->scl->receive_cb(p_data->chan->scl, cb_data);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/**
38262306a36Sopenharmony_ci * svc_normal_to_secure_thread() - the function to run in the kthread
38362306a36Sopenharmony_ci * @data: data pointer for kthread function
38462306a36Sopenharmony_ci *
38562306a36Sopenharmony_ci * Service layer driver creates stratix10_svc_smc_hvc_call kthread on CPU
38662306a36Sopenharmony_ci * node 0, its function stratix10_svc_secure_call_thread is used to handle
38762306a36Sopenharmony_ci * SMC or HVC calls between kernel driver and secure monitor software.
38862306a36Sopenharmony_ci *
38962306a36Sopenharmony_ci * Return: 0 for success or -ENOMEM on error.
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_cistatic int svc_normal_to_secure_thread(void *data)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct stratix10_svc_controller
39462306a36Sopenharmony_ci			*ctrl = (struct stratix10_svc_controller *)data;
39562306a36Sopenharmony_ci	struct stratix10_svc_data *pdata;
39662306a36Sopenharmony_ci	struct stratix10_svc_cb_data *cbdata;
39762306a36Sopenharmony_ci	struct arm_smccc_res res;
39862306a36Sopenharmony_ci	unsigned long a0, a1, a2, a3, a4, a5, a6, a7;
39962306a36Sopenharmony_ci	int ret_fifo = 0;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	pdata =  kmalloc(sizeof(*pdata), GFP_KERNEL);
40262306a36Sopenharmony_ci	if (!pdata)
40362306a36Sopenharmony_ci		return -ENOMEM;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	cbdata = kmalloc(sizeof(*cbdata), GFP_KERNEL);
40662306a36Sopenharmony_ci	if (!cbdata) {
40762306a36Sopenharmony_ci		kfree(pdata);
40862306a36Sopenharmony_ci		return -ENOMEM;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* default set, to remove build warning */
41262306a36Sopenharmony_ci	a0 = INTEL_SIP_SMC_FPGA_CONFIG_LOOPBACK;
41362306a36Sopenharmony_ci	a1 = 0;
41462306a36Sopenharmony_ci	a2 = 0;
41562306a36Sopenharmony_ci	a3 = 0;
41662306a36Sopenharmony_ci	a4 = 0;
41762306a36Sopenharmony_ci	a5 = 0;
41862306a36Sopenharmony_ci	a6 = 0;
41962306a36Sopenharmony_ci	a7 = 0;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	pr_debug("smc_hvc_shm_thread is running\n");
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	while (!kthread_should_stop()) {
42462306a36Sopenharmony_ci		ret_fifo = kfifo_out_spinlocked(&ctrl->svc_fifo,
42562306a36Sopenharmony_ci						pdata, sizeof(*pdata),
42662306a36Sopenharmony_ci						&ctrl->svc_fifo_lock);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		if (!ret_fifo)
42962306a36Sopenharmony_ci			continue;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		pr_debug("get from FIFO pa=0x%016x, command=%u, size=%u\n",
43262306a36Sopenharmony_ci			 (unsigned int)pdata->paddr, pdata->command,
43362306a36Sopenharmony_ci			 (unsigned int)pdata->size);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		switch (pdata->command) {
43662306a36Sopenharmony_ci		case COMMAND_RECONFIG_DATA_CLAIM:
43762306a36Sopenharmony_ci			svc_thread_cmd_data_claim(ctrl, pdata, cbdata);
43862306a36Sopenharmony_ci			continue;
43962306a36Sopenharmony_ci		case COMMAND_RECONFIG:
44062306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FPGA_CONFIG_START;
44162306a36Sopenharmony_ci			pr_debug("conf_type=%u\n", (unsigned int)pdata->flag);
44262306a36Sopenharmony_ci			a1 = pdata->flag;
44362306a36Sopenharmony_ci			a2 = 0;
44462306a36Sopenharmony_ci			break;
44562306a36Sopenharmony_ci		case COMMAND_RECONFIG_DATA_SUBMIT:
44662306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FPGA_CONFIG_WRITE;
44762306a36Sopenharmony_ci			a1 = (unsigned long)pdata->paddr;
44862306a36Sopenharmony_ci			a2 = (unsigned long)pdata->size;
44962306a36Sopenharmony_ci			break;
45062306a36Sopenharmony_ci		case COMMAND_RECONFIG_STATUS:
45162306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FPGA_CONFIG_ISDONE;
45262306a36Sopenharmony_ci			a1 = 0;
45362306a36Sopenharmony_ci			a2 = 0;
45462306a36Sopenharmony_ci			break;
45562306a36Sopenharmony_ci		case COMMAND_RSU_STATUS:
45662306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_RSU_STATUS;
45762306a36Sopenharmony_ci			a1 = 0;
45862306a36Sopenharmony_ci			a2 = 0;
45962306a36Sopenharmony_ci			break;
46062306a36Sopenharmony_ci		case COMMAND_RSU_UPDATE:
46162306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_RSU_UPDATE;
46262306a36Sopenharmony_ci			a1 = pdata->arg[0];
46362306a36Sopenharmony_ci			a2 = 0;
46462306a36Sopenharmony_ci			break;
46562306a36Sopenharmony_ci		case COMMAND_RSU_NOTIFY:
46662306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_RSU_NOTIFY;
46762306a36Sopenharmony_ci			a1 = pdata->arg[0];
46862306a36Sopenharmony_ci			a2 = 0;
46962306a36Sopenharmony_ci			break;
47062306a36Sopenharmony_ci		case COMMAND_RSU_RETRY:
47162306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_RSU_RETRY_COUNTER;
47262306a36Sopenharmony_ci			a1 = 0;
47362306a36Sopenharmony_ci			a2 = 0;
47462306a36Sopenharmony_ci			break;
47562306a36Sopenharmony_ci		case COMMAND_RSU_MAX_RETRY:
47662306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_RSU_MAX_RETRY;
47762306a36Sopenharmony_ci			a1 = 0;
47862306a36Sopenharmony_ci			a2 = 0;
47962306a36Sopenharmony_ci			break;
48062306a36Sopenharmony_ci		case COMMAND_RSU_DCMF_VERSION:
48162306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_RSU_DCMF_VERSION;
48262306a36Sopenharmony_ci			a1 = 0;
48362306a36Sopenharmony_ci			a2 = 0;
48462306a36Sopenharmony_ci			break;
48562306a36Sopenharmony_ci		case COMMAND_FIRMWARE_VERSION:
48662306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FIRMWARE_VERSION;
48762306a36Sopenharmony_ci			a1 = 0;
48862306a36Sopenharmony_ci			a2 = 0;
48962306a36Sopenharmony_ci			break;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		/* for FCS */
49262306a36Sopenharmony_ci		case COMMAND_FCS_DATA_ENCRYPTION:
49362306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FCS_CRYPTION;
49462306a36Sopenharmony_ci			a1 = 1;
49562306a36Sopenharmony_ci			a2 = (unsigned long)pdata->paddr;
49662306a36Sopenharmony_ci			a3 = (unsigned long)pdata->size;
49762306a36Sopenharmony_ci			a4 = (unsigned long)pdata->paddr_output;
49862306a36Sopenharmony_ci			a5 = (unsigned long)pdata->size_output;
49962306a36Sopenharmony_ci			break;
50062306a36Sopenharmony_ci		case COMMAND_FCS_DATA_DECRYPTION:
50162306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FCS_CRYPTION;
50262306a36Sopenharmony_ci			a1 = 0;
50362306a36Sopenharmony_ci			a2 = (unsigned long)pdata->paddr;
50462306a36Sopenharmony_ci			a3 = (unsigned long)pdata->size;
50562306a36Sopenharmony_ci			a4 = (unsigned long)pdata->paddr_output;
50662306a36Sopenharmony_ci			a5 = (unsigned long)pdata->size_output;
50762306a36Sopenharmony_ci			break;
50862306a36Sopenharmony_ci		case COMMAND_FCS_RANDOM_NUMBER_GEN:
50962306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FCS_RANDOM_NUMBER;
51062306a36Sopenharmony_ci			a1 = (unsigned long)pdata->paddr;
51162306a36Sopenharmony_ci			a2 = 0;
51262306a36Sopenharmony_ci			break;
51362306a36Sopenharmony_ci		case COMMAND_FCS_REQUEST_SERVICE:
51462306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FCS_SERVICE_REQUEST;
51562306a36Sopenharmony_ci			a1 = (unsigned long)pdata->paddr;
51662306a36Sopenharmony_ci			a2 = (unsigned long)pdata->size;
51762306a36Sopenharmony_ci			break;
51862306a36Sopenharmony_ci		case COMMAND_FCS_SEND_CERTIFICATE:
51962306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FCS_SEND_CERTIFICATE;
52062306a36Sopenharmony_ci			a1 = (unsigned long)pdata->paddr;
52162306a36Sopenharmony_ci			a2 = (unsigned long)pdata->size;
52262306a36Sopenharmony_ci			break;
52362306a36Sopenharmony_ci		case COMMAND_FCS_GET_PROVISION_DATA:
52462306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_FCS_GET_PROVISION_DATA;
52562306a36Sopenharmony_ci			a1 = (unsigned long)pdata->paddr;
52662306a36Sopenharmony_ci			a2 = 0;
52762306a36Sopenharmony_ci			break;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		/* for polling */
53062306a36Sopenharmony_ci		case COMMAND_POLL_SERVICE_STATUS:
53162306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_SERVICE_COMPLETED;
53262306a36Sopenharmony_ci			a1 = (unsigned long)pdata->paddr;
53362306a36Sopenharmony_ci			a2 = (unsigned long)pdata->size;
53462306a36Sopenharmony_ci			break;
53562306a36Sopenharmony_ci		case COMMAND_RSU_DCMF_STATUS:
53662306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_RSU_DCMF_STATUS;
53762306a36Sopenharmony_ci			a1 = 0;
53862306a36Sopenharmony_ci			a2 = 0;
53962306a36Sopenharmony_ci			break;
54062306a36Sopenharmony_ci		case COMMAND_SMC_SVC_VERSION:
54162306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_SVC_VERSION;
54262306a36Sopenharmony_ci			a1 = 0;
54362306a36Sopenharmony_ci			a2 = 0;
54462306a36Sopenharmony_ci			break;
54562306a36Sopenharmony_ci		case COMMAND_MBOX_SEND_CMD:
54662306a36Sopenharmony_ci			a0 = INTEL_SIP_SMC_MBOX_SEND_CMD;
54762306a36Sopenharmony_ci			a1 = pdata->arg[0];
54862306a36Sopenharmony_ci			a2 = (unsigned long)pdata->paddr;
54962306a36Sopenharmony_ci			a3 = (unsigned long)pdata->size / BYTE_TO_WORD_SIZE;
55062306a36Sopenharmony_ci			a4 = pdata->arg[1];
55162306a36Sopenharmony_ci			a5 = (unsigned long)pdata->paddr_output;
55262306a36Sopenharmony_ci			a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE;
55362306a36Sopenharmony_ci			break;
55462306a36Sopenharmony_ci		default:
55562306a36Sopenharmony_ci			pr_warn("it shouldn't happen\n");
55662306a36Sopenharmony_ci			break;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci		pr_debug("%s: before SMC call -- a0=0x%016x a1=0x%016x",
55962306a36Sopenharmony_ci			 __func__,
56062306a36Sopenharmony_ci			 (unsigned int)a0,
56162306a36Sopenharmony_ci			 (unsigned int)a1);
56262306a36Sopenharmony_ci		pr_debug(" a2=0x%016x\n", (unsigned int)a2);
56362306a36Sopenharmony_ci		pr_debug(" a3=0x%016x\n", (unsigned int)a3);
56462306a36Sopenharmony_ci		pr_debug(" a4=0x%016x\n", (unsigned int)a4);
56562306a36Sopenharmony_ci		pr_debug(" a5=0x%016x\n", (unsigned int)a5);
56662306a36Sopenharmony_ci		ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		pr_debug("%s: after SMC call -- res.a0=0x%016x",
56962306a36Sopenharmony_ci			 __func__, (unsigned int)res.a0);
57062306a36Sopenharmony_ci		pr_debug(" res.a1=0x%016x, res.a2=0x%016x",
57162306a36Sopenharmony_ci			 (unsigned int)res.a1, (unsigned int)res.a2);
57262306a36Sopenharmony_ci		pr_debug(" res.a3=0x%016x\n", (unsigned int)res.a3);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		if (pdata->command == COMMAND_RSU_STATUS) {
57562306a36Sopenharmony_ci			if (res.a0 == INTEL_SIP_SMC_RSU_ERROR)
57662306a36Sopenharmony_ci				cbdata->status = BIT(SVC_STATUS_ERROR);
57762306a36Sopenharmony_ci			else
57862306a36Sopenharmony_ci				cbdata->status = BIT(SVC_STATUS_OK);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci			cbdata->kaddr1 = &res;
58162306a36Sopenharmony_ci			cbdata->kaddr2 = NULL;
58262306a36Sopenharmony_ci			cbdata->kaddr3 = NULL;
58362306a36Sopenharmony_ci			pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata);
58462306a36Sopenharmony_ci			continue;
58562306a36Sopenharmony_ci		}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		switch (res.a0) {
58862306a36Sopenharmony_ci		case INTEL_SIP_SMC_STATUS_OK:
58962306a36Sopenharmony_ci			svc_thread_recv_status_ok(pdata, cbdata, res);
59062306a36Sopenharmony_ci			break;
59162306a36Sopenharmony_ci		case INTEL_SIP_SMC_STATUS_BUSY:
59262306a36Sopenharmony_ci			switch (pdata->command) {
59362306a36Sopenharmony_ci			case COMMAND_RECONFIG_DATA_SUBMIT:
59462306a36Sopenharmony_ci				svc_thread_cmd_data_claim(ctrl,
59562306a36Sopenharmony_ci							  pdata, cbdata);
59662306a36Sopenharmony_ci				break;
59762306a36Sopenharmony_ci			case COMMAND_RECONFIG_STATUS:
59862306a36Sopenharmony_ci			case COMMAND_POLL_SERVICE_STATUS:
59962306a36Sopenharmony_ci				svc_thread_cmd_config_status(ctrl,
60062306a36Sopenharmony_ci							     pdata, cbdata);
60162306a36Sopenharmony_ci				break;
60262306a36Sopenharmony_ci			default:
60362306a36Sopenharmony_ci				pr_warn("it shouldn't happen\n");
60462306a36Sopenharmony_ci				break;
60562306a36Sopenharmony_ci			}
60662306a36Sopenharmony_ci			break;
60762306a36Sopenharmony_ci		case INTEL_SIP_SMC_STATUS_REJECTED:
60862306a36Sopenharmony_ci			pr_debug("%s: STATUS_REJECTED\n", __func__);
60962306a36Sopenharmony_ci			/* for FCS */
61062306a36Sopenharmony_ci			switch (pdata->command) {
61162306a36Sopenharmony_ci			case COMMAND_FCS_REQUEST_SERVICE:
61262306a36Sopenharmony_ci			case COMMAND_FCS_SEND_CERTIFICATE:
61362306a36Sopenharmony_ci			case COMMAND_FCS_GET_PROVISION_DATA:
61462306a36Sopenharmony_ci			case COMMAND_FCS_DATA_ENCRYPTION:
61562306a36Sopenharmony_ci			case COMMAND_FCS_DATA_DECRYPTION:
61662306a36Sopenharmony_ci			case COMMAND_FCS_RANDOM_NUMBER_GEN:
61762306a36Sopenharmony_ci			case COMMAND_MBOX_SEND_CMD:
61862306a36Sopenharmony_ci				cbdata->status = BIT(SVC_STATUS_INVALID_PARAM);
61962306a36Sopenharmony_ci				cbdata->kaddr1 = NULL;
62062306a36Sopenharmony_ci				cbdata->kaddr2 = NULL;
62162306a36Sopenharmony_ci				cbdata->kaddr3 = NULL;
62262306a36Sopenharmony_ci				pdata->chan->scl->receive_cb(pdata->chan->scl,
62362306a36Sopenharmony_ci							     cbdata);
62462306a36Sopenharmony_ci				break;
62562306a36Sopenharmony_ci			}
62662306a36Sopenharmony_ci			break;
62762306a36Sopenharmony_ci		case INTEL_SIP_SMC_STATUS_ERROR:
62862306a36Sopenharmony_ci		case INTEL_SIP_SMC_RSU_ERROR:
62962306a36Sopenharmony_ci			pr_err("%s: STATUS_ERROR\n", __func__);
63062306a36Sopenharmony_ci			cbdata->status = BIT(SVC_STATUS_ERROR);
63162306a36Sopenharmony_ci			cbdata->kaddr1 = &res.a1;
63262306a36Sopenharmony_ci			cbdata->kaddr2 = (res.a2) ?
63362306a36Sopenharmony_ci				svc_pa_to_va(res.a2) : NULL;
63462306a36Sopenharmony_ci			cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL;
63562306a36Sopenharmony_ci			pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata);
63662306a36Sopenharmony_ci			break;
63762306a36Sopenharmony_ci		default:
63862306a36Sopenharmony_ci			pr_warn("Secure firmware doesn't support...\n");
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci			/*
64162306a36Sopenharmony_ci			 * be compatible with older version firmware which
64262306a36Sopenharmony_ci			 * doesn't support newer RSU commands
64362306a36Sopenharmony_ci			 */
64462306a36Sopenharmony_ci			if ((pdata->command != COMMAND_RSU_UPDATE) &&
64562306a36Sopenharmony_ci				(pdata->command != COMMAND_RSU_STATUS)) {
64662306a36Sopenharmony_ci				cbdata->status =
64762306a36Sopenharmony_ci					BIT(SVC_STATUS_NO_SUPPORT);
64862306a36Sopenharmony_ci				cbdata->kaddr1 = NULL;
64962306a36Sopenharmony_ci				cbdata->kaddr2 = NULL;
65062306a36Sopenharmony_ci				cbdata->kaddr3 = NULL;
65162306a36Sopenharmony_ci				pdata->chan->scl->receive_cb(
65262306a36Sopenharmony_ci					pdata->chan->scl, cbdata);
65362306a36Sopenharmony_ci			}
65462306a36Sopenharmony_ci			break;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	kfree(cbdata);
66062306a36Sopenharmony_ci	kfree(pdata);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return 0;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci/**
66662306a36Sopenharmony_ci * svc_normal_to_secure_shm_thread() - the function to run in the kthread
66762306a36Sopenharmony_ci * @data: data pointer for kthread function
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci * Service layer driver creates stratix10_svc_smc_hvc_shm kthread on CPU
67062306a36Sopenharmony_ci * node 0, its function stratix10_svc_secure_shm_thread is used to query the
67162306a36Sopenharmony_ci * physical address of memory block reserved by secure monitor software at
67262306a36Sopenharmony_ci * secure world.
67362306a36Sopenharmony_ci *
67462306a36Sopenharmony_ci * svc_normal_to_secure_shm_thread() terminates directly since it is a
67562306a36Sopenharmony_ci * standlone thread for which no one will call kthread_stop() or return when
67662306a36Sopenharmony_ci * 'kthread_should_stop()' is true.
67762306a36Sopenharmony_ci */
67862306a36Sopenharmony_cistatic int svc_normal_to_secure_shm_thread(void *data)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	struct stratix10_svc_sh_memory
68162306a36Sopenharmony_ci			*sh_mem = (struct stratix10_svc_sh_memory *)data;
68262306a36Sopenharmony_ci	struct arm_smccc_res res;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* SMC or HVC call to get shared memory info from secure world */
68562306a36Sopenharmony_ci	sh_mem->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM,
68662306a36Sopenharmony_ci			  0, 0, 0, 0, 0, 0, 0, &res);
68762306a36Sopenharmony_ci	if (res.a0 == INTEL_SIP_SMC_STATUS_OK) {
68862306a36Sopenharmony_ci		sh_mem->addr = res.a1;
68962306a36Sopenharmony_ci		sh_mem->size = res.a2;
69062306a36Sopenharmony_ci	} else {
69162306a36Sopenharmony_ci		pr_err("%s: after SMC call -- res.a0=0x%016x",  __func__,
69262306a36Sopenharmony_ci		       (unsigned int)res.a0);
69362306a36Sopenharmony_ci		sh_mem->addr = 0;
69462306a36Sopenharmony_ci		sh_mem->size = 0;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	complete(&sh_mem->sync_complete);
69862306a36Sopenharmony_ci	return 0;
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci/**
70262306a36Sopenharmony_ci * svc_get_sh_memory() - get memory block reserved by secure monitor SW
70362306a36Sopenharmony_ci * @pdev: pointer to service layer device
70462306a36Sopenharmony_ci * @sh_memory: pointer to service shared memory structure
70562306a36Sopenharmony_ci *
70662306a36Sopenharmony_ci * Return: zero for successfully getting the physical address of memory block
70762306a36Sopenharmony_ci * reserved by secure monitor software, or negative value on error.
70862306a36Sopenharmony_ci */
70962306a36Sopenharmony_cistatic int svc_get_sh_memory(struct platform_device *pdev,
71062306a36Sopenharmony_ci				    struct stratix10_svc_sh_memory *sh_memory)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
71362306a36Sopenharmony_ci	struct task_struct *sh_memory_task;
71462306a36Sopenharmony_ci	unsigned int cpu = 0;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	init_completion(&sh_memory->sync_complete);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* smc or hvc call happens on cpu 0 bound kthread */
71962306a36Sopenharmony_ci	sh_memory_task = kthread_create_on_node(svc_normal_to_secure_shm_thread,
72062306a36Sopenharmony_ci					       (void *)sh_memory,
72162306a36Sopenharmony_ci						cpu_to_node(cpu),
72262306a36Sopenharmony_ci						"svc_smc_hvc_shm_thread");
72362306a36Sopenharmony_ci	if (IS_ERR(sh_memory_task)) {
72462306a36Sopenharmony_ci		dev_err(dev, "fail to create stratix10_svc_smc_shm_thread\n");
72562306a36Sopenharmony_ci		return -EINVAL;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	wake_up_process(sh_memory_task);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&sh_memory->sync_complete, 10 * HZ)) {
73162306a36Sopenharmony_ci		dev_err(dev,
73262306a36Sopenharmony_ci			"timeout to get sh-memory paras from secure world\n");
73362306a36Sopenharmony_ci		return -ETIMEDOUT;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (!sh_memory->addr || !sh_memory->size) {
73762306a36Sopenharmony_ci		dev_err(dev,
73862306a36Sopenharmony_ci			"failed to get shared memory info from secure world\n");
73962306a36Sopenharmony_ci		return -ENOMEM;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	dev_dbg(dev, "SM software provides paddr: 0x%016x, size: 0x%08x\n",
74362306a36Sopenharmony_ci		(unsigned int)sh_memory->addr,
74462306a36Sopenharmony_ci		(unsigned int)sh_memory->size);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	return 0;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci/**
75062306a36Sopenharmony_ci * svc_create_memory_pool() - create a memory pool from reserved memory block
75162306a36Sopenharmony_ci * @pdev: pointer to service layer device
75262306a36Sopenharmony_ci * @sh_memory: pointer to service shared memory structure
75362306a36Sopenharmony_ci *
75462306a36Sopenharmony_ci * Return: pool allocated from reserved memory block or ERR_PTR() on error.
75562306a36Sopenharmony_ci */
75662306a36Sopenharmony_cistatic struct gen_pool *
75762306a36Sopenharmony_cisvc_create_memory_pool(struct platform_device *pdev,
75862306a36Sopenharmony_ci		       struct stratix10_svc_sh_memory *sh_memory)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
76162306a36Sopenharmony_ci	struct gen_pool *genpool;
76262306a36Sopenharmony_ci	unsigned long vaddr;
76362306a36Sopenharmony_ci	phys_addr_t paddr;
76462306a36Sopenharmony_ci	size_t size;
76562306a36Sopenharmony_ci	phys_addr_t begin;
76662306a36Sopenharmony_ci	phys_addr_t end;
76762306a36Sopenharmony_ci	void *va;
76862306a36Sopenharmony_ci	size_t page_mask = PAGE_SIZE - 1;
76962306a36Sopenharmony_ci	int min_alloc_order = 3;
77062306a36Sopenharmony_ci	int ret;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	begin = roundup(sh_memory->addr, PAGE_SIZE);
77362306a36Sopenharmony_ci	end = rounddown(sh_memory->addr + sh_memory->size, PAGE_SIZE);
77462306a36Sopenharmony_ci	paddr = begin;
77562306a36Sopenharmony_ci	size = end - begin;
77662306a36Sopenharmony_ci	va = devm_memremap(dev, paddr, size, MEMREMAP_WC);
77762306a36Sopenharmony_ci	if (IS_ERR(va)) {
77862306a36Sopenharmony_ci		dev_err(dev, "fail to remap shared memory\n");
77962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci	vaddr = (unsigned long)va;
78262306a36Sopenharmony_ci	dev_dbg(dev,
78362306a36Sopenharmony_ci		"reserved memory vaddr: %p, paddr: 0x%16x size: 0x%8x\n",
78462306a36Sopenharmony_ci		va, (unsigned int)paddr, (unsigned int)size);
78562306a36Sopenharmony_ci	if ((vaddr & page_mask) || (paddr & page_mask) ||
78662306a36Sopenharmony_ci	    (size & page_mask)) {
78762306a36Sopenharmony_ci		dev_err(dev, "page is not aligned\n");
78862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci	genpool = gen_pool_create(min_alloc_order, -1);
79162306a36Sopenharmony_ci	if (!genpool) {
79262306a36Sopenharmony_ci		dev_err(dev, "fail to create genpool\n");
79362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci	gen_pool_set_algo(genpool, gen_pool_best_fit, NULL);
79662306a36Sopenharmony_ci	ret = gen_pool_add_virt(genpool, vaddr, paddr, size, -1);
79762306a36Sopenharmony_ci	if (ret) {
79862306a36Sopenharmony_ci		dev_err(dev, "fail to add memory chunk to the pool\n");
79962306a36Sopenharmony_ci		gen_pool_destroy(genpool);
80062306a36Sopenharmony_ci		return ERR_PTR(ret);
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	return genpool;
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/**
80762306a36Sopenharmony_ci * svc_smccc_smc() - secure monitor call between normal and secure world
80862306a36Sopenharmony_ci * @a0: argument passed in registers 0
80962306a36Sopenharmony_ci * @a1: argument passed in registers 1
81062306a36Sopenharmony_ci * @a2: argument passed in registers 2
81162306a36Sopenharmony_ci * @a3: argument passed in registers 3
81262306a36Sopenharmony_ci * @a4: argument passed in registers 4
81362306a36Sopenharmony_ci * @a5: argument passed in registers 5
81462306a36Sopenharmony_ci * @a6: argument passed in registers 6
81562306a36Sopenharmony_ci * @a7: argument passed in registers 7
81662306a36Sopenharmony_ci * @res: result values from register 0 to 3
81762306a36Sopenharmony_ci */
81862306a36Sopenharmony_cistatic void svc_smccc_smc(unsigned long a0, unsigned long a1,
81962306a36Sopenharmony_ci			  unsigned long a2, unsigned long a3,
82062306a36Sopenharmony_ci			  unsigned long a4, unsigned long a5,
82162306a36Sopenharmony_ci			  unsigned long a6, unsigned long a7,
82262306a36Sopenharmony_ci			  struct arm_smccc_res *res)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci/**
82862306a36Sopenharmony_ci * svc_smccc_hvc() - hypervisor call between normal and secure world
82962306a36Sopenharmony_ci * @a0: argument passed in registers 0
83062306a36Sopenharmony_ci * @a1: argument passed in registers 1
83162306a36Sopenharmony_ci * @a2: argument passed in registers 2
83262306a36Sopenharmony_ci * @a3: argument passed in registers 3
83362306a36Sopenharmony_ci * @a4: argument passed in registers 4
83462306a36Sopenharmony_ci * @a5: argument passed in registers 5
83562306a36Sopenharmony_ci * @a6: argument passed in registers 6
83662306a36Sopenharmony_ci * @a7: argument passed in registers 7
83762306a36Sopenharmony_ci * @res: result values from register 0 to 3
83862306a36Sopenharmony_ci */
83962306a36Sopenharmony_cistatic void svc_smccc_hvc(unsigned long a0, unsigned long a1,
84062306a36Sopenharmony_ci			  unsigned long a2, unsigned long a3,
84162306a36Sopenharmony_ci			  unsigned long a4, unsigned long a5,
84262306a36Sopenharmony_ci			  unsigned long a6, unsigned long a7,
84362306a36Sopenharmony_ci			  struct arm_smccc_res *res)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci/**
84962306a36Sopenharmony_ci * get_invoke_func() - invoke SMC or HVC call
85062306a36Sopenharmony_ci * @dev: pointer to device
85162306a36Sopenharmony_ci *
85262306a36Sopenharmony_ci * Return: function pointer to svc_smccc_smc or svc_smccc_hvc.
85362306a36Sopenharmony_ci */
85462306a36Sopenharmony_cistatic svc_invoke_fn *get_invoke_func(struct device *dev)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	const char *method;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	if (of_property_read_string(dev->of_node, "method", &method)) {
85962306a36Sopenharmony_ci		dev_warn(dev, "missing \"method\" property\n");
86062306a36Sopenharmony_ci		return ERR_PTR(-ENXIO);
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (!strcmp(method, "smc"))
86462306a36Sopenharmony_ci		return svc_smccc_smc;
86562306a36Sopenharmony_ci	if (!strcmp(method, "hvc"))
86662306a36Sopenharmony_ci		return svc_smccc_hvc;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	dev_warn(dev, "invalid \"method\" property: %s\n", method);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci/**
87462306a36Sopenharmony_ci * stratix10_svc_request_channel_byname() - request a service channel
87562306a36Sopenharmony_ci * @client: pointer to service client
87662306a36Sopenharmony_ci * @name: service client name
87762306a36Sopenharmony_ci *
87862306a36Sopenharmony_ci * This function is used by service client to request a service channel.
87962306a36Sopenharmony_ci *
88062306a36Sopenharmony_ci * Return: a pointer to channel assigned to the client on success,
88162306a36Sopenharmony_ci * or ERR_PTR() on error.
88262306a36Sopenharmony_ci */
88362306a36Sopenharmony_cistruct stratix10_svc_chan *stratix10_svc_request_channel_byname(
88462306a36Sopenharmony_ci	struct stratix10_svc_client *client, const char *name)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	struct device *dev = client->dev;
88762306a36Sopenharmony_ci	struct stratix10_svc_controller *controller;
88862306a36Sopenharmony_ci	struct stratix10_svc_chan *chan = NULL;
88962306a36Sopenharmony_ci	unsigned long flag;
89062306a36Sopenharmony_ci	int i;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/* if probe was called after client's, or error on probe */
89362306a36Sopenharmony_ci	if (list_empty(&svc_ctrl))
89462306a36Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	controller = list_first_entry(&svc_ctrl,
89762306a36Sopenharmony_ci				      struct stratix10_svc_controller, node);
89862306a36Sopenharmony_ci	for (i = 0; i < SVC_NUM_CHANNEL; i++) {
89962306a36Sopenharmony_ci		if (!strcmp(controller->chans[i].name, name)) {
90062306a36Sopenharmony_ci			chan = &controller->chans[i];
90162306a36Sopenharmony_ci			break;
90262306a36Sopenharmony_ci		}
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/* if there was no channel match */
90662306a36Sopenharmony_ci	if (i == SVC_NUM_CHANNEL) {
90762306a36Sopenharmony_ci		dev_err(dev, "%s: channel not allocated\n", __func__);
90862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if (chan->scl || !try_module_get(controller->dev->driver->owner)) {
91262306a36Sopenharmony_ci		dev_dbg(dev, "%s: svc not free\n", __func__);
91362306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flag);
91762306a36Sopenharmony_ci	chan->scl = client;
91862306a36Sopenharmony_ci	chan->ctrl->num_active_client++;
91962306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flag);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return chan;
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(stratix10_svc_request_channel_byname);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci/**
92662306a36Sopenharmony_ci * stratix10_svc_free_channel() - free service channel
92762306a36Sopenharmony_ci * @chan: service channel to be freed
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci * This function is used by service client to free a service channel.
93062306a36Sopenharmony_ci */
93162306a36Sopenharmony_civoid stratix10_svc_free_channel(struct stratix10_svc_chan *chan)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	unsigned long flag;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flag);
93662306a36Sopenharmony_ci	chan->scl = NULL;
93762306a36Sopenharmony_ci	chan->ctrl->num_active_client--;
93862306a36Sopenharmony_ci	module_put(chan->ctrl->dev->driver->owner);
93962306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flag);
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(stratix10_svc_free_channel);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci/**
94462306a36Sopenharmony_ci * stratix10_svc_send() - send a message data to the remote
94562306a36Sopenharmony_ci * @chan: service channel assigned to the client
94662306a36Sopenharmony_ci * @msg: message data to be sent, in the format of
94762306a36Sopenharmony_ci * "struct stratix10_svc_client_msg"
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * This function is used by service client to add a message to the service
95062306a36Sopenharmony_ci * layer driver's queue for being sent to the secure world.
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * Return: 0 for success, -ENOMEM or -ENOBUFS on error.
95362306a36Sopenharmony_ci */
95462306a36Sopenharmony_ciint stratix10_svc_send(struct stratix10_svc_chan *chan, void *msg)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	struct stratix10_svc_client_msg
95762306a36Sopenharmony_ci		*p_msg = (struct stratix10_svc_client_msg *)msg;
95862306a36Sopenharmony_ci	struct stratix10_svc_data_mem *p_mem;
95962306a36Sopenharmony_ci	struct stratix10_svc_data *p_data;
96062306a36Sopenharmony_ci	int ret = 0;
96162306a36Sopenharmony_ci	unsigned int cpu = 0;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	p_data = kzalloc(sizeof(*p_data), GFP_KERNEL);
96462306a36Sopenharmony_ci	if (!p_data)
96562306a36Sopenharmony_ci		return -ENOMEM;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/* first client will create kernel thread */
96862306a36Sopenharmony_ci	if (!chan->ctrl->task) {
96962306a36Sopenharmony_ci		chan->ctrl->task =
97062306a36Sopenharmony_ci			kthread_create_on_node(svc_normal_to_secure_thread,
97162306a36Sopenharmony_ci					      (void *)chan->ctrl,
97262306a36Sopenharmony_ci					      cpu_to_node(cpu),
97362306a36Sopenharmony_ci					      "svc_smc_hvc_thread");
97462306a36Sopenharmony_ci			if (IS_ERR(chan->ctrl->task)) {
97562306a36Sopenharmony_ci				dev_err(chan->ctrl->dev,
97662306a36Sopenharmony_ci					"failed to create svc_smc_hvc_thread\n");
97762306a36Sopenharmony_ci				kfree(p_data);
97862306a36Sopenharmony_ci				return -EINVAL;
97962306a36Sopenharmony_ci			}
98062306a36Sopenharmony_ci		kthread_bind(chan->ctrl->task, cpu);
98162306a36Sopenharmony_ci		wake_up_process(chan->ctrl->task);
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	pr_debug("%s: sent P-va=%p, P-com=%x, P-size=%u\n", __func__,
98562306a36Sopenharmony_ci		 p_msg->payload, p_msg->command,
98662306a36Sopenharmony_ci		 (unsigned int)p_msg->payload_length);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (list_empty(&svc_data_mem)) {
98962306a36Sopenharmony_ci		if (p_msg->command == COMMAND_RECONFIG) {
99062306a36Sopenharmony_ci			struct stratix10_svc_command_config_type *ct =
99162306a36Sopenharmony_ci				(struct stratix10_svc_command_config_type *)
99262306a36Sopenharmony_ci				p_msg->payload;
99362306a36Sopenharmony_ci			p_data->flag = ct->flags;
99462306a36Sopenharmony_ci		}
99562306a36Sopenharmony_ci	} else {
99662306a36Sopenharmony_ci		list_for_each_entry(p_mem, &svc_data_mem, node)
99762306a36Sopenharmony_ci			if (p_mem->vaddr == p_msg->payload) {
99862306a36Sopenharmony_ci				p_data->paddr = p_mem->paddr;
99962306a36Sopenharmony_ci				p_data->size = p_msg->payload_length;
100062306a36Sopenharmony_ci				break;
100162306a36Sopenharmony_ci			}
100262306a36Sopenharmony_ci		if (p_msg->payload_output) {
100362306a36Sopenharmony_ci			list_for_each_entry(p_mem, &svc_data_mem, node)
100462306a36Sopenharmony_ci				if (p_mem->vaddr == p_msg->payload_output) {
100562306a36Sopenharmony_ci					p_data->paddr_output =
100662306a36Sopenharmony_ci						p_mem->paddr;
100762306a36Sopenharmony_ci					p_data->size_output =
100862306a36Sopenharmony_ci						p_msg->payload_length_output;
100962306a36Sopenharmony_ci					break;
101062306a36Sopenharmony_ci				}
101162306a36Sopenharmony_ci		}
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	p_data->command = p_msg->command;
101562306a36Sopenharmony_ci	p_data->arg[0] = p_msg->arg[0];
101662306a36Sopenharmony_ci	p_data->arg[1] = p_msg->arg[1];
101762306a36Sopenharmony_ci	p_data->arg[2] = p_msg->arg[2];
101862306a36Sopenharmony_ci	p_data->size = p_msg->payload_length;
101962306a36Sopenharmony_ci	p_data->chan = chan;
102062306a36Sopenharmony_ci	pr_debug("%s: put to FIFO pa=0x%016x, cmd=%x, size=%u\n", __func__,
102162306a36Sopenharmony_ci	       (unsigned int)p_data->paddr, p_data->command,
102262306a36Sopenharmony_ci	       (unsigned int)p_data->size);
102362306a36Sopenharmony_ci	ret = kfifo_in_spinlocked(&chan->ctrl->svc_fifo, p_data,
102462306a36Sopenharmony_ci				  sizeof(*p_data),
102562306a36Sopenharmony_ci				  &chan->ctrl->svc_fifo_lock);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	kfree(p_data);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (!ret)
103062306a36Sopenharmony_ci		return -ENOBUFS;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	return 0;
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(stratix10_svc_send);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci/**
103762306a36Sopenharmony_ci * stratix10_svc_done() - complete service request transactions
103862306a36Sopenharmony_ci * @chan: service channel assigned to the client
103962306a36Sopenharmony_ci *
104062306a36Sopenharmony_ci * This function should be called when client has finished its request
104162306a36Sopenharmony_ci * or there is an error in the request process. It allows the service layer
104262306a36Sopenharmony_ci * to stop the running thread to have maximize savings in kernel resources.
104362306a36Sopenharmony_ci */
104462306a36Sopenharmony_civoid stratix10_svc_done(struct stratix10_svc_chan *chan)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	/* stop thread when thread is running AND only one active client */
104762306a36Sopenharmony_ci	if (chan->ctrl->task && chan->ctrl->num_active_client <= 1) {
104862306a36Sopenharmony_ci		pr_debug("svc_smc_hvc_shm_thread is stopped\n");
104962306a36Sopenharmony_ci		kthread_stop(chan->ctrl->task);
105062306a36Sopenharmony_ci		chan->ctrl->task = NULL;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(stratix10_svc_done);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci/**
105662306a36Sopenharmony_ci * stratix10_svc_allocate_memory() - allocate memory
105762306a36Sopenharmony_ci * @chan: service channel assigned to the client
105862306a36Sopenharmony_ci * @size: memory size requested by a specific service client
105962306a36Sopenharmony_ci *
106062306a36Sopenharmony_ci * Service layer allocates the requested number of bytes buffer from the
106162306a36Sopenharmony_ci * memory pool, service client uses this function to get allocated buffers.
106262306a36Sopenharmony_ci *
106362306a36Sopenharmony_ci * Return: address of allocated memory on success, or ERR_PTR() on error.
106462306a36Sopenharmony_ci */
106562306a36Sopenharmony_civoid *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan,
106662306a36Sopenharmony_ci				    size_t size)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct stratix10_svc_data_mem *pmem;
106962306a36Sopenharmony_ci	unsigned long va;
107062306a36Sopenharmony_ci	phys_addr_t pa;
107162306a36Sopenharmony_ci	struct gen_pool *genpool = chan->ctrl->genpool;
107262306a36Sopenharmony_ci	size_t s = roundup(size, 1 << genpool->min_alloc_order);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL);
107562306a36Sopenharmony_ci	if (!pmem)
107662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	va = gen_pool_alloc(genpool, s);
107962306a36Sopenharmony_ci	if (!va)
108062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	memset((void *)va, 0, s);
108362306a36Sopenharmony_ci	pa = gen_pool_virt_to_phys(genpool, va);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	pmem->vaddr = (void *)va;
108662306a36Sopenharmony_ci	pmem->paddr = pa;
108762306a36Sopenharmony_ci	pmem->size = s;
108862306a36Sopenharmony_ci	list_add_tail(&pmem->node, &svc_data_mem);
108962306a36Sopenharmony_ci	pr_debug("%s: va=%p, pa=0x%016x\n", __func__,
109062306a36Sopenharmony_ci		 pmem->vaddr, (unsigned int)pmem->paddr);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	return (void *)va;
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(stratix10_svc_allocate_memory);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci/**
109762306a36Sopenharmony_ci * stratix10_svc_free_memory() - free allocated memory
109862306a36Sopenharmony_ci * @chan: service channel assigned to the client
109962306a36Sopenharmony_ci * @kaddr: memory to be freed
110062306a36Sopenharmony_ci *
110162306a36Sopenharmony_ci * This function is used by service client to free allocated buffers.
110262306a36Sopenharmony_ci */
110362306a36Sopenharmony_civoid stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	struct stratix10_svc_data_mem *pmem;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	list_for_each_entry(pmem, &svc_data_mem, node)
110862306a36Sopenharmony_ci		if (pmem->vaddr == kaddr) {
110962306a36Sopenharmony_ci			gen_pool_free(chan->ctrl->genpool,
111062306a36Sopenharmony_ci				       (unsigned long)kaddr, pmem->size);
111162306a36Sopenharmony_ci			pmem->vaddr = NULL;
111262306a36Sopenharmony_ci			list_del(&pmem->node);
111362306a36Sopenharmony_ci			return;
111462306a36Sopenharmony_ci		}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	list_del(&svc_data_mem);
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(stratix10_svc_free_memory);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic const struct of_device_id stratix10_svc_drv_match[] = {
112162306a36Sopenharmony_ci	{.compatible = "intel,stratix10-svc"},
112262306a36Sopenharmony_ci	{.compatible = "intel,agilex-svc"},
112362306a36Sopenharmony_ci	{},
112462306a36Sopenharmony_ci};
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_cistatic int stratix10_svc_drv_probe(struct platform_device *pdev)
112762306a36Sopenharmony_ci{
112862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
112962306a36Sopenharmony_ci	struct stratix10_svc_controller *controller;
113062306a36Sopenharmony_ci	struct stratix10_svc_chan *chans;
113162306a36Sopenharmony_ci	struct gen_pool *genpool;
113262306a36Sopenharmony_ci	struct stratix10_svc_sh_memory *sh_memory;
113362306a36Sopenharmony_ci	struct stratix10_svc *svc;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	svc_invoke_fn *invoke_fn;
113662306a36Sopenharmony_ci	size_t fifo_size;
113762306a36Sopenharmony_ci	int ret;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* get SMC or HVC function */
114062306a36Sopenharmony_ci	invoke_fn = get_invoke_func(dev);
114162306a36Sopenharmony_ci	if (IS_ERR(invoke_fn))
114262306a36Sopenharmony_ci		return -EINVAL;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	sh_memory = devm_kzalloc(dev, sizeof(*sh_memory), GFP_KERNEL);
114562306a36Sopenharmony_ci	if (!sh_memory)
114662306a36Sopenharmony_ci		return -ENOMEM;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	sh_memory->invoke_fn = invoke_fn;
114962306a36Sopenharmony_ci	ret = svc_get_sh_memory(pdev, sh_memory);
115062306a36Sopenharmony_ci	if (ret)
115162306a36Sopenharmony_ci		return ret;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	genpool = svc_create_memory_pool(pdev, sh_memory);
115462306a36Sopenharmony_ci	if (IS_ERR(genpool))
115562306a36Sopenharmony_ci		return PTR_ERR(genpool);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	/* allocate service controller and supporting channel */
115862306a36Sopenharmony_ci	controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
115962306a36Sopenharmony_ci	if (!controller) {
116062306a36Sopenharmony_ci		ret = -ENOMEM;
116162306a36Sopenharmony_ci		goto err_destroy_pool;
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	chans = devm_kmalloc_array(dev, SVC_NUM_CHANNEL,
116562306a36Sopenharmony_ci				   sizeof(*chans), GFP_KERNEL | __GFP_ZERO);
116662306a36Sopenharmony_ci	if (!chans) {
116762306a36Sopenharmony_ci		ret = -ENOMEM;
116862306a36Sopenharmony_ci		goto err_destroy_pool;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	controller->dev = dev;
117262306a36Sopenharmony_ci	controller->num_chans = SVC_NUM_CHANNEL;
117362306a36Sopenharmony_ci	controller->num_active_client = 0;
117462306a36Sopenharmony_ci	controller->chans = chans;
117562306a36Sopenharmony_ci	controller->genpool = genpool;
117662306a36Sopenharmony_ci	controller->task = NULL;
117762306a36Sopenharmony_ci	controller->invoke_fn = invoke_fn;
117862306a36Sopenharmony_ci	init_completion(&controller->complete_status);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	fifo_size = sizeof(struct stratix10_svc_data) * SVC_NUM_DATA_IN_FIFO;
118162306a36Sopenharmony_ci	ret = kfifo_alloc(&controller->svc_fifo, fifo_size, GFP_KERNEL);
118262306a36Sopenharmony_ci	if (ret) {
118362306a36Sopenharmony_ci		dev_err(dev, "failed to allocate FIFO\n");
118462306a36Sopenharmony_ci		goto err_destroy_pool;
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci	spin_lock_init(&controller->svc_fifo_lock);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	chans[0].scl = NULL;
118962306a36Sopenharmony_ci	chans[0].ctrl = controller;
119062306a36Sopenharmony_ci	chans[0].name = SVC_CLIENT_FPGA;
119162306a36Sopenharmony_ci	spin_lock_init(&chans[0].lock);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	chans[1].scl = NULL;
119462306a36Sopenharmony_ci	chans[1].ctrl = controller;
119562306a36Sopenharmony_ci	chans[1].name = SVC_CLIENT_RSU;
119662306a36Sopenharmony_ci	spin_lock_init(&chans[1].lock);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	chans[2].scl = NULL;
119962306a36Sopenharmony_ci	chans[2].ctrl = controller;
120062306a36Sopenharmony_ci	chans[2].name = SVC_CLIENT_FCS;
120162306a36Sopenharmony_ci	spin_lock_init(&chans[2].lock);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	list_add_tail(&controller->node, &svc_ctrl);
120462306a36Sopenharmony_ci	platform_set_drvdata(pdev, controller);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	/* add svc client device(s) */
120762306a36Sopenharmony_ci	svc = devm_kzalloc(dev, sizeof(*svc), GFP_KERNEL);
120862306a36Sopenharmony_ci	if (!svc) {
120962306a36Sopenharmony_ci		ret = -ENOMEM;
121062306a36Sopenharmony_ci		goto err_free_kfifo;
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	svc->stratix10_svc_rsu = platform_device_alloc(STRATIX10_RSU, 0);
121462306a36Sopenharmony_ci	if (!svc->stratix10_svc_rsu) {
121562306a36Sopenharmony_ci		dev_err(dev, "failed to allocate %s device\n", STRATIX10_RSU);
121662306a36Sopenharmony_ci		ret = -ENOMEM;
121762306a36Sopenharmony_ci		goto err_free_kfifo;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	ret = platform_device_add(svc->stratix10_svc_rsu);
122162306a36Sopenharmony_ci	if (ret) {
122262306a36Sopenharmony_ci		platform_device_put(svc->stratix10_svc_rsu);
122362306a36Sopenharmony_ci		goto err_free_kfifo;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	svc->intel_svc_fcs = platform_device_alloc(INTEL_FCS, 1);
122762306a36Sopenharmony_ci	if (!svc->intel_svc_fcs) {
122862306a36Sopenharmony_ci		dev_err(dev, "failed to allocate %s device\n", INTEL_FCS);
122962306a36Sopenharmony_ci		ret = -ENOMEM;
123062306a36Sopenharmony_ci		goto err_unregister_dev;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	ret = platform_device_add(svc->intel_svc_fcs);
123462306a36Sopenharmony_ci	if (ret) {
123562306a36Sopenharmony_ci		platform_device_put(svc->intel_svc_fcs);
123662306a36Sopenharmony_ci		goto err_unregister_dev;
123762306a36Sopenharmony_ci	}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	dev_set_drvdata(dev, svc);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	pr_info("Intel Service Layer Driver Initialized\n");
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	return 0;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cierr_unregister_dev:
124662306a36Sopenharmony_ci	platform_device_unregister(svc->stratix10_svc_rsu);
124762306a36Sopenharmony_cierr_free_kfifo:
124862306a36Sopenharmony_ci	kfifo_free(&controller->svc_fifo);
124962306a36Sopenharmony_cierr_destroy_pool:
125062306a36Sopenharmony_ci	gen_pool_destroy(genpool);
125162306a36Sopenharmony_ci	return ret;
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistatic int stratix10_svc_drv_remove(struct platform_device *pdev)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct stratix10_svc *svc = dev_get_drvdata(&pdev->dev);
125762306a36Sopenharmony_ci	struct stratix10_svc_controller *ctrl = platform_get_drvdata(pdev);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	platform_device_unregister(svc->intel_svc_fcs);
126062306a36Sopenharmony_ci	platform_device_unregister(svc->stratix10_svc_rsu);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	kfifo_free(&ctrl->svc_fifo);
126362306a36Sopenharmony_ci	if (ctrl->task) {
126462306a36Sopenharmony_ci		kthread_stop(ctrl->task);
126562306a36Sopenharmony_ci		ctrl->task = NULL;
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci	if (ctrl->genpool)
126862306a36Sopenharmony_ci		gen_pool_destroy(ctrl->genpool);
126962306a36Sopenharmony_ci	list_del(&ctrl->node);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	return 0;
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic struct platform_driver stratix10_svc_driver = {
127562306a36Sopenharmony_ci	.probe = stratix10_svc_drv_probe,
127662306a36Sopenharmony_ci	.remove = stratix10_svc_drv_remove,
127762306a36Sopenharmony_ci	.driver = {
127862306a36Sopenharmony_ci		.name = "stratix10-svc",
127962306a36Sopenharmony_ci		.of_match_table = stratix10_svc_drv_match,
128062306a36Sopenharmony_ci	},
128162306a36Sopenharmony_ci};
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic int __init stratix10_svc_init(void)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	struct device_node *fw_np;
128662306a36Sopenharmony_ci	struct device_node *np;
128762306a36Sopenharmony_ci	int ret;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	fw_np = of_find_node_by_name(NULL, "firmware");
129062306a36Sopenharmony_ci	if (!fw_np)
129162306a36Sopenharmony_ci		return -ENODEV;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	np = of_find_matching_node(fw_np, stratix10_svc_drv_match);
129462306a36Sopenharmony_ci	if (!np)
129562306a36Sopenharmony_ci		return -ENODEV;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	of_node_put(np);
129862306a36Sopenharmony_ci	ret = of_platform_populate(fw_np, stratix10_svc_drv_match, NULL, NULL);
129962306a36Sopenharmony_ci	if (ret)
130062306a36Sopenharmony_ci		return ret;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	return platform_driver_register(&stratix10_svc_driver);
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cistatic void __exit stratix10_svc_exit(void)
130662306a36Sopenharmony_ci{
130762306a36Sopenharmony_ci	return platform_driver_unregister(&stratix10_svc_driver);
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cisubsys_initcall(stratix10_svc_init);
131162306a36Sopenharmony_cimodule_exit(stratix10_svc_exit);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
131462306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Stratix10 Service Layer Driver");
131562306a36Sopenharmony_ciMODULE_AUTHOR("Richard Gong <richard.gong@intel.com>");
131662306a36Sopenharmony_ciMODULE_ALIAS("platform:stratix10-svc");
1317