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