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,2017 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/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/ccp.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "ccp-dev.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic u32 ccp_alloc_ksb(struct ccp_cmd_queue *cmd_q, unsigned int count) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci int start; 2262306a36Sopenharmony_ci struct ccp_device *ccp = cmd_q->ccp; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci for (;;) { 2562306a36Sopenharmony_ci mutex_lock(&ccp->sb_mutex); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci start = (u32)bitmap_find_next_zero_area(ccp->sb, 2862306a36Sopenharmony_ci ccp->sb_count, 2962306a36Sopenharmony_ci ccp->sb_start, 3062306a36Sopenharmony_ci count, 0); 3162306a36Sopenharmony_ci if (start <= ccp->sb_count) { 3262306a36Sopenharmony_ci bitmap_set(ccp->sb, start, count); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci mutex_unlock(&ccp->sb_mutex); 3562306a36Sopenharmony_ci break; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci ccp->sb_avail = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci mutex_unlock(&ccp->sb_mutex); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Wait for KSB entries to become available */ 4362306a36Sopenharmony_ci if (wait_event_interruptible(ccp->sb_queue, ccp->sb_avail)) 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return KSB_START + start; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void ccp_free_ksb(struct ccp_cmd_queue *cmd_q, unsigned int start, 5162306a36Sopenharmony_ci unsigned int count) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct ccp_device *ccp = cmd_q->ccp; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (!start) 5662306a36Sopenharmony_ci return; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci mutex_lock(&ccp->sb_mutex); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci bitmap_clear(ccp->sb, start - KSB_START, count); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ccp->sb_avail = 1; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci mutex_unlock(&ccp->sb_mutex); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci wake_up_interruptible_all(&ccp->sb_queue); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic unsigned int ccp_get_free_slots(struct ccp_cmd_queue *cmd_q) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return CMD_Q_DEPTH(ioread32(cmd_q->reg_status)); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int ccp_do_cmd(struct ccp_op *op, u32 *cr, unsigned int cr_count) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct ccp_cmd_queue *cmd_q = op->cmd_q; 7762306a36Sopenharmony_ci struct ccp_device *ccp = cmd_q->ccp; 7862306a36Sopenharmony_ci void __iomem *cr_addr; 7962306a36Sopenharmony_ci u32 cr0, cmd; 8062306a36Sopenharmony_ci unsigned int i; 8162306a36Sopenharmony_ci int ret = 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* We could read a status register to see how many free slots 8462306a36Sopenharmony_ci * are actually available, but reading that register resets it 8562306a36Sopenharmony_ci * and you could lose some error information. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci cmd_q->free_slots--; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci cr0 = (cmd_q->id << REQ0_CMD_Q_SHIFT) 9062306a36Sopenharmony_ci | (op->jobid << REQ0_JOBID_SHIFT) 9162306a36Sopenharmony_ci | REQ0_WAIT_FOR_WRITE; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (op->soc) 9462306a36Sopenharmony_ci cr0 |= REQ0_STOP_ON_COMPLETE 9562306a36Sopenharmony_ci | REQ0_INT_ON_COMPLETE; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (op->ioc || !cmd_q->free_slots) 9862306a36Sopenharmony_ci cr0 |= REQ0_INT_ON_COMPLETE; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Start at CMD_REQ1 */ 10162306a36Sopenharmony_ci cr_addr = ccp->io_regs + CMD_REQ0 + CMD_REQ_INCR; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci mutex_lock(&ccp->req_mutex); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Write CMD_REQ1 through CMD_REQx first */ 10662306a36Sopenharmony_ci for (i = 0; i < cr_count; i++, cr_addr += CMD_REQ_INCR) 10762306a36Sopenharmony_ci iowrite32(*(cr + i), cr_addr); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Tell the CCP to start */ 11062306a36Sopenharmony_ci wmb(); 11162306a36Sopenharmony_ci iowrite32(cr0, ccp->io_regs + CMD_REQ0); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci mutex_unlock(&ccp->req_mutex); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (cr0 & REQ0_INT_ON_COMPLETE) { 11662306a36Sopenharmony_ci /* Wait for the job to complete */ 11762306a36Sopenharmony_ci ret = wait_event_interruptible(cmd_q->int_queue, 11862306a36Sopenharmony_ci cmd_q->int_rcvd); 11962306a36Sopenharmony_ci if (ret || cmd_q->cmd_error) { 12062306a36Sopenharmony_ci /* On error delete all related jobs from the queue */ 12162306a36Sopenharmony_ci cmd = (cmd_q->id << DEL_Q_ID_SHIFT) 12262306a36Sopenharmony_ci | op->jobid; 12362306a36Sopenharmony_ci if (cmd_q->cmd_error) 12462306a36Sopenharmony_ci ccp_log_error(cmd_q->ccp, 12562306a36Sopenharmony_ci cmd_q->cmd_error); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci iowrite32(cmd, ccp->io_regs + DEL_CMD_Q_JOB); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!ret) 13062306a36Sopenharmony_ci ret = -EIO; 13162306a36Sopenharmony_ci } else if (op->soc) { 13262306a36Sopenharmony_ci /* Delete just head job from the queue on SoC */ 13362306a36Sopenharmony_ci cmd = DEL_Q_ACTIVE 13462306a36Sopenharmony_ci | (cmd_q->id << DEL_Q_ID_SHIFT) 13562306a36Sopenharmony_ci | op->jobid; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci iowrite32(cmd, ccp->io_regs + DEL_CMD_Q_JOB); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci cmd_q->free_slots = CMD_Q_DEPTH(cmd_q->q_status); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci cmd_q->int_rcvd = 0; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int ccp_perform_aes(struct ccp_op *op) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci u32 cr[6]; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Fill out the register contents for REQ1 through REQ6 */ 15362306a36Sopenharmony_ci cr[0] = (CCP_ENGINE_AES << REQ1_ENGINE_SHIFT) 15462306a36Sopenharmony_ci | (op->u.aes.type << REQ1_AES_TYPE_SHIFT) 15562306a36Sopenharmony_ci | (op->u.aes.mode << REQ1_AES_MODE_SHIFT) 15662306a36Sopenharmony_ci | (op->u.aes.action << REQ1_AES_ACTION_SHIFT) 15762306a36Sopenharmony_ci | (op->sb_key << REQ1_KEY_KSB_SHIFT); 15862306a36Sopenharmony_ci cr[1] = op->src.u.dma.length - 1; 15962306a36Sopenharmony_ci cr[2] = ccp_addr_lo(&op->src.u.dma); 16062306a36Sopenharmony_ci cr[3] = (op->sb_ctx << REQ4_KSB_SHIFT) 16162306a36Sopenharmony_ci | (CCP_MEMTYPE_SYSTEM << REQ4_MEMTYPE_SHIFT) 16262306a36Sopenharmony_ci | ccp_addr_hi(&op->src.u.dma); 16362306a36Sopenharmony_ci cr[4] = ccp_addr_lo(&op->dst.u.dma); 16462306a36Sopenharmony_ci cr[5] = (CCP_MEMTYPE_SYSTEM << REQ6_MEMTYPE_SHIFT) 16562306a36Sopenharmony_ci | ccp_addr_hi(&op->dst.u.dma); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (op->u.aes.mode == CCP_AES_MODE_CFB) 16862306a36Sopenharmony_ci cr[0] |= ((0x7f) << REQ1_AES_CFB_SIZE_SHIFT); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (op->eom) 17162306a36Sopenharmony_ci cr[0] |= REQ1_EOM; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (op->init) 17462306a36Sopenharmony_ci cr[0] |= REQ1_INIT; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return ccp_do_cmd(op, cr, ARRAY_SIZE(cr)); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int ccp_perform_xts_aes(struct ccp_op *op) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci u32 cr[6]; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Fill out the register contents for REQ1 through REQ6 */ 18462306a36Sopenharmony_ci cr[0] = (CCP_ENGINE_XTS_AES_128 << REQ1_ENGINE_SHIFT) 18562306a36Sopenharmony_ci | (op->u.xts.action << REQ1_AES_ACTION_SHIFT) 18662306a36Sopenharmony_ci | (op->u.xts.unit_size << REQ1_XTS_AES_SIZE_SHIFT) 18762306a36Sopenharmony_ci | (op->sb_key << REQ1_KEY_KSB_SHIFT); 18862306a36Sopenharmony_ci cr[1] = op->src.u.dma.length - 1; 18962306a36Sopenharmony_ci cr[2] = ccp_addr_lo(&op->src.u.dma); 19062306a36Sopenharmony_ci cr[3] = (op->sb_ctx << REQ4_KSB_SHIFT) 19162306a36Sopenharmony_ci | (CCP_MEMTYPE_SYSTEM << REQ4_MEMTYPE_SHIFT) 19262306a36Sopenharmony_ci | ccp_addr_hi(&op->src.u.dma); 19362306a36Sopenharmony_ci cr[4] = ccp_addr_lo(&op->dst.u.dma); 19462306a36Sopenharmony_ci cr[5] = (CCP_MEMTYPE_SYSTEM << REQ6_MEMTYPE_SHIFT) 19562306a36Sopenharmony_ci | ccp_addr_hi(&op->dst.u.dma); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (op->eom) 19862306a36Sopenharmony_ci cr[0] |= REQ1_EOM; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (op->init) 20162306a36Sopenharmony_ci cr[0] |= REQ1_INIT; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return ccp_do_cmd(op, cr, ARRAY_SIZE(cr)); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int ccp_perform_sha(struct ccp_op *op) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci u32 cr[6]; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Fill out the register contents for REQ1 through REQ6 */ 21162306a36Sopenharmony_ci cr[0] = (CCP_ENGINE_SHA << REQ1_ENGINE_SHIFT) 21262306a36Sopenharmony_ci | (op->u.sha.type << REQ1_SHA_TYPE_SHIFT) 21362306a36Sopenharmony_ci | REQ1_INIT; 21462306a36Sopenharmony_ci cr[1] = op->src.u.dma.length - 1; 21562306a36Sopenharmony_ci cr[2] = ccp_addr_lo(&op->src.u.dma); 21662306a36Sopenharmony_ci cr[3] = (op->sb_ctx << REQ4_KSB_SHIFT) 21762306a36Sopenharmony_ci | (CCP_MEMTYPE_SYSTEM << REQ4_MEMTYPE_SHIFT) 21862306a36Sopenharmony_ci | ccp_addr_hi(&op->src.u.dma); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (op->eom) { 22162306a36Sopenharmony_ci cr[0] |= REQ1_EOM; 22262306a36Sopenharmony_ci cr[4] = lower_32_bits(op->u.sha.msg_bits); 22362306a36Sopenharmony_ci cr[5] = upper_32_bits(op->u.sha.msg_bits); 22462306a36Sopenharmony_ci } else { 22562306a36Sopenharmony_ci cr[4] = 0; 22662306a36Sopenharmony_ci cr[5] = 0; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return ccp_do_cmd(op, cr, ARRAY_SIZE(cr)); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int ccp_perform_rsa(struct ccp_op *op) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci u32 cr[6]; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Fill out the register contents for REQ1 through REQ6 */ 23762306a36Sopenharmony_ci cr[0] = (CCP_ENGINE_RSA << REQ1_ENGINE_SHIFT) 23862306a36Sopenharmony_ci | (op->u.rsa.mod_size << REQ1_RSA_MOD_SIZE_SHIFT) 23962306a36Sopenharmony_ci | (op->sb_key << REQ1_KEY_KSB_SHIFT) 24062306a36Sopenharmony_ci | REQ1_EOM; 24162306a36Sopenharmony_ci cr[1] = op->u.rsa.input_len - 1; 24262306a36Sopenharmony_ci cr[2] = ccp_addr_lo(&op->src.u.dma); 24362306a36Sopenharmony_ci cr[3] = (op->sb_ctx << REQ4_KSB_SHIFT) 24462306a36Sopenharmony_ci | (CCP_MEMTYPE_SYSTEM << REQ4_MEMTYPE_SHIFT) 24562306a36Sopenharmony_ci | ccp_addr_hi(&op->src.u.dma); 24662306a36Sopenharmony_ci cr[4] = ccp_addr_lo(&op->dst.u.dma); 24762306a36Sopenharmony_ci cr[5] = (CCP_MEMTYPE_SYSTEM << REQ6_MEMTYPE_SHIFT) 24862306a36Sopenharmony_ci | ccp_addr_hi(&op->dst.u.dma); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return ccp_do_cmd(op, cr, ARRAY_SIZE(cr)); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int ccp_perform_passthru(struct ccp_op *op) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci u32 cr[6]; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Fill out the register contents for REQ1 through REQ6 */ 25862306a36Sopenharmony_ci cr[0] = (CCP_ENGINE_PASSTHRU << REQ1_ENGINE_SHIFT) 25962306a36Sopenharmony_ci | (op->u.passthru.bit_mod << REQ1_PT_BW_SHIFT) 26062306a36Sopenharmony_ci | (op->u.passthru.byte_swap << REQ1_PT_BS_SHIFT); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (op->src.type == CCP_MEMTYPE_SYSTEM) 26362306a36Sopenharmony_ci cr[1] = op->src.u.dma.length - 1; 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci cr[1] = op->dst.u.dma.length - 1; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (op->src.type == CCP_MEMTYPE_SYSTEM) { 26862306a36Sopenharmony_ci cr[2] = ccp_addr_lo(&op->src.u.dma); 26962306a36Sopenharmony_ci cr[3] = (CCP_MEMTYPE_SYSTEM << REQ4_MEMTYPE_SHIFT) 27062306a36Sopenharmony_ci | ccp_addr_hi(&op->src.u.dma); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (op->u.passthru.bit_mod != CCP_PASSTHRU_BITWISE_NOOP) 27362306a36Sopenharmony_ci cr[3] |= (op->sb_key << REQ4_KSB_SHIFT); 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci cr[2] = op->src.u.sb * CCP_SB_BYTES; 27662306a36Sopenharmony_ci cr[3] = (CCP_MEMTYPE_SB << REQ4_MEMTYPE_SHIFT); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (op->dst.type == CCP_MEMTYPE_SYSTEM) { 28062306a36Sopenharmony_ci cr[4] = ccp_addr_lo(&op->dst.u.dma); 28162306a36Sopenharmony_ci cr[5] = (CCP_MEMTYPE_SYSTEM << REQ6_MEMTYPE_SHIFT) 28262306a36Sopenharmony_ci | ccp_addr_hi(&op->dst.u.dma); 28362306a36Sopenharmony_ci } else { 28462306a36Sopenharmony_ci cr[4] = op->dst.u.sb * CCP_SB_BYTES; 28562306a36Sopenharmony_ci cr[5] = (CCP_MEMTYPE_SB << REQ6_MEMTYPE_SHIFT); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (op->eom) 28962306a36Sopenharmony_ci cr[0] |= REQ1_EOM; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return ccp_do_cmd(op, cr, ARRAY_SIZE(cr)); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int ccp_perform_ecc(struct ccp_op *op) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci u32 cr[6]; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Fill out the register contents for REQ1 through REQ6 */ 29962306a36Sopenharmony_ci cr[0] = REQ1_ECC_AFFINE_CONVERT 30062306a36Sopenharmony_ci | (CCP_ENGINE_ECC << REQ1_ENGINE_SHIFT) 30162306a36Sopenharmony_ci | (op->u.ecc.function << REQ1_ECC_FUNCTION_SHIFT) 30262306a36Sopenharmony_ci | REQ1_EOM; 30362306a36Sopenharmony_ci cr[1] = op->src.u.dma.length - 1; 30462306a36Sopenharmony_ci cr[2] = ccp_addr_lo(&op->src.u.dma); 30562306a36Sopenharmony_ci cr[3] = (CCP_MEMTYPE_SYSTEM << REQ4_MEMTYPE_SHIFT) 30662306a36Sopenharmony_ci | ccp_addr_hi(&op->src.u.dma); 30762306a36Sopenharmony_ci cr[4] = ccp_addr_lo(&op->dst.u.dma); 30862306a36Sopenharmony_ci cr[5] = (CCP_MEMTYPE_SYSTEM << REQ6_MEMTYPE_SHIFT) 30962306a36Sopenharmony_ci | ccp_addr_hi(&op->dst.u.dma); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return ccp_do_cmd(op, cr, ARRAY_SIZE(cr)); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void ccp_disable_queue_interrupts(struct ccp_device *ccp) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci iowrite32(0x00, ccp->io_regs + IRQ_MASK_REG); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void ccp_enable_queue_interrupts(struct ccp_device *ccp) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci iowrite32(ccp->qim, ccp->io_regs + IRQ_MASK_REG); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void ccp_irq_bh(unsigned long data) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct ccp_device *ccp = (struct ccp_device *)data; 32762306a36Sopenharmony_ci struct ccp_cmd_queue *cmd_q; 32862306a36Sopenharmony_ci u32 q_int, status; 32962306a36Sopenharmony_ci unsigned int i; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci status = ioread32(ccp->io_regs + IRQ_STATUS_REG); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) { 33462306a36Sopenharmony_ci cmd_q = &ccp->cmd_q[i]; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci q_int = status & (cmd_q->int_ok | cmd_q->int_err); 33762306a36Sopenharmony_ci if (q_int) { 33862306a36Sopenharmony_ci cmd_q->int_status = status; 33962306a36Sopenharmony_ci cmd_q->q_status = ioread32(cmd_q->reg_status); 34062306a36Sopenharmony_ci cmd_q->q_int_status = ioread32(cmd_q->reg_int_status); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* On error, only save the first error value */ 34362306a36Sopenharmony_ci if ((q_int & cmd_q->int_err) && !cmd_q->cmd_error) 34462306a36Sopenharmony_ci cmd_q->cmd_error = CMD_Q_ERROR(cmd_q->q_status); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci cmd_q->int_rcvd = 1; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Acknowledge the interrupt and wake the kthread */ 34962306a36Sopenharmony_ci iowrite32(q_int, ccp->io_regs + IRQ_STATUS_REG); 35062306a36Sopenharmony_ci wake_up_interruptible(&cmd_q->int_queue); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci ccp_enable_queue_interrupts(ccp); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic irqreturn_t ccp_irq_handler(int irq, void *data) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct ccp_device *ccp = (struct ccp_device *)data; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci ccp_disable_queue_interrupts(ccp); 36162306a36Sopenharmony_ci if (ccp->use_tasklet) 36262306a36Sopenharmony_ci tasklet_schedule(&ccp->irq_tasklet); 36362306a36Sopenharmony_ci else 36462306a36Sopenharmony_ci ccp_irq_bh((unsigned long)ccp); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return IRQ_HANDLED; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int ccp_init(struct ccp_device *ccp) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct device *dev = ccp->dev; 37262306a36Sopenharmony_ci struct ccp_cmd_queue *cmd_q; 37362306a36Sopenharmony_ci struct dma_pool *dma_pool; 37462306a36Sopenharmony_ci char dma_pool_name[MAX_DMAPOOL_NAME_LEN]; 37562306a36Sopenharmony_ci unsigned int qmr, i; 37662306a36Sopenharmony_ci int ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Find available queues */ 37962306a36Sopenharmony_ci ccp->qim = 0; 38062306a36Sopenharmony_ci qmr = ioread32(ccp->io_regs + Q_MASK_REG); 38162306a36Sopenharmony_ci for (i = 0; (i < MAX_HW_QUEUES) && (ccp->cmd_q_count < ccp->max_q_count); i++) { 38262306a36Sopenharmony_ci if (!(qmr & (1 << i))) 38362306a36Sopenharmony_ci continue; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* Allocate a dma pool for this queue */ 38662306a36Sopenharmony_ci snprintf(dma_pool_name, sizeof(dma_pool_name), "%s_q%d", 38762306a36Sopenharmony_ci ccp->name, i); 38862306a36Sopenharmony_ci dma_pool = dma_pool_create(dma_pool_name, dev, 38962306a36Sopenharmony_ci CCP_DMAPOOL_MAX_SIZE, 39062306a36Sopenharmony_ci CCP_DMAPOOL_ALIGN, 0); 39162306a36Sopenharmony_ci if (!dma_pool) { 39262306a36Sopenharmony_ci dev_err(dev, "unable to allocate dma pool\n"); 39362306a36Sopenharmony_ci ret = -ENOMEM; 39462306a36Sopenharmony_ci goto e_pool; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci cmd_q = &ccp->cmd_q[ccp->cmd_q_count]; 39862306a36Sopenharmony_ci ccp->cmd_q_count++; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci cmd_q->ccp = ccp; 40162306a36Sopenharmony_ci cmd_q->id = i; 40262306a36Sopenharmony_ci cmd_q->dma_pool = dma_pool; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Reserve 2 KSB regions for the queue */ 40562306a36Sopenharmony_ci cmd_q->sb_key = KSB_START + ccp->sb_start++; 40662306a36Sopenharmony_ci cmd_q->sb_ctx = KSB_START + ccp->sb_start++; 40762306a36Sopenharmony_ci ccp->sb_count -= 2; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Preset some register values and masks that are queue 41062306a36Sopenharmony_ci * number dependent 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci cmd_q->reg_status = ccp->io_regs + CMD_Q_STATUS_BASE + 41362306a36Sopenharmony_ci (CMD_Q_STATUS_INCR * i); 41462306a36Sopenharmony_ci cmd_q->reg_int_status = ccp->io_regs + CMD_Q_INT_STATUS_BASE + 41562306a36Sopenharmony_ci (CMD_Q_STATUS_INCR * i); 41662306a36Sopenharmony_ci cmd_q->int_ok = 1 << (i * 2); 41762306a36Sopenharmony_ci cmd_q->int_err = 1 << ((i * 2) + 1); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci cmd_q->free_slots = ccp_get_free_slots(cmd_q); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci init_waitqueue_head(&cmd_q->int_queue); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Build queue interrupt mask (two interrupts per queue) */ 42462306a36Sopenharmony_ci ccp->qim |= cmd_q->int_ok | cmd_q->int_err; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci#ifdef CONFIG_ARM64 42762306a36Sopenharmony_ci /* For arm64 set the recommended queue cache settings */ 42862306a36Sopenharmony_ci iowrite32(ccp->axcache, ccp->io_regs + CMD_Q_CACHE_BASE + 42962306a36Sopenharmony_ci (CMD_Q_CACHE_INC * i)); 43062306a36Sopenharmony_ci#endif 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci dev_dbg(dev, "queue #%u available\n", i); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci if (ccp->cmd_q_count == 0) { 43562306a36Sopenharmony_ci dev_notice(dev, "no command queues available\n"); 43662306a36Sopenharmony_ci ret = -EIO; 43762306a36Sopenharmony_ci goto e_pool; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci dev_notice(dev, "%u command queues available\n", ccp->cmd_q_count); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Disable and clear interrupts until ready */ 44262306a36Sopenharmony_ci ccp_disable_queue_interrupts(ccp); 44362306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) { 44462306a36Sopenharmony_ci cmd_q = &ccp->cmd_q[i]; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci ioread32(cmd_q->reg_int_status); 44762306a36Sopenharmony_ci ioread32(cmd_q->reg_status); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci iowrite32(ccp->qim, ccp->io_regs + IRQ_STATUS_REG); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* Request an irq */ 45262306a36Sopenharmony_ci ret = sp_request_ccp_irq(ccp->sp, ccp_irq_handler, ccp->name, ccp); 45362306a36Sopenharmony_ci if (ret) { 45462306a36Sopenharmony_ci dev_err(dev, "unable to allocate an IRQ\n"); 45562306a36Sopenharmony_ci goto e_pool; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Initialize the ISR tasklet? */ 45962306a36Sopenharmony_ci if (ccp->use_tasklet) 46062306a36Sopenharmony_ci tasklet_init(&ccp->irq_tasklet, ccp_irq_bh, 46162306a36Sopenharmony_ci (unsigned long)ccp); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci dev_dbg(dev, "Starting threads...\n"); 46462306a36Sopenharmony_ci /* Create a kthread for each queue */ 46562306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) { 46662306a36Sopenharmony_ci struct task_struct *kthread; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci cmd_q = &ccp->cmd_q[i]; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci kthread = kthread_run(ccp_cmd_queue_thread, cmd_q, 47162306a36Sopenharmony_ci "%s-q%u", ccp->name, cmd_q->id); 47262306a36Sopenharmony_ci if (IS_ERR(kthread)) { 47362306a36Sopenharmony_ci dev_err(dev, "error creating queue thread (%ld)\n", 47462306a36Sopenharmony_ci PTR_ERR(kthread)); 47562306a36Sopenharmony_ci ret = PTR_ERR(kthread); 47662306a36Sopenharmony_ci goto e_kthread; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci cmd_q->kthread = kthread; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci dev_dbg(dev, "Enabling interrupts...\n"); 48362306a36Sopenharmony_ci /* Enable interrupts */ 48462306a36Sopenharmony_ci ccp_enable_queue_interrupts(ccp); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci dev_dbg(dev, "Registering device...\n"); 48762306a36Sopenharmony_ci ccp_add_device(ccp); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ret = ccp_register_rng(ccp); 49062306a36Sopenharmony_ci if (ret) 49162306a36Sopenharmony_ci goto e_kthread; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Register the DMA engine support */ 49462306a36Sopenharmony_ci ret = ccp_dmaengine_register(ccp); 49562306a36Sopenharmony_ci if (ret) 49662306a36Sopenharmony_ci goto e_hwrng; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cie_hwrng: 50162306a36Sopenharmony_ci ccp_unregister_rng(ccp); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cie_kthread: 50462306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) 50562306a36Sopenharmony_ci if (ccp->cmd_q[i].kthread) 50662306a36Sopenharmony_ci kthread_stop(ccp->cmd_q[i].kthread); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci sp_free_ccp_irq(ccp->sp, ccp); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cie_pool: 51162306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) 51262306a36Sopenharmony_ci dma_pool_destroy(ccp->cmd_q[i].dma_pool); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void ccp_destroy(struct ccp_device *ccp) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct ccp_cmd_queue *cmd_q; 52062306a36Sopenharmony_ci struct ccp_cmd *cmd; 52162306a36Sopenharmony_ci unsigned int i; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Unregister the DMA engine */ 52462306a36Sopenharmony_ci ccp_dmaengine_unregister(ccp); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* Unregister the RNG */ 52762306a36Sopenharmony_ci ccp_unregister_rng(ccp); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Remove this device from the list of available units */ 53062306a36Sopenharmony_ci ccp_del_device(ccp); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Disable and clear interrupts */ 53362306a36Sopenharmony_ci ccp_disable_queue_interrupts(ccp); 53462306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) { 53562306a36Sopenharmony_ci cmd_q = &ccp->cmd_q[i]; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci ioread32(cmd_q->reg_int_status); 53862306a36Sopenharmony_ci ioread32(cmd_q->reg_status); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci iowrite32(ccp->qim, ccp->io_regs + IRQ_STATUS_REG); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Stop the queue kthreads */ 54362306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) 54462306a36Sopenharmony_ci if (ccp->cmd_q[i].kthread) 54562306a36Sopenharmony_ci kthread_stop(ccp->cmd_q[i].kthread); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci sp_free_ccp_irq(ccp->sp, ccp); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci for (i = 0; i < ccp->cmd_q_count; i++) 55062306a36Sopenharmony_ci dma_pool_destroy(ccp->cmd_q[i].dma_pool); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Flush the cmd and backlog queue */ 55362306a36Sopenharmony_ci while (!list_empty(&ccp->cmd)) { 55462306a36Sopenharmony_ci /* Invoke the callback directly with an error code */ 55562306a36Sopenharmony_ci cmd = list_first_entry(&ccp->cmd, struct ccp_cmd, entry); 55662306a36Sopenharmony_ci list_del(&cmd->entry); 55762306a36Sopenharmony_ci cmd->callback(cmd->data, -ENODEV); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci while (!list_empty(&ccp->backlog)) { 56062306a36Sopenharmony_ci /* Invoke the callback directly with an error code */ 56162306a36Sopenharmony_ci cmd = list_first_entry(&ccp->backlog, struct ccp_cmd, entry); 56262306a36Sopenharmony_ci list_del(&cmd->entry); 56362306a36Sopenharmony_ci cmd->callback(cmd->data, -ENODEV); 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic const struct ccp_actions ccp3_actions = { 56862306a36Sopenharmony_ci .aes = ccp_perform_aes, 56962306a36Sopenharmony_ci .xts_aes = ccp_perform_xts_aes, 57062306a36Sopenharmony_ci .des3 = NULL, 57162306a36Sopenharmony_ci .sha = ccp_perform_sha, 57262306a36Sopenharmony_ci .rsa = ccp_perform_rsa, 57362306a36Sopenharmony_ci .passthru = ccp_perform_passthru, 57462306a36Sopenharmony_ci .ecc = ccp_perform_ecc, 57562306a36Sopenharmony_ci .sballoc = ccp_alloc_ksb, 57662306a36Sopenharmony_ci .sbfree = ccp_free_ksb, 57762306a36Sopenharmony_ci .init = ccp_init, 57862306a36Sopenharmony_ci .destroy = ccp_destroy, 57962306a36Sopenharmony_ci .get_free_slots = ccp_get_free_slots, 58062306a36Sopenharmony_ci .irqhandler = ccp_irq_handler, 58162306a36Sopenharmony_ci}; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ciconst struct ccp_vdata ccpv3_platform = { 58462306a36Sopenharmony_ci .version = CCP_VERSION(3, 0), 58562306a36Sopenharmony_ci .setup = NULL, 58662306a36Sopenharmony_ci .perform = &ccp3_actions, 58762306a36Sopenharmony_ci .offset = 0, 58862306a36Sopenharmony_ci .rsamax = CCP_RSA_MAX_WIDTH, 58962306a36Sopenharmony_ci}; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ciconst struct ccp_vdata ccpv3 = { 59262306a36Sopenharmony_ci .version = CCP_VERSION(3, 0), 59362306a36Sopenharmony_ci .setup = NULL, 59462306a36Sopenharmony_ci .perform = &ccp3_actions, 59562306a36Sopenharmony_ci .offset = 0x20000, 59662306a36Sopenharmony_ci .rsamax = CCP_RSA_MAX_WIDTH, 59762306a36Sopenharmony_ci}; 598