162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AMD Cryptographic Coprocessor (CCP) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013,2019 Advanced Micro Devices, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Tom Lendacky <thomas.lendacky@amd.com> 862306a36Sopenharmony_ci * Author: Gary R Hook <gary.hook@amd.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/kthread.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/spinlock.h> 1762306a36Sopenharmony_ci#include <linux/spinlock_types.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci#include <linux/mutex.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/hw_random.h> 2262306a36Sopenharmony_ci#include <linux/cpu.h> 2362306a36Sopenharmony_ci#include <linux/atomic.h> 2462306a36Sopenharmony_ci#ifdef CONFIG_X86 2562306a36Sopenharmony_ci#include <asm/cpu_device_id.h> 2662306a36Sopenharmony_ci#endif 2762306a36Sopenharmony_ci#include <linux/ccp.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "ccp-dev.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MAX_CCPS 32 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Limit CCP use to a specifed number of queues per device */ 3462306a36Sopenharmony_cistatic unsigned int nqueues; 3562306a36Sopenharmony_cimodule_param(nqueues, uint, 0444); 3662306a36Sopenharmony_ciMODULE_PARM_DESC(nqueues, "Number of queues per CCP (minimum 1; default: all available)"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Limit the maximum number of configured CCPs */ 3962306a36Sopenharmony_cistatic atomic_t dev_count = ATOMIC_INIT(0); 4062306a36Sopenharmony_cistatic unsigned int max_devs = MAX_CCPS; 4162306a36Sopenharmony_cimodule_param(max_devs, uint, 0444); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(max_devs, "Maximum number of CCPs to enable (default: all; 0 disables all CCPs)"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct ccp_tasklet_data { 4562306a36Sopenharmony_ci struct completion completion; 4662306a36Sopenharmony_ci struct ccp_cmd *cmd; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Human-readable error strings */ 5062306a36Sopenharmony_ci#define CCP_MAX_ERROR_CODE 64 5162306a36Sopenharmony_cistatic char *ccp_error_codes[] = { 5262306a36Sopenharmony_ci "", 5362306a36Sopenharmony_ci "ILLEGAL_ENGINE", 5462306a36Sopenharmony_ci "ILLEGAL_KEY_ID", 5562306a36Sopenharmony_ci "ILLEGAL_FUNCTION_TYPE", 5662306a36Sopenharmony_ci "ILLEGAL_FUNCTION_MODE", 5762306a36Sopenharmony_ci "ILLEGAL_FUNCTION_ENCRYPT", 5862306a36Sopenharmony_ci "ILLEGAL_FUNCTION_SIZE", 5962306a36Sopenharmony_ci "Zlib_MISSING_INIT_EOM", 6062306a36Sopenharmony_ci "ILLEGAL_FUNCTION_RSVD", 6162306a36Sopenharmony_ci "ILLEGAL_BUFFER_LENGTH", 6262306a36Sopenharmony_ci "VLSB_FAULT", 6362306a36Sopenharmony_ci "ILLEGAL_MEM_ADDR", 6462306a36Sopenharmony_ci "ILLEGAL_MEM_SEL", 6562306a36Sopenharmony_ci "ILLEGAL_CONTEXT_ID", 6662306a36Sopenharmony_ci "ILLEGAL_KEY_ADDR", 6762306a36Sopenharmony_ci "0xF Reserved", 6862306a36Sopenharmony_ci "Zlib_ILLEGAL_MULTI_QUEUE", 6962306a36Sopenharmony_ci "Zlib_ILLEGAL_JOBID_CHANGE", 7062306a36Sopenharmony_ci "CMD_TIMEOUT", 7162306a36Sopenharmony_ci "IDMA0_AXI_SLVERR", 7262306a36Sopenharmony_ci "IDMA0_AXI_DECERR", 7362306a36Sopenharmony_ci "0x15 Reserved", 7462306a36Sopenharmony_ci "IDMA1_AXI_SLAVE_FAULT", 7562306a36Sopenharmony_ci "IDMA1_AIXI_DECERR", 7662306a36Sopenharmony_ci "0x18 Reserved", 7762306a36Sopenharmony_ci "ZLIBVHB_AXI_SLVERR", 7862306a36Sopenharmony_ci "ZLIBVHB_AXI_DECERR", 7962306a36Sopenharmony_ci "0x1B Reserved", 8062306a36Sopenharmony_ci "ZLIB_UNEXPECTED_EOM", 8162306a36Sopenharmony_ci "ZLIB_EXTRA_DATA", 8262306a36Sopenharmony_ci "ZLIB_BTYPE", 8362306a36Sopenharmony_ci "ZLIB_UNDEFINED_SYMBOL", 8462306a36Sopenharmony_ci "ZLIB_UNDEFINED_DISTANCE_S", 8562306a36Sopenharmony_ci "ZLIB_CODE_LENGTH_SYMBOL", 8662306a36Sopenharmony_ci "ZLIB _VHB_ILLEGAL_FETCH", 8762306a36Sopenharmony_ci "ZLIB_UNCOMPRESSED_LEN", 8862306a36Sopenharmony_ci "ZLIB_LIMIT_REACHED", 8962306a36Sopenharmony_ci "ZLIB_CHECKSUM_MISMATCH0", 9062306a36Sopenharmony_ci "ODMA0_AXI_SLVERR", 9162306a36Sopenharmony_ci "ODMA0_AXI_DECERR", 9262306a36Sopenharmony_ci "0x28 Reserved", 9362306a36Sopenharmony_ci "ODMA1_AXI_SLVERR", 9462306a36Sopenharmony_ci "ODMA1_AXI_DECERR", 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_civoid ccp_log_error(struct ccp_device *d, unsigned int e) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci if (WARN_ON(e >= CCP_MAX_ERROR_CODE)) 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (e < ARRAY_SIZE(ccp_error_codes)) 10362306a36Sopenharmony_ci dev_err(d->dev, "CCP error %d: %s\n", e, ccp_error_codes[e]); 10462306a36Sopenharmony_ci else 10562306a36Sopenharmony_ci dev_err(d->dev, "CCP error %d: Unknown Error\n", e); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* List of CCPs, CCP count, read-write access lock, and access functions 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * Lock structure: get ccp_unit_lock for reading whenever we need to 11162306a36Sopenharmony_ci * examine the CCP list. While holding it for reading we can acquire 11262306a36Sopenharmony_ci * the RR lock to update the round-robin next-CCP pointer. The unit lock 11362306a36Sopenharmony_ci * must be acquired before the RR lock. 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * If the unit-lock is acquired for writing, we have total control over 11662306a36Sopenharmony_ci * the list, so there's no value in getting the RR lock. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic DEFINE_RWLOCK(ccp_unit_lock); 11962306a36Sopenharmony_cistatic LIST_HEAD(ccp_units); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* Round-robin counter */ 12262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ccp_rr_lock); 12362306a36Sopenharmony_cistatic struct ccp_device *ccp_rr; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/** 12662306a36Sopenharmony_ci * ccp_add_device - add a CCP device to the list 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * @ccp: ccp_device struct pointer 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * Put this CCP on the unit list, which makes it available 13162306a36Sopenharmony_ci * for use. 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Returns zero if a CCP device is present, -ENODEV otherwise. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_civoid ccp_add_device(struct ccp_device *ccp) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci unsigned long flags; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci write_lock_irqsave(&ccp_unit_lock, flags); 14062306a36Sopenharmony_ci list_add_tail(&ccp->entry, &ccp_units); 14162306a36Sopenharmony_ci if (!ccp_rr) 14262306a36Sopenharmony_ci /* We already have the list lock (we're first) so this 14362306a36Sopenharmony_ci * pointer can't change on us. Set its initial value. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci ccp_rr = ccp; 14662306a36Sopenharmony_ci write_unlock_irqrestore(&ccp_unit_lock, flags); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/** 15062306a36Sopenharmony_ci * ccp_del_device - remove a CCP device from the list 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * @ccp: ccp_device struct pointer 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * Remove this unit from the list of devices. If the next device 15562306a36Sopenharmony_ci * up for use is this one, adjust the pointer. If this is the last 15662306a36Sopenharmony_ci * device, NULL the pointer. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_civoid ccp_del_device(struct ccp_device *ccp) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci unsigned long flags; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci write_lock_irqsave(&ccp_unit_lock, flags); 16362306a36Sopenharmony_ci if (ccp_rr == ccp) { 16462306a36Sopenharmony_ci /* ccp_unit_lock is read/write; any read access 16562306a36Sopenharmony_ci * will be suspended while we make changes to the 16662306a36Sopenharmony_ci * list and RR pointer. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci if (list_is_last(&ccp_rr->entry, &ccp_units)) 16962306a36Sopenharmony_ci ccp_rr = list_first_entry(&ccp_units, struct ccp_device, 17062306a36Sopenharmony_ci entry); 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci ccp_rr = list_next_entry(ccp_rr, entry); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci list_del(&ccp->entry); 17562306a36Sopenharmony_ci if (list_empty(&ccp_units)) 17662306a36Sopenharmony_ci ccp_rr = NULL; 17762306a36Sopenharmony_ci write_unlock_irqrestore(&ccp_unit_lock, flags); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciint ccp_register_rng(struct ccp_device *ccp) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci int ret = 0; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci dev_dbg(ccp->dev, "Registering RNG...\n"); 18762306a36Sopenharmony_ci /* Register an RNG */ 18862306a36Sopenharmony_ci ccp->hwrng.name = ccp->rngname; 18962306a36Sopenharmony_ci ccp->hwrng.read = ccp_trng_read; 19062306a36Sopenharmony_ci ret = hwrng_register(&ccp->hwrng); 19162306a36Sopenharmony_ci if (ret) 19262306a36Sopenharmony_ci dev_err(ccp->dev, "error registering hwrng (%d)\n", ret); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return ret; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_civoid ccp_unregister_rng(struct ccp_device *ccp) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci if (ccp->hwrng.name) 20062306a36Sopenharmony_ci hwrng_unregister(&ccp->hwrng); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic struct ccp_device *ccp_get_device(void) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci unsigned long flags; 20662306a36Sopenharmony_ci struct ccp_device *dp = NULL; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* We round-robin through the unit list. 20962306a36Sopenharmony_ci * The (ccp_rr) pointer refers to the next unit to use. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci read_lock_irqsave(&ccp_unit_lock, flags); 21262306a36Sopenharmony_ci if (!list_empty(&ccp_units)) { 21362306a36Sopenharmony_ci spin_lock(&ccp_rr_lock); 21462306a36Sopenharmony_ci dp = ccp_rr; 21562306a36Sopenharmony_ci if (list_is_last(&ccp_rr->entry, &ccp_units)) 21662306a36Sopenharmony_ci ccp_rr = list_first_entry(&ccp_units, struct ccp_device, 21762306a36Sopenharmony_ci entry); 21862306a36Sopenharmony_ci else 21962306a36Sopenharmony_ci ccp_rr = list_next_entry(ccp_rr, entry); 22062306a36Sopenharmony_ci spin_unlock(&ccp_rr_lock); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci read_unlock_irqrestore(&ccp_unit_lock, flags); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return dp; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/** 22862306a36Sopenharmony_ci * ccp_present - check if a CCP device is present 22962306a36Sopenharmony_ci * 23062306a36Sopenharmony_ci * Returns zero if a CCP device is present, -ENODEV otherwise. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ciint ccp_present(void) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci unsigned long flags; 23562306a36Sopenharmony_ci int ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci read_lock_irqsave(&ccp_unit_lock, flags); 23862306a36Sopenharmony_ci ret = list_empty(&ccp_units); 23962306a36Sopenharmony_ci read_unlock_irqrestore(&ccp_unit_lock, flags); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return ret ? -ENODEV : 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccp_present); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * ccp_version - get the version of the CCP device 24762306a36Sopenharmony_ci * 24862306a36Sopenharmony_ci * Returns the version from the first unit on the list; 24962306a36Sopenharmony_ci * otherwise a zero if no CCP device is present 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ciunsigned int ccp_version(void) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct ccp_device *dp; 25462306a36Sopenharmony_ci unsigned long flags; 25562306a36Sopenharmony_ci int ret = 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci read_lock_irqsave(&ccp_unit_lock, flags); 25862306a36Sopenharmony_ci if (!list_empty(&ccp_units)) { 25962306a36Sopenharmony_ci dp = list_first_entry(&ccp_units, struct ccp_device, entry); 26062306a36Sopenharmony_ci ret = dp->vdata->version; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci read_unlock_irqrestore(&ccp_unit_lock, flags); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccp_version); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/** 26962306a36Sopenharmony_ci * ccp_enqueue_cmd - queue an operation for processing by the CCP 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * @cmd: ccp_cmd struct to be processed 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * Queue a cmd to be processed by the CCP. If queueing the cmd 27462306a36Sopenharmony_ci * would exceed the defined length of the cmd queue the cmd will 27562306a36Sopenharmony_ci * only be queued if the CCP_CMD_MAY_BACKLOG flag is set and will 27662306a36Sopenharmony_ci * result in a return code of -EBUSY. 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * The callback routine specified in the ccp_cmd struct will be 27962306a36Sopenharmony_ci * called to notify the caller of completion (if the cmd was not 28062306a36Sopenharmony_ci * backlogged) or advancement out of the backlog. If the cmd has 28162306a36Sopenharmony_ci * advanced out of the backlog the "err" value of the callback 28262306a36Sopenharmony_ci * will be -EINPROGRESS. Any other "err" value during callback is 28362306a36Sopenharmony_ci * the result of the operation. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * The cmd has been successfully queued if: 28662306a36Sopenharmony_ci * the return code is -EINPROGRESS or 28762306a36Sopenharmony_ci * the return code is -EBUSY and CCP_CMD_MAY_BACKLOG flag is set 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ciint ccp_enqueue_cmd(struct ccp_cmd *cmd) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct ccp_device *ccp; 29262306a36Sopenharmony_ci unsigned long flags; 29362306a36Sopenharmony_ci unsigned int i; 29462306a36Sopenharmony_ci int ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Some commands might need to be sent to a specific device */ 29762306a36Sopenharmony_ci ccp = cmd->ccp ? cmd->ccp : ccp_get_device(); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!ccp) 30062306a36Sopenharmony_ci return -ENODEV; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Caller must supply a callback routine */ 30362306a36Sopenharmony_ci if (!cmd->callback) 30462306a36Sopenharmony_ci return -EINVAL; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci cmd->ccp = ccp; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci spin_lock_irqsave(&ccp->cmd_lock, flags); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci i = ccp->cmd_q_count; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (ccp->cmd_count >= MAX_CMD_QLEN) { 31362306a36Sopenharmony_ci if (cmd->flags & CCP_CMD_MAY_BACKLOG) { 31462306a36Sopenharmony_ci ret = -EBUSY; 31562306a36Sopenharmony_ci list_add_tail(&cmd->entry, &ccp->backlog); 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci ret = -ENOSPC; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci } else { 32062306a36Sopenharmony_ci ret = -EINPROGRESS; 32162306a36Sopenharmony_ci ccp->cmd_count++; 32262306a36Sopenharmony_ci list_add_tail(&cmd->entry, &ccp->cmd); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Find an idle queue */ 32562306a36Sopenharmony_ci if (!ccp->suspending) { 32662306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) { 32762306a36Sopenharmony_ci if (ccp->cmd_q[i].active) 32862306a36Sopenharmony_ci continue; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci spin_unlock_irqrestore(&ccp->cmd_lock, flags); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* If we found an idle queue, wake it up */ 33862306a36Sopenharmony_ci if (i < ccp->cmd_q_count) 33962306a36Sopenharmony_ci wake_up_process(ccp->cmd_q[i].kthread); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccp_enqueue_cmd); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void ccp_do_cmd_backlog(struct work_struct *work) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct ccp_cmd *cmd = container_of(work, struct ccp_cmd, work); 34862306a36Sopenharmony_ci struct ccp_device *ccp = cmd->ccp; 34962306a36Sopenharmony_ci unsigned long flags; 35062306a36Sopenharmony_ci unsigned int i; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci cmd->callback(cmd->data, -EINPROGRESS); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci spin_lock_irqsave(&ccp->cmd_lock, flags); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ccp->cmd_count++; 35762306a36Sopenharmony_ci list_add_tail(&cmd->entry, &ccp->cmd); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* Find an idle queue */ 36062306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) { 36162306a36Sopenharmony_ci if (ccp->cmd_q[i].active) 36262306a36Sopenharmony_ci continue; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci spin_unlock_irqrestore(&ccp->cmd_lock, flags); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* If we found an idle queue, wake it up */ 37062306a36Sopenharmony_ci if (i < ccp->cmd_q_count) 37162306a36Sopenharmony_ci wake_up_process(ccp->cmd_q[i].kthread); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic struct ccp_cmd *ccp_dequeue_cmd(struct ccp_cmd_queue *cmd_q) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct ccp_device *ccp = cmd_q->ccp; 37762306a36Sopenharmony_ci struct ccp_cmd *cmd = NULL; 37862306a36Sopenharmony_ci struct ccp_cmd *backlog = NULL; 37962306a36Sopenharmony_ci unsigned long flags; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci spin_lock_irqsave(&ccp->cmd_lock, flags); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci cmd_q->active = 0; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (ccp->suspending) { 38662306a36Sopenharmony_ci cmd_q->suspended = 1; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci spin_unlock_irqrestore(&ccp->cmd_lock, flags); 38962306a36Sopenharmony_ci wake_up_interruptible(&ccp->suspend_queue); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return NULL; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (ccp->cmd_count) { 39562306a36Sopenharmony_ci cmd_q->active = 1; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci cmd = list_first_entry(&ccp->cmd, struct ccp_cmd, entry); 39862306a36Sopenharmony_ci list_del(&cmd->entry); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ccp->cmd_count--; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (!list_empty(&ccp->backlog)) { 40462306a36Sopenharmony_ci backlog = list_first_entry(&ccp->backlog, struct ccp_cmd, 40562306a36Sopenharmony_ci entry); 40662306a36Sopenharmony_ci list_del(&backlog->entry); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci spin_unlock_irqrestore(&ccp->cmd_lock, flags); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (backlog) { 41262306a36Sopenharmony_ci INIT_WORK(&backlog->work, ccp_do_cmd_backlog); 41362306a36Sopenharmony_ci schedule_work(&backlog->work); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return cmd; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void ccp_do_cmd_complete(unsigned long data) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct ccp_tasklet_data *tdata = (struct ccp_tasklet_data *)data; 42262306a36Sopenharmony_ci struct ccp_cmd *cmd = tdata->cmd; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci cmd->callback(cmd->data, cmd->ret); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci complete(&tdata->completion); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * ccp_cmd_queue_thread - create a kernel thread to manage a CCP queue 43162306a36Sopenharmony_ci * 43262306a36Sopenharmony_ci * @data: thread-specific data 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ciint ccp_cmd_queue_thread(void *data) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct ccp_cmd_queue *cmd_q = (struct ccp_cmd_queue *)data; 43762306a36Sopenharmony_ci struct ccp_cmd *cmd; 43862306a36Sopenharmony_ci struct ccp_tasklet_data tdata; 43962306a36Sopenharmony_ci struct tasklet_struct tasklet; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci tasklet_init(&tasklet, ccp_do_cmd_complete, (unsigned long)&tdata); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 44462306a36Sopenharmony_ci while (!kthread_should_stop()) { 44562306a36Sopenharmony_ci schedule(); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci cmd = ccp_dequeue_cmd(cmd_q); 45062306a36Sopenharmony_ci if (!cmd) 45162306a36Sopenharmony_ci continue; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Execute the command */ 45662306a36Sopenharmony_ci cmd->ret = ccp_run_cmd(cmd_q, cmd); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Schedule the completion callback */ 45962306a36Sopenharmony_ci tdata.cmd = cmd; 46062306a36Sopenharmony_ci init_completion(&tdata.completion); 46162306a36Sopenharmony_ci tasklet_schedule(&tasklet); 46262306a36Sopenharmony_ci wait_for_completion(&tdata.completion); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/** 47162306a36Sopenharmony_ci * ccp_alloc_struct - allocate and initialize the ccp_device struct 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * @sp: sp_device struct of the CCP 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_cistruct ccp_device *ccp_alloc_struct(struct sp_device *sp) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct device *dev = sp->dev; 47862306a36Sopenharmony_ci struct ccp_device *ccp; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ccp = devm_kzalloc(dev, sizeof(*ccp), GFP_KERNEL); 48162306a36Sopenharmony_ci if (!ccp) 48262306a36Sopenharmony_ci return NULL; 48362306a36Sopenharmony_ci ccp->dev = dev; 48462306a36Sopenharmony_ci ccp->sp = sp; 48562306a36Sopenharmony_ci ccp->axcache = sp->axcache; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci INIT_LIST_HEAD(&ccp->cmd); 48862306a36Sopenharmony_ci INIT_LIST_HEAD(&ccp->backlog); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci spin_lock_init(&ccp->cmd_lock); 49162306a36Sopenharmony_ci mutex_init(&ccp->req_mutex); 49262306a36Sopenharmony_ci mutex_init(&ccp->sb_mutex); 49362306a36Sopenharmony_ci ccp->sb_count = KSB_COUNT; 49462306a36Sopenharmony_ci ccp->sb_start = 0; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* Initialize the wait queues */ 49762306a36Sopenharmony_ci init_waitqueue_head(&ccp->sb_queue); 49862306a36Sopenharmony_ci init_waitqueue_head(&ccp->suspend_queue); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci snprintf(ccp->name, MAX_CCP_NAME_LEN, "ccp-%u", sp->ord); 50162306a36Sopenharmony_ci snprintf(ccp->rngname, MAX_CCP_NAME_LEN, "ccp-%u-rng", sp->ord); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return ccp; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciint ccp_trng_read(struct hwrng *rng, void *data, size_t max, bool wait) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct ccp_device *ccp = container_of(rng, struct ccp_device, hwrng); 50962306a36Sopenharmony_ci u32 trng_value; 51062306a36Sopenharmony_ci int len = min_t(int, sizeof(trng_value), max); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Locking is provided by the caller so we can update device 51362306a36Sopenharmony_ci * hwrng-related fields safely 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci trng_value = ioread32(ccp->io_regs + TRNG_OUT_REG); 51662306a36Sopenharmony_ci if (!trng_value) { 51762306a36Sopenharmony_ci /* Zero is returned if not data is available or if a 51862306a36Sopenharmony_ci * bad-entropy error is present. Assume an error if 51962306a36Sopenharmony_ci * we exceed TRNG_RETRIES reads of zero. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci if (ccp->hwrng_retries++ > TRNG_RETRIES) 52262306a36Sopenharmony_ci return -EIO; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Reset the counter and save the rng value */ 52862306a36Sopenharmony_ci ccp->hwrng_retries = 0; 52962306a36Sopenharmony_ci memcpy(data, &trng_value, len); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return len; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cibool ccp_queues_suspended(struct ccp_device *ccp) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci unsigned int suspended = 0; 53762306a36Sopenharmony_ci unsigned long flags; 53862306a36Sopenharmony_ci unsigned int i; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci spin_lock_irqsave(&ccp->cmd_lock, flags); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) 54362306a36Sopenharmony_ci if (ccp->cmd_q[i].suspended) 54462306a36Sopenharmony_ci suspended++; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci spin_unlock_irqrestore(&ccp->cmd_lock, flags); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return ccp->cmd_q_count == suspended; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_civoid ccp_dev_suspend(struct sp_device *sp) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct ccp_device *ccp = sp->ccp_data; 55462306a36Sopenharmony_ci unsigned long flags; 55562306a36Sopenharmony_ci unsigned int i; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* If there's no device there's nothing to do */ 55862306a36Sopenharmony_ci if (!ccp) 55962306a36Sopenharmony_ci return; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci spin_lock_irqsave(&ccp->cmd_lock, flags); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ccp->suspending = 1; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Wake all the queue kthreads to prepare for suspend */ 56662306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) 56762306a36Sopenharmony_ci wake_up_process(ccp->cmd_q[i].kthread); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci spin_unlock_irqrestore(&ccp->cmd_lock, flags); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* Wait for all queue kthreads to say they're done */ 57262306a36Sopenharmony_ci while (!ccp_queues_suspended(ccp)) 57362306a36Sopenharmony_ci wait_event_interruptible(ccp->suspend_queue, 57462306a36Sopenharmony_ci ccp_queues_suspended(ccp)); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_civoid ccp_dev_resume(struct sp_device *sp) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct ccp_device *ccp = sp->ccp_data; 58062306a36Sopenharmony_ci unsigned long flags; 58162306a36Sopenharmony_ci unsigned int i; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* If there's no device there's nothing to do */ 58462306a36Sopenharmony_ci if (!ccp) 58562306a36Sopenharmony_ci return; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci spin_lock_irqsave(&ccp->cmd_lock, flags); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ccp->suspending = 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Wake up all the kthreads */ 59262306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) { 59362306a36Sopenharmony_ci ccp->cmd_q[i].suspended = 0; 59462306a36Sopenharmony_ci wake_up_process(ccp->cmd_q[i].kthread); 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci spin_unlock_irqrestore(&ccp->cmd_lock, flags); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ciint ccp_dev_init(struct sp_device *sp) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct device *dev = sp->dev; 60362306a36Sopenharmony_ci struct ccp_device *ccp; 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * Check how many we have so far, and stop after reaching 60862306a36Sopenharmony_ci * that number 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci if (atomic_inc_return(&dev_count) > max_devs) 61162306a36Sopenharmony_ci return 0; /* don't fail the load */ 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci ret = -ENOMEM; 61462306a36Sopenharmony_ci ccp = ccp_alloc_struct(sp); 61562306a36Sopenharmony_ci if (!ccp) 61662306a36Sopenharmony_ci goto e_err; 61762306a36Sopenharmony_ci sp->ccp_data = ccp; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!nqueues || (nqueues > MAX_HW_QUEUES)) 62062306a36Sopenharmony_ci ccp->max_q_count = MAX_HW_QUEUES; 62162306a36Sopenharmony_ci else 62262306a36Sopenharmony_ci ccp->max_q_count = nqueues; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci ccp->vdata = (struct ccp_vdata *)sp->dev_vdata->ccp_vdata; 62562306a36Sopenharmony_ci if (!ccp->vdata || !ccp->vdata->version) { 62662306a36Sopenharmony_ci ret = -ENODEV; 62762306a36Sopenharmony_ci dev_err(dev, "missing driver data\n"); 62862306a36Sopenharmony_ci goto e_err; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci ccp->use_tasklet = sp->use_tasklet; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ccp->io_regs = sp->io_map + ccp->vdata->offset; 63462306a36Sopenharmony_ci if (ccp->vdata->setup) 63562306a36Sopenharmony_ci ccp->vdata->setup(ccp); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ret = ccp->vdata->perform->init(ccp); 63862306a36Sopenharmony_ci if (ret) { 63962306a36Sopenharmony_ci /* A positive number means that the device cannot be initialized, 64062306a36Sopenharmony_ci * but no additional message is required. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ci if (ret > 0) 64362306a36Sopenharmony_ci goto e_quiet; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* An unexpected problem occurred, and should be reported in the log */ 64662306a36Sopenharmony_ci goto e_err; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci dev_notice(dev, "ccp enabled\n"); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cie_err: 65462306a36Sopenharmony_ci dev_notice(dev, "ccp initialization failed\n"); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cie_quiet: 65762306a36Sopenharmony_ci sp->ccp_data = NULL; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return ret; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_civoid ccp_dev_destroy(struct sp_device *sp) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct ccp_device *ccp = sp->ccp_data; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (!ccp) 66762306a36Sopenharmony_ci return; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci ccp->vdata->perform->destroy(ccp); 67062306a36Sopenharmony_ci} 671