162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Intel Keem Bay OCS HCU Crypto Driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018-2020 Intel Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/iopoll.h> 1162306a36Sopenharmony_ci#include <linux/irq.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <crypto/sha2.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "ocs-hcu.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Registers. */ 1962306a36Sopenharmony_ci#define OCS_HCU_MODE 0x00 2062306a36Sopenharmony_ci#define OCS_HCU_CHAIN 0x04 2162306a36Sopenharmony_ci#define OCS_HCU_OPERATION 0x08 2262306a36Sopenharmony_ci#define OCS_HCU_KEY_0 0x0C 2362306a36Sopenharmony_ci#define OCS_HCU_ISR 0x50 2462306a36Sopenharmony_ci#define OCS_HCU_IER 0x54 2562306a36Sopenharmony_ci#define OCS_HCU_STATUS 0x58 2662306a36Sopenharmony_ci#define OCS_HCU_MSG_LEN_LO 0x60 2762306a36Sopenharmony_ci#define OCS_HCU_MSG_LEN_HI 0x64 2862306a36Sopenharmony_ci#define OCS_HCU_KEY_BYTE_ORDER_CFG 0x80 2962306a36Sopenharmony_ci#define OCS_HCU_DMA_SRC_ADDR 0x400 3062306a36Sopenharmony_ci#define OCS_HCU_DMA_SRC_SIZE 0x408 3162306a36Sopenharmony_ci#define OCS_HCU_DMA_DST_SIZE 0x40C 3262306a36Sopenharmony_ci#define OCS_HCU_DMA_DMA_MODE 0x410 3362306a36Sopenharmony_ci#define OCS_HCU_DMA_NEXT_SRC_DESCR 0x418 3462306a36Sopenharmony_ci#define OCS_HCU_DMA_MSI_ISR 0x480 3562306a36Sopenharmony_ci#define OCS_HCU_DMA_MSI_IER 0x484 3662306a36Sopenharmony_ci#define OCS_HCU_DMA_MSI_MASK 0x488 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Register bit definitions. */ 3962306a36Sopenharmony_ci#define HCU_MODE_ALGO_SHIFT 16 4062306a36Sopenharmony_ci#define HCU_MODE_HMAC_SHIFT 22 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define HCU_STATUS_BUSY BIT(0) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define HCU_BYTE_ORDER_SWAP BIT(0) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define HCU_IRQ_HASH_DONE BIT(2) 4762306a36Sopenharmony_ci#define HCU_IRQ_HASH_ERR_MASK (BIT(3) | BIT(1) | BIT(0)) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define HCU_DMA_IRQ_SRC_DONE BIT(0) 5062306a36Sopenharmony_ci#define HCU_DMA_IRQ_SAI_ERR BIT(2) 5162306a36Sopenharmony_ci#define HCU_DMA_IRQ_BAD_COMP_ERR BIT(3) 5262306a36Sopenharmony_ci#define HCU_DMA_IRQ_INBUF_RD_ERR BIT(4) 5362306a36Sopenharmony_ci#define HCU_DMA_IRQ_INBUF_WD_ERR BIT(5) 5462306a36Sopenharmony_ci#define HCU_DMA_IRQ_OUTBUF_WR_ERR BIT(6) 5562306a36Sopenharmony_ci#define HCU_DMA_IRQ_OUTBUF_RD_ERR BIT(7) 5662306a36Sopenharmony_ci#define HCU_DMA_IRQ_CRD_ERR BIT(8) 5762306a36Sopenharmony_ci#define HCU_DMA_IRQ_ERR_MASK (HCU_DMA_IRQ_SAI_ERR | \ 5862306a36Sopenharmony_ci HCU_DMA_IRQ_BAD_COMP_ERR | \ 5962306a36Sopenharmony_ci HCU_DMA_IRQ_INBUF_RD_ERR | \ 6062306a36Sopenharmony_ci HCU_DMA_IRQ_INBUF_WD_ERR | \ 6162306a36Sopenharmony_ci HCU_DMA_IRQ_OUTBUF_WR_ERR | \ 6262306a36Sopenharmony_ci HCU_DMA_IRQ_OUTBUF_RD_ERR | \ 6362306a36Sopenharmony_ci HCU_DMA_IRQ_CRD_ERR) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define HCU_DMA_SNOOP_MASK (0x7 << 28) 6662306a36Sopenharmony_ci#define HCU_DMA_SRC_LL_EN BIT(25) 6762306a36Sopenharmony_ci#define HCU_DMA_EN BIT(31) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define OCS_HCU_ENDIANNESS_VALUE 0x2A 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define HCU_DMA_MSI_UNMASK BIT(0) 7262306a36Sopenharmony_ci#define HCU_DMA_MSI_DISABLE 0 7362306a36Sopenharmony_ci#define HCU_IRQ_DISABLE 0 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define OCS_HCU_START BIT(0) 7662306a36Sopenharmony_ci#define OCS_HCU_TERMINATE BIT(1) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define OCS_LL_DMA_FLAG_TERMINATE BIT(31) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define OCS_HCU_HW_KEY_LEN_U32 (OCS_HCU_HW_KEY_LEN / sizeof(u32)) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define HCU_DATA_WRITE_ENDIANNESS_OFFSET 26 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define OCS_HCU_NUM_CHAINS_SHA256_224_SM3 (SHA256_DIGEST_SIZE / sizeof(u32)) 8562306a36Sopenharmony_ci#define OCS_HCU_NUM_CHAINS_SHA384_512 (SHA512_DIGEST_SIZE / sizeof(u32)) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * While polling on a busy HCU, wait maximum 200us between one check and the 8962306a36Sopenharmony_ci * other. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci#define OCS_HCU_WAIT_BUSY_RETRY_DELAY_US 200 9262306a36Sopenharmony_ci/* Wait on a busy HCU for maximum 1 second. */ 9362306a36Sopenharmony_ci#define OCS_HCU_WAIT_BUSY_TIMEOUT_US 1000000 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/** 9662306a36Sopenharmony_ci * struct ocs_hcu_dma_entry - An entry in an OCS DMA linked list. 9762306a36Sopenharmony_ci * @src_addr: Source address of the data. 9862306a36Sopenharmony_ci * @src_len: Length of data to be fetched. 9962306a36Sopenharmony_ci * @nxt_desc: Next descriptor to fetch. 10062306a36Sopenharmony_ci * @ll_flags: Flags (Freeze @ terminate) for the DMA engine. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistruct ocs_hcu_dma_entry { 10362306a36Sopenharmony_ci u32 src_addr; 10462306a36Sopenharmony_ci u32 src_len; 10562306a36Sopenharmony_ci u32 nxt_desc; 10662306a36Sopenharmony_ci u32 ll_flags; 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/** 11062306a36Sopenharmony_ci * struct ocs_hcu_dma_list - OCS-specific DMA linked list. 11162306a36Sopenharmony_ci * @head: The head of the list (points to the array backing the list). 11262306a36Sopenharmony_ci * @tail: The current tail of the list; NULL if the list is empty. 11362306a36Sopenharmony_ci * @dma_addr: The DMA address of @head (i.e., the DMA address of the backing 11462306a36Sopenharmony_ci * array). 11562306a36Sopenharmony_ci * @max_nents: Maximum number of entries in the list (i.e., number of elements 11662306a36Sopenharmony_ci * in the backing array). 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * The OCS DMA list is an array-backed list of OCS DMA descriptors. The array 11962306a36Sopenharmony_ci * backing the list is allocated with dma_alloc_coherent() and pointed by 12062306a36Sopenharmony_ci * @head. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistruct ocs_hcu_dma_list { 12362306a36Sopenharmony_ci struct ocs_hcu_dma_entry *head; 12462306a36Sopenharmony_ci struct ocs_hcu_dma_entry *tail; 12562306a36Sopenharmony_ci dma_addr_t dma_addr; 12662306a36Sopenharmony_ci size_t max_nents; 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic inline u32 ocs_hcu_num_chains(enum ocs_hcu_algo algo) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci switch (algo) { 13262306a36Sopenharmony_ci case OCS_HCU_ALGO_SHA224: 13362306a36Sopenharmony_ci case OCS_HCU_ALGO_SHA256: 13462306a36Sopenharmony_ci case OCS_HCU_ALGO_SM3: 13562306a36Sopenharmony_ci return OCS_HCU_NUM_CHAINS_SHA256_224_SM3; 13662306a36Sopenharmony_ci case OCS_HCU_ALGO_SHA384: 13762306a36Sopenharmony_ci case OCS_HCU_ALGO_SHA512: 13862306a36Sopenharmony_ci return OCS_HCU_NUM_CHAINS_SHA384_512; 13962306a36Sopenharmony_ci default: 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci }; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic inline u32 ocs_hcu_digest_size(enum ocs_hcu_algo algo) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci switch (algo) { 14762306a36Sopenharmony_ci case OCS_HCU_ALGO_SHA224: 14862306a36Sopenharmony_ci return SHA224_DIGEST_SIZE; 14962306a36Sopenharmony_ci case OCS_HCU_ALGO_SHA256: 15062306a36Sopenharmony_ci case OCS_HCU_ALGO_SM3: 15162306a36Sopenharmony_ci /* SM3 shares the same block size. */ 15262306a36Sopenharmony_ci return SHA256_DIGEST_SIZE; 15362306a36Sopenharmony_ci case OCS_HCU_ALGO_SHA384: 15462306a36Sopenharmony_ci return SHA384_DIGEST_SIZE; 15562306a36Sopenharmony_ci case OCS_HCU_ALGO_SHA512: 15662306a36Sopenharmony_ci return SHA512_DIGEST_SIZE; 15762306a36Sopenharmony_ci default: 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/** 16362306a36Sopenharmony_ci * ocs_hcu_wait_busy() - Wait for HCU OCS hardware to became usable. 16462306a36Sopenharmony_ci * @hcu_dev: OCS HCU device to wait for. 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Return: 0 if device free, -ETIMEOUT if device busy and internal timeout has 16762306a36Sopenharmony_ci * expired. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistatic int ocs_hcu_wait_busy(struct ocs_hcu_dev *hcu_dev) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci long val; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return readl_poll_timeout(hcu_dev->io_base + OCS_HCU_STATUS, val, 17462306a36Sopenharmony_ci !(val & HCU_STATUS_BUSY), 17562306a36Sopenharmony_ci OCS_HCU_WAIT_BUSY_RETRY_DELAY_US, 17662306a36Sopenharmony_ci OCS_HCU_WAIT_BUSY_TIMEOUT_US); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void ocs_hcu_done_irq_en(struct ocs_hcu_dev *hcu_dev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci /* Clear any pending interrupts. */ 18262306a36Sopenharmony_ci writel(0xFFFFFFFF, hcu_dev->io_base + OCS_HCU_ISR); 18362306a36Sopenharmony_ci hcu_dev->irq_err = false; 18462306a36Sopenharmony_ci /* Enable error and HCU done interrupts. */ 18562306a36Sopenharmony_ci writel(HCU_IRQ_HASH_DONE | HCU_IRQ_HASH_ERR_MASK, 18662306a36Sopenharmony_ci hcu_dev->io_base + OCS_HCU_IER); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void ocs_hcu_dma_irq_en(struct ocs_hcu_dev *hcu_dev) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci /* Clear any pending interrupts. */ 19262306a36Sopenharmony_ci writel(0xFFFFFFFF, hcu_dev->io_base + OCS_HCU_DMA_MSI_ISR); 19362306a36Sopenharmony_ci hcu_dev->irq_err = false; 19462306a36Sopenharmony_ci /* Only operating on DMA source completion and error interrupts. */ 19562306a36Sopenharmony_ci writel(HCU_DMA_IRQ_ERR_MASK | HCU_DMA_IRQ_SRC_DONE, 19662306a36Sopenharmony_ci hcu_dev->io_base + OCS_HCU_DMA_MSI_IER); 19762306a36Sopenharmony_ci /* Unmask */ 19862306a36Sopenharmony_ci writel(HCU_DMA_MSI_UNMASK, hcu_dev->io_base + OCS_HCU_DMA_MSI_MASK); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void ocs_hcu_irq_dis(struct ocs_hcu_dev *hcu_dev) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci writel(HCU_IRQ_DISABLE, hcu_dev->io_base + OCS_HCU_IER); 20462306a36Sopenharmony_ci writel(HCU_DMA_MSI_DISABLE, hcu_dev->io_base + OCS_HCU_DMA_MSI_IER); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int ocs_hcu_wait_and_disable_irq(struct ocs_hcu_dev *hcu_dev) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int rc; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci rc = wait_for_completion_interruptible(&hcu_dev->irq_done); 21262306a36Sopenharmony_ci if (rc) 21362306a36Sopenharmony_ci goto exit; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (hcu_dev->irq_err) { 21662306a36Sopenharmony_ci /* Unset flag and return error. */ 21762306a36Sopenharmony_ci hcu_dev->irq_err = false; 21862306a36Sopenharmony_ci rc = -EIO; 21962306a36Sopenharmony_ci goto exit; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciexit: 22362306a36Sopenharmony_ci ocs_hcu_irq_dis(hcu_dev); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return rc; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * ocs_hcu_get_intermediate_data() - Get intermediate data. 23062306a36Sopenharmony_ci * @hcu_dev: The target HCU device. 23162306a36Sopenharmony_ci * @data: Where to store the intermediate. 23262306a36Sopenharmony_ci * @algo: The algorithm being used. 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * This function is used to save the current hashing process state in order to 23562306a36Sopenharmony_ci * continue it in the future. 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * Note: once all data has been processed, the intermediate data actually 23862306a36Sopenharmony_ci * contains the hashing result. So this function is also used to retrieve the 23962306a36Sopenharmony_ci * final result of a hashing process. 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cistatic int ocs_hcu_get_intermediate_data(struct ocs_hcu_dev *hcu_dev, 24462306a36Sopenharmony_ci struct ocs_hcu_idata *data, 24562306a36Sopenharmony_ci enum ocs_hcu_algo algo) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci const int n = ocs_hcu_num_chains(algo); 24862306a36Sopenharmony_ci u32 *chain; 24962306a36Sopenharmony_ci int rc; 25062306a36Sopenharmony_ci int i; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Data not requested. */ 25362306a36Sopenharmony_ci if (!data) 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci chain = (u32 *)data->digest; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Ensure that the OCS is no longer busy before reading the chains. */ 25962306a36Sopenharmony_ci rc = ocs_hcu_wait_busy(hcu_dev); 26062306a36Sopenharmony_ci if (rc) 26162306a36Sopenharmony_ci return rc; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * This loops is safe because data->digest is an array of 26562306a36Sopenharmony_ci * SHA512_DIGEST_SIZE bytes and the maximum value returned by 26662306a36Sopenharmony_ci * ocs_hcu_num_chains() is OCS_HCU_NUM_CHAINS_SHA384_512 which is equal 26762306a36Sopenharmony_ci * to SHA512_DIGEST_SIZE / sizeof(u32). 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci for (i = 0; i < n; i++) 27062306a36Sopenharmony_ci chain[i] = readl(hcu_dev->io_base + OCS_HCU_CHAIN); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci data->msg_len_lo = readl(hcu_dev->io_base + OCS_HCU_MSG_LEN_LO); 27362306a36Sopenharmony_ci data->msg_len_hi = readl(hcu_dev->io_base + OCS_HCU_MSG_LEN_HI); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/** 27962306a36Sopenharmony_ci * ocs_hcu_set_intermediate_data() - Set intermediate data. 28062306a36Sopenharmony_ci * @hcu_dev: The target HCU device. 28162306a36Sopenharmony_ci * @data: The intermediate data to be set. 28262306a36Sopenharmony_ci * @algo: The algorithm being used. 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * This function is used to continue a previous hashing process. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_cistatic void ocs_hcu_set_intermediate_data(struct ocs_hcu_dev *hcu_dev, 28762306a36Sopenharmony_ci const struct ocs_hcu_idata *data, 28862306a36Sopenharmony_ci enum ocs_hcu_algo algo) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci const int n = ocs_hcu_num_chains(algo); 29162306a36Sopenharmony_ci u32 *chain = (u32 *)data->digest; 29262306a36Sopenharmony_ci int i; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * This loops is safe because data->digest is an array of 29662306a36Sopenharmony_ci * SHA512_DIGEST_SIZE bytes and the maximum value returned by 29762306a36Sopenharmony_ci * ocs_hcu_num_chains() is OCS_HCU_NUM_CHAINS_SHA384_512 which is equal 29862306a36Sopenharmony_ci * to SHA512_DIGEST_SIZE / sizeof(u32). 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci for (i = 0; i < n; i++) 30162306a36Sopenharmony_ci writel(chain[i], hcu_dev->io_base + OCS_HCU_CHAIN); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci writel(data->msg_len_lo, hcu_dev->io_base + OCS_HCU_MSG_LEN_LO); 30462306a36Sopenharmony_ci writel(data->msg_len_hi, hcu_dev->io_base + OCS_HCU_MSG_LEN_HI); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int ocs_hcu_get_digest(struct ocs_hcu_dev *hcu_dev, 30862306a36Sopenharmony_ci enum ocs_hcu_algo algo, u8 *dgst, size_t dgst_len) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci u32 *chain; 31162306a36Sopenharmony_ci int rc; 31262306a36Sopenharmony_ci int i; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!dgst) 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Length of the output buffer must match the algo digest size. */ 31862306a36Sopenharmony_ci if (dgst_len != ocs_hcu_digest_size(algo)) 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Ensure that the OCS is no longer busy before reading the chains. */ 32262306a36Sopenharmony_ci rc = ocs_hcu_wait_busy(hcu_dev); 32362306a36Sopenharmony_ci if (rc) 32462306a36Sopenharmony_ci return rc; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci chain = (u32 *)dgst; 32762306a36Sopenharmony_ci for (i = 0; i < dgst_len / sizeof(u32); i++) 32862306a36Sopenharmony_ci chain[i] = readl(hcu_dev->io_base + OCS_HCU_CHAIN); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/** 33462306a36Sopenharmony_ci * ocs_hcu_hw_cfg() - Configure the HCU hardware. 33562306a36Sopenharmony_ci * @hcu_dev: The HCU device to configure. 33662306a36Sopenharmony_ci * @algo: The algorithm to be used by the HCU device. 33762306a36Sopenharmony_ci * @use_hmac: Whether or not HW HMAC should be used. 33862306a36Sopenharmony_ci * 33962306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_cistatic int ocs_hcu_hw_cfg(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo, 34262306a36Sopenharmony_ci bool use_hmac) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci u32 cfg; 34562306a36Sopenharmony_ci int rc; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (algo != OCS_HCU_ALGO_SHA256 && algo != OCS_HCU_ALGO_SHA224 && 34862306a36Sopenharmony_ci algo != OCS_HCU_ALGO_SHA384 && algo != OCS_HCU_ALGO_SHA512 && 34962306a36Sopenharmony_ci algo != OCS_HCU_ALGO_SM3) 35062306a36Sopenharmony_ci return -EINVAL; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci rc = ocs_hcu_wait_busy(hcu_dev); 35362306a36Sopenharmony_ci if (rc) 35462306a36Sopenharmony_ci return rc; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Ensure interrupts are disabled. */ 35762306a36Sopenharmony_ci ocs_hcu_irq_dis(hcu_dev); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* Configure endianness, hashing algorithm and HW HMAC (if needed) */ 36062306a36Sopenharmony_ci cfg = OCS_HCU_ENDIANNESS_VALUE << HCU_DATA_WRITE_ENDIANNESS_OFFSET; 36162306a36Sopenharmony_ci cfg |= algo << HCU_MODE_ALGO_SHIFT; 36262306a36Sopenharmony_ci if (use_hmac) 36362306a36Sopenharmony_ci cfg |= BIT(HCU_MODE_HMAC_SHIFT); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci writel(cfg, hcu_dev->io_base + OCS_HCU_MODE); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/** 37162306a36Sopenharmony_ci * ocs_hcu_clear_key() - Clear key stored in OCS HMAC KEY registers. 37262306a36Sopenharmony_ci * @hcu_dev: The OCS HCU device whose key registers should be cleared. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic void ocs_hcu_clear_key(struct ocs_hcu_dev *hcu_dev) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci int reg_off; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Clear OCS_HCU_KEY_[0..15] */ 37962306a36Sopenharmony_ci for (reg_off = 0; reg_off < OCS_HCU_HW_KEY_LEN; reg_off += sizeof(u32)) 38062306a36Sopenharmony_ci writel(0, hcu_dev->io_base + OCS_HCU_KEY_0 + reg_off); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/** 38462306a36Sopenharmony_ci * ocs_hcu_write_key() - Write key to OCS HMAC KEY registers. 38562306a36Sopenharmony_ci * @hcu_dev: The OCS HCU device the key should be written to. 38662306a36Sopenharmony_ci * @key: The key to be written. 38762306a36Sopenharmony_ci * @len: The size of the key to write. It must be OCS_HCU_HW_KEY_LEN. 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_cistatic int ocs_hcu_write_key(struct ocs_hcu_dev *hcu_dev, const u8 *key, size_t len) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci u32 key_u32[OCS_HCU_HW_KEY_LEN_U32]; 39462306a36Sopenharmony_ci int i; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (len > OCS_HCU_HW_KEY_LEN) 39762306a36Sopenharmony_ci return -EINVAL; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Copy key into temporary u32 array. */ 40062306a36Sopenharmony_ci memcpy(key_u32, key, len); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * Hardware requires all the bytes of the HW Key vector to be 40462306a36Sopenharmony_ci * written. So pad with zero until we reach OCS_HCU_HW_KEY_LEN. 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci memzero_explicit((u8 *)key_u32 + len, OCS_HCU_HW_KEY_LEN - len); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* 40962306a36Sopenharmony_ci * OCS hardware expects the MSB of the key to be written at the highest 41062306a36Sopenharmony_ci * address of the HCU Key vector; in other word, the key must be 41162306a36Sopenharmony_ci * written in reverse order. 41262306a36Sopenharmony_ci * 41362306a36Sopenharmony_ci * Therefore, we first enable byte swapping for the HCU key vector; 41462306a36Sopenharmony_ci * so that bytes of 32-bit word written to OCS_HCU_KEY_[0..15] will be 41562306a36Sopenharmony_ci * swapped: 41662306a36Sopenharmony_ci * 3 <---> 0, 2 <---> 1. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci writel(HCU_BYTE_ORDER_SWAP, 41962306a36Sopenharmony_ci hcu_dev->io_base + OCS_HCU_KEY_BYTE_ORDER_CFG); 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * And then we write the 32-bit words composing the key starting from 42262306a36Sopenharmony_ci * the end of the key. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci for (i = 0; i < OCS_HCU_HW_KEY_LEN_U32; i++) 42562306a36Sopenharmony_ci writel(key_u32[OCS_HCU_HW_KEY_LEN_U32 - 1 - i], 42662306a36Sopenharmony_ci hcu_dev->io_base + OCS_HCU_KEY_0 + (sizeof(u32) * i)); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci memzero_explicit(key_u32, OCS_HCU_HW_KEY_LEN); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/** 43462306a36Sopenharmony_ci * ocs_hcu_ll_dma_start() - Start OCS HCU hashing via DMA 43562306a36Sopenharmony_ci * @hcu_dev: The OCS HCU device to use. 43662306a36Sopenharmony_ci * @dma_list: The OCS DMA list mapping the data to hash. 43762306a36Sopenharmony_ci * @finalize: Whether or not this is the last hashing operation and therefore 43862306a36Sopenharmony_ci * the final hash should be compute even if data is not 43962306a36Sopenharmony_ci * block-aligned. 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cistatic int ocs_hcu_ll_dma_start(struct ocs_hcu_dev *hcu_dev, 44462306a36Sopenharmony_ci const struct ocs_hcu_dma_list *dma_list, 44562306a36Sopenharmony_ci bool finalize) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci u32 cfg = HCU_DMA_SNOOP_MASK | HCU_DMA_SRC_LL_EN | HCU_DMA_EN; 44862306a36Sopenharmony_ci int rc; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (!dma_list) 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * For final requests we use HCU_DONE IRQ to be notified when all input 45562306a36Sopenharmony_ci * data has been processed by the HCU; however, we cannot do so for 45662306a36Sopenharmony_ci * non-final requests, because we don't get a HCU_DONE IRQ when we 45762306a36Sopenharmony_ci * don't terminate the operation. 45862306a36Sopenharmony_ci * 45962306a36Sopenharmony_ci * Therefore, for non-final requests, we use the DMA IRQ, which 46062306a36Sopenharmony_ci * triggers when DMA has finishing feeding all the input data to the 46162306a36Sopenharmony_ci * HCU, but the HCU may still be processing it. This is fine, since we 46262306a36Sopenharmony_ci * will wait for the HCU processing to be completed when we try to read 46362306a36Sopenharmony_ci * intermediate results, in ocs_hcu_get_intermediate_data(). 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci if (finalize) 46662306a36Sopenharmony_ci ocs_hcu_done_irq_en(hcu_dev); 46762306a36Sopenharmony_ci else 46862306a36Sopenharmony_ci ocs_hcu_dma_irq_en(hcu_dev); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci reinit_completion(&hcu_dev->irq_done); 47162306a36Sopenharmony_ci writel(dma_list->dma_addr, hcu_dev->io_base + OCS_HCU_DMA_NEXT_SRC_DESCR); 47262306a36Sopenharmony_ci writel(0, hcu_dev->io_base + OCS_HCU_DMA_SRC_SIZE); 47362306a36Sopenharmony_ci writel(0, hcu_dev->io_base + OCS_HCU_DMA_DST_SIZE); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci writel(OCS_HCU_START, hcu_dev->io_base + OCS_HCU_OPERATION); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci writel(cfg, hcu_dev->io_base + OCS_HCU_DMA_DMA_MODE); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (finalize) 48062306a36Sopenharmony_ci writel(OCS_HCU_TERMINATE, hcu_dev->io_base + OCS_HCU_OPERATION); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci rc = ocs_hcu_wait_and_disable_irq(hcu_dev); 48362306a36Sopenharmony_ci if (rc) 48462306a36Sopenharmony_ci return rc; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistruct ocs_hcu_dma_list *ocs_hcu_dma_list_alloc(struct ocs_hcu_dev *hcu_dev, 49062306a36Sopenharmony_ci int max_nents) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct ocs_hcu_dma_list *dma_list; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci dma_list = kmalloc(sizeof(*dma_list), GFP_KERNEL); 49562306a36Sopenharmony_ci if (!dma_list) 49662306a36Sopenharmony_ci return NULL; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* Total size of the DMA list to allocate. */ 49962306a36Sopenharmony_ci dma_list->head = dma_alloc_coherent(hcu_dev->dev, 50062306a36Sopenharmony_ci sizeof(*dma_list->head) * max_nents, 50162306a36Sopenharmony_ci &dma_list->dma_addr, GFP_KERNEL); 50262306a36Sopenharmony_ci if (!dma_list->head) { 50362306a36Sopenharmony_ci kfree(dma_list); 50462306a36Sopenharmony_ci return NULL; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci dma_list->max_nents = max_nents; 50762306a36Sopenharmony_ci dma_list->tail = NULL; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return dma_list; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_civoid ocs_hcu_dma_list_free(struct ocs_hcu_dev *hcu_dev, 51362306a36Sopenharmony_ci struct ocs_hcu_dma_list *dma_list) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci if (!dma_list) 51662306a36Sopenharmony_ci return; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci dma_free_coherent(hcu_dev->dev, 51962306a36Sopenharmony_ci sizeof(*dma_list->head) * dma_list->max_nents, 52062306a36Sopenharmony_ci dma_list->head, dma_list->dma_addr); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci kfree(dma_list); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci/* Add a new DMA entry at the end of the OCS DMA list. */ 52662306a36Sopenharmony_ciint ocs_hcu_dma_list_add_tail(struct ocs_hcu_dev *hcu_dev, 52762306a36Sopenharmony_ci struct ocs_hcu_dma_list *dma_list, 52862306a36Sopenharmony_ci dma_addr_t addr, u32 len) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct device *dev = hcu_dev->dev; 53162306a36Sopenharmony_ci struct ocs_hcu_dma_entry *old_tail; 53262306a36Sopenharmony_ci struct ocs_hcu_dma_entry *new_tail; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (!len) 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!dma_list) 53862306a36Sopenharmony_ci return -EINVAL; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (addr & ~OCS_HCU_DMA_BIT_MASK) { 54162306a36Sopenharmony_ci dev_err(dev, 54262306a36Sopenharmony_ci "Unexpected error: Invalid DMA address for OCS HCU\n"); 54362306a36Sopenharmony_ci return -EINVAL; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci old_tail = dma_list->tail; 54762306a36Sopenharmony_ci new_tail = old_tail ? old_tail + 1 : dma_list->head; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* Check if list is full. */ 55062306a36Sopenharmony_ci if (new_tail - dma_list->head >= dma_list->max_nents) 55162306a36Sopenharmony_ci return -ENOMEM; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * If there was an old tail (i.e., this is not the first element we are 55562306a36Sopenharmony_ci * adding), un-terminate the old tail and make it point to the new one. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci if (old_tail) { 55862306a36Sopenharmony_ci old_tail->ll_flags &= ~OCS_LL_DMA_FLAG_TERMINATE; 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * The old tail 'nxt_desc' must point to the DMA address of the 56162306a36Sopenharmony_ci * new tail. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci old_tail->nxt_desc = dma_list->dma_addr + 56462306a36Sopenharmony_ci sizeof(*dma_list->tail) * (new_tail - 56562306a36Sopenharmony_ci dma_list->head); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci new_tail->src_addr = (u32)addr; 56962306a36Sopenharmony_ci new_tail->src_len = (u32)len; 57062306a36Sopenharmony_ci new_tail->ll_flags = OCS_LL_DMA_FLAG_TERMINATE; 57162306a36Sopenharmony_ci new_tail->nxt_desc = 0; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Update list tail with new tail. */ 57462306a36Sopenharmony_ci dma_list->tail = new_tail; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/** 58062306a36Sopenharmony_ci * ocs_hcu_hash_init() - Initialize hash operation context. 58162306a36Sopenharmony_ci * @ctx: The context to initialize. 58262306a36Sopenharmony_ci * @algo: The hashing algorithm to use. 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ciint ocs_hcu_hash_init(struct ocs_hcu_hash_ctx *ctx, enum ocs_hcu_algo algo) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci if (!ctx) 58962306a36Sopenharmony_ci return -EINVAL; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ctx->algo = algo; 59262306a36Sopenharmony_ci ctx->idata.msg_len_lo = 0; 59362306a36Sopenharmony_ci ctx->idata.msg_len_hi = 0; 59462306a36Sopenharmony_ci /* No need to set idata.digest to 0. */ 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/** 60062306a36Sopenharmony_ci * ocs_hcu_hash_update() - Perform a hashing iteration. 60162306a36Sopenharmony_ci * @hcu_dev: The OCS HCU device to use. 60262306a36Sopenharmony_ci * @ctx: The OCS HCU hashing context. 60362306a36Sopenharmony_ci * @dma_list: The OCS DMA list mapping the input data to process. 60462306a36Sopenharmony_ci * 60562306a36Sopenharmony_ci * Return: 0 on success; negative error code otherwise. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ciint ocs_hcu_hash_update(struct ocs_hcu_dev *hcu_dev, 60862306a36Sopenharmony_ci struct ocs_hcu_hash_ctx *ctx, 60962306a36Sopenharmony_ci const struct ocs_hcu_dma_list *dma_list) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci int rc; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!hcu_dev || !ctx) 61462306a36Sopenharmony_ci return -EINVAL; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Configure the hardware for the current request. */ 61762306a36Sopenharmony_ci rc = ocs_hcu_hw_cfg(hcu_dev, ctx->algo, false); 61862306a36Sopenharmony_ci if (rc) 61962306a36Sopenharmony_ci return rc; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* If we already processed some data, idata needs to be set. */ 62262306a36Sopenharmony_ci if (ctx->idata.msg_len_lo || ctx->idata.msg_len_hi) 62362306a36Sopenharmony_ci ocs_hcu_set_intermediate_data(hcu_dev, &ctx->idata, ctx->algo); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* Start linked-list DMA hashing. */ 62662306a36Sopenharmony_ci rc = ocs_hcu_ll_dma_start(hcu_dev, dma_list, false); 62762306a36Sopenharmony_ci if (rc) 62862306a36Sopenharmony_ci return rc; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* Update idata and return. */ 63162306a36Sopenharmony_ci return ocs_hcu_get_intermediate_data(hcu_dev, &ctx->idata, ctx->algo); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/** 63562306a36Sopenharmony_ci * ocs_hcu_hash_finup() - Update and finalize hash computation. 63662306a36Sopenharmony_ci * @hcu_dev: The OCS HCU device to use. 63762306a36Sopenharmony_ci * @ctx: The OCS HCU hashing context. 63862306a36Sopenharmony_ci * @dma_list: The OCS DMA list mapping the input data to process. 63962306a36Sopenharmony_ci * @dgst: The buffer where to save the computed digest. 64062306a36Sopenharmony_ci * @dgst_len: The length of @dgst. 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * Return: 0 on success; negative error code otherwise. 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ciint ocs_hcu_hash_finup(struct ocs_hcu_dev *hcu_dev, 64562306a36Sopenharmony_ci const struct ocs_hcu_hash_ctx *ctx, 64662306a36Sopenharmony_ci const struct ocs_hcu_dma_list *dma_list, 64762306a36Sopenharmony_ci u8 *dgst, size_t dgst_len) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci int rc; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (!hcu_dev || !ctx) 65262306a36Sopenharmony_ci return -EINVAL; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* Configure the hardware for the current request. */ 65562306a36Sopenharmony_ci rc = ocs_hcu_hw_cfg(hcu_dev, ctx->algo, false); 65662306a36Sopenharmony_ci if (rc) 65762306a36Sopenharmony_ci return rc; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* If we already processed some data, idata needs to be set. */ 66062306a36Sopenharmony_ci if (ctx->idata.msg_len_lo || ctx->idata.msg_len_hi) 66162306a36Sopenharmony_ci ocs_hcu_set_intermediate_data(hcu_dev, &ctx->idata, ctx->algo); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Start linked-list DMA hashing. */ 66462306a36Sopenharmony_ci rc = ocs_hcu_ll_dma_start(hcu_dev, dma_list, true); 66562306a36Sopenharmony_ci if (rc) 66662306a36Sopenharmony_ci return rc; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Get digest and return. */ 66962306a36Sopenharmony_ci return ocs_hcu_get_digest(hcu_dev, ctx->algo, dgst, dgst_len); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci/** 67362306a36Sopenharmony_ci * ocs_hcu_hash_final() - Finalize hash computation. 67462306a36Sopenharmony_ci * @hcu_dev: The OCS HCU device to use. 67562306a36Sopenharmony_ci * @ctx: The OCS HCU hashing context. 67662306a36Sopenharmony_ci * @dgst: The buffer where to save the computed digest. 67762306a36Sopenharmony_ci * @dgst_len: The length of @dgst. 67862306a36Sopenharmony_ci * 67962306a36Sopenharmony_ci * Return: 0 on success; negative error code otherwise. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_ciint ocs_hcu_hash_final(struct ocs_hcu_dev *hcu_dev, 68262306a36Sopenharmony_ci const struct ocs_hcu_hash_ctx *ctx, u8 *dgst, 68362306a36Sopenharmony_ci size_t dgst_len) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci int rc; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (!hcu_dev || !ctx) 68862306a36Sopenharmony_ci return -EINVAL; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Configure the hardware for the current request. */ 69162306a36Sopenharmony_ci rc = ocs_hcu_hw_cfg(hcu_dev, ctx->algo, false); 69262306a36Sopenharmony_ci if (rc) 69362306a36Sopenharmony_ci return rc; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* If we already processed some data, idata needs to be set. */ 69662306a36Sopenharmony_ci if (ctx->idata.msg_len_lo || ctx->idata.msg_len_hi) 69762306a36Sopenharmony_ci ocs_hcu_set_intermediate_data(hcu_dev, &ctx->idata, ctx->algo); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * Enable HCU interrupts, so that HCU_DONE will be triggered once the 70162306a36Sopenharmony_ci * final hash is computed. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci ocs_hcu_done_irq_en(hcu_dev); 70462306a36Sopenharmony_ci reinit_completion(&hcu_dev->irq_done); 70562306a36Sopenharmony_ci writel(OCS_HCU_TERMINATE, hcu_dev->io_base + OCS_HCU_OPERATION); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci rc = ocs_hcu_wait_and_disable_irq(hcu_dev); 70862306a36Sopenharmony_ci if (rc) 70962306a36Sopenharmony_ci return rc; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Get digest and return. */ 71262306a36Sopenharmony_ci return ocs_hcu_get_digest(hcu_dev, ctx->algo, dgst, dgst_len); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/** 71662306a36Sopenharmony_ci * ocs_hcu_digest() - Compute hash digest. 71762306a36Sopenharmony_ci * @hcu_dev: The OCS HCU device to use. 71862306a36Sopenharmony_ci * @algo: The hash algorithm to use. 71962306a36Sopenharmony_ci * @data: The input data to process. 72062306a36Sopenharmony_ci * @data_len: The length of @data. 72162306a36Sopenharmony_ci * @dgst: The buffer where to save the computed digest. 72262306a36Sopenharmony_ci * @dgst_len: The length of @dgst. 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * Return: 0 on success; negative error code otherwise. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ciint ocs_hcu_digest(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo, 72762306a36Sopenharmony_ci void *data, size_t data_len, u8 *dgst, size_t dgst_len) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct device *dev = hcu_dev->dev; 73062306a36Sopenharmony_ci dma_addr_t dma_handle; 73162306a36Sopenharmony_ci u32 reg; 73262306a36Sopenharmony_ci int rc; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Configure the hardware for the current request. */ 73562306a36Sopenharmony_ci rc = ocs_hcu_hw_cfg(hcu_dev, algo, false); 73662306a36Sopenharmony_ci if (rc) 73762306a36Sopenharmony_ci return rc; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci dma_handle = dma_map_single(dev, data, data_len, DMA_TO_DEVICE); 74062306a36Sopenharmony_ci if (dma_mapping_error(dev, dma_handle)) 74162306a36Sopenharmony_ci return -EIO; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci reg = HCU_DMA_SNOOP_MASK | HCU_DMA_EN; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci ocs_hcu_done_irq_en(hcu_dev); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci reinit_completion(&hcu_dev->irq_done); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci writel(dma_handle, hcu_dev->io_base + OCS_HCU_DMA_SRC_ADDR); 75062306a36Sopenharmony_ci writel(data_len, hcu_dev->io_base + OCS_HCU_DMA_SRC_SIZE); 75162306a36Sopenharmony_ci writel(OCS_HCU_START, hcu_dev->io_base + OCS_HCU_OPERATION); 75262306a36Sopenharmony_ci writel(reg, hcu_dev->io_base + OCS_HCU_DMA_DMA_MODE); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci writel(OCS_HCU_TERMINATE, hcu_dev->io_base + OCS_HCU_OPERATION); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci rc = ocs_hcu_wait_and_disable_irq(hcu_dev); 75762306a36Sopenharmony_ci if (rc) 75862306a36Sopenharmony_ci return rc; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci dma_unmap_single(dev, dma_handle, data_len, DMA_TO_DEVICE); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return ocs_hcu_get_digest(hcu_dev, algo, dgst, dgst_len); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci/** 76662306a36Sopenharmony_ci * ocs_hcu_hmac() - Compute HMAC. 76762306a36Sopenharmony_ci * @hcu_dev: The OCS HCU device to use. 76862306a36Sopenharmony_ci * @algo: The hash algorithm to use with HMAC. 76962306a36Sopenharmony_ci * @key: The key to use. 77062306a36Sopenharmony_ci * @dma_list: The OCS DMA list mapping the input data to process. 77162306a36Sopenharmony_ci * @key_len: The length of @key. 77262306a36Sopenharmony_ci * @dgst: The buffer where to save the computed HMAC. 77362306a36Sopenharmony_ci * @dgst_len: The length of @dgst. 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * Return: 0 on success; negative error code otherwise. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ciint ocs_hcu_hmac(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo, 77862306a36Sopenharmony_ci const u8 *key, size_t key_len, 77962306a36Sopenharmony_ci const struct ocs_hcu_dma_list *dma_list, 78062306a36Sopenharmony_ci u8 *dgst, size_t dgst_len) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci int rc; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Ensure 'key' is not NULL. */ 78562306a36Sopenharmony_ci if (!key || key_len == 0) 78662306a36Sopenharmony_ci return -EINVAL; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* Configure the hardware for the current request. */ 78962306a36Sopenharmony_ci rc = ocs_hcu_hw_cfg(hcu_dev, algo, true); 79062306a36Sopenharmony_ci if (rc) 79162306a36Sopenharmony_ci return rc; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci rc = ocs_hcu_write_key(hcu_dev, key, key_len); 79462306a36Sopenharmony_ci if (rc) 79562306a36Sopenharmony_ci return rc; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci rc = ocs_hcu_ll_dma_start(hcu_dev, dma_list, true); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* Clear HW key before processing return code. */ 80062306a36Sopenharmony_ci ocs_hcu_clear_key(hcu_dev); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (rc) 80362306a36Sopenharmony_ci return rc; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return ocs_hcu_get_digest(hcu_dev, algo, dgst, dgst_len); 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ciirqreturn_t ocs_hcu_irq_handler(int irq, void *dev_id) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct ocs_hcu_dev *hcu_dev = dev_id; 81162306a36Sopenharmony_ci u32 hcu_irq; 81262306a36Sopenharmony_ci u32 dma_irq; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* Read and clear the HCU interrupt. */ 81562306a36Sopenharmony_ci hcu_irq = readl(hcu_dev->io_base + OCS_HCU_ISR); 81662306a36Sopenharmony_ci writel(hcu_irq, hcu_dev->io_base + OCS_HCU_ISR); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Read and clear the HCU DMA interrupt. */ 81962306a36Sopenharmony_ci dma_irq = readl(hcu_dev->io_base + OCS_HCU_DMA_MSI_ISR); 82062306a36Sopenharmony_ci writel(dma_irq, hcu_dev->io_base + OCS_HCU_DMA_MSI_ISR); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* Check for errors. */ 82362306a36Sopenharmony_ci if (hcu_irq & HCU_IRQ_HASH_ERR_MASK || dma_irq & HCU_DMA_IRQ_ERR_MASK) { 82462306a36Sopenharmony_ci hcu_dev->irq_err = true; 82562306a36Sopenharmony_ci goto complete; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* Check for DONE IRQs. */ 82962306a36Sopenharmony_ci if (hcu_irq & HCU_IRQ_HASH_DONE || dma_irq & HCU_DMA_IRQ_SRC_DONE) 83062306a36Sopenharmony_ci goto complete; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return IRQ_NONE; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cicomplete: 83562306a36Sopenharmony_ci complete(&hcu_dev->irq_done); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci return IRQ_HANDLED; 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 841