162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Cryptographic API. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Support for ATMEL SHA1/SHA256 HW acceleration. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2012 Eukréa Electromatique - ATMEL 862306a36Sopenharmony_ci * Author: Nicolas Royer <nicolas@eukrea.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Some ideas are from omap-sham.c drivers. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/clk.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/hw_random.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/device.h> 2462306a36Sopenharmony_ci#include <linux/dmaengine.h> 2562306a36Sopenharmony_ci#include <linux/init.h> 2662306a36Sopenharmony_ci#include <linux/errno.h> 2762306a36Sopenharmony_ci#include <linux/interrupt.h> 2862306a36Sopenharmony_ci#include <linux/irq.h> 2962306a36Sopenharmony_ci#include <linux/scatterlist.h> 3062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3162306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 3262306a36Sopenharmony_ci#include <linux/delay.h> 3362306a36Sopenharmony_ci#include <linux/crypto.h> 3462306a36Sopenharmony_ci#include <crypto/scatterwalk.h> 3562306a36Sopenharmony_ci#include <crypto/algapi.h> 3662306a36Sopenharmony_ci#include <crypto/sha1.h> 3762306a36Sopenharmony_ci#include <crypto/sha2.h> 3862306a36Sopenharmony_ci#include <crypto/hash.h> 3962306a36Sopenharmony_ci#include <crypto/internal/hash.h> 4062306a36Sopenharmony_ci#include "atmel-sha-regs.h" 4162306a36Sopenharmony_ci#include "atmel-authenc.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define ATMEL_SHA_PRIORITY 300 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* SHA flags */ 4662306a36Sopenharmony_ci#define SHA_FLAGS_BUSY BIT(0) 4762306a36Sopenharmony_ci#define SHA_FLAGS_FINAL BIT(1) 4862306a36Sopenharmony_ci#define SHA_FLAGS_DMA_ACTIVE BIT(2) 4962306a36Sopenharmony_ci#define SHA_FLAGS_OUTPUT_READY BIT(3) 5062306a36Sopenharmony_ci#define SHA_FLAGS_INIT BIT(4) 5162306a36Sopenharmony_ci#define SHA_FLAGS_CPU BIT(5) 5262306a36Sopenharmony_ci#define SHA_FLAGS_DMA_READY BIT(6) 5362306a36Sopenharmony_ci#define SHA_FLAGS_DUMP_REG BIT(7) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* bits[11:8] are reserved. */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define SHA_FLAGS_FINUP BIT(16) 5862306a36Sopenharmony_ci#define SHA_FLAGS_SG BIT(17) 5962306a36Sopenharmony_ci#define SHA_FLAGS_ERROR BIT(23) 6062306a36Sopenharmony_ci#define SHA_FLAGS_PAD BIT(24) 6162306a36Sopenharmony_ci#define SHA_FLAGS_RESTORE BIT(25) 6262306a36Sopenharmony_ci#define SHA_FLAGS_IDATAR0 BIT(26) 6362306a36Sopenharmony_ci#define SHA_FLAGS_WAIT_DATARDY BIT(27) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define SHA_OP_INIT 0 6662306a36Sopenharmony_ci#define SHA_OP_UPDATE 1 6762306a36Sopenharmony_ci#define SHA_OP_FINAL 2 6862306a36Sopenharmony_ci#define SHA_OP_DIGEST 3 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define SHA_BUFFER_LEN (PAGE_SIZE / 16) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define ATMEL_SHA_DMA_THRESHOLD 56 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct atmel_sha_caps { 7562306a36Sopenharmony_ci bool has_dma; 7662306a36Sopenharmony_ci bool has_dualbuff; 7762306a36Sopenharmony_ci bool has_sha224; 7862306a36Sopenharmony_ci bool has_sha_384_512; 7962306a36Sopenharmony_ci bool has_uihv; 8062306a36Sopenharmony_ci bool has_hmac; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct atmel_sha_dev; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * .statesize = sizeof(struct atmel_sha_reqctx) must be <= PAGE_SIZE / 8 as 8762306a36Sopenharmony_ci * tested by the ahash_prepare_alg() function. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistruct atmel_sha_reqctx { 9062306a36Sopenharmony_ci struct atmel_sha_dev *dd; 9162306a36Sopenharmony_ci unsigned long flags; 9262306a36Sopenharmony_ci unsigned long op; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci u8 digest[SHA512_DIGEST_SIZE] __aligned(sizeof(u32)); 9562306a36Sopenharmony_ci u64 digcnt[2]; 9662306a36Sopenharmony_ci size_t bufcnt; 9762306a36Sopenharmony_ci size_t buflen; 9862306a36Sopenharmony_ci dma_addr_t dma_addr; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* walk state */ 10162306a36Sopenharmony_ci struct scatterlist *sg; 10262306a36Sopenharmony_ci unsigned int offset; /* offset in current sg */ 10362306a36Sopenharmony_ci unsigned int total; /* total request */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci size_t block_size; 10662306a36Sopenharmony_ci size_t hash_size; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci u8 buffer[SHA_BUFFER_LEN + SHA512_BLOCK_SIZE] __aligned(sizeof(u32)); 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_citypedef int (*atmel_sha_fn_t)(struct atmel_sha_dev *); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct atmel_sha_ctx { 11462306a36Sopenharmony_ci struct atmel_sha_dev *dd; 11562306a36Sopenharmony_ci atmel_sha_fn_t start; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci unsigned long flags; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define ATMEL_SHA_QUEUE_LENGTH 50 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct atmel_sha_dma { 12362306a36Sopenharmony_ci struct dma_chan *chan; 12462306a36Sopenharmony_ci struct dma_slave_config dma_conf; 12562306a36Sopenharmony_ci struct scatterlist *sg; 12662306a36Sopenharmony_ci int nents; 12762306a36Sopenharmony_ci unsigned int last_sg_length; 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct atmel_sha_dev { 13162306a36Sopenharmony_ci struct list_head list; 13262306a36Sopenharmony_ci unsigned long phys_base; 13362306a36Sopenharmony_ci struct device *dev; 13462306a36Sopenharmony_ci struct clk *iclk; 13562306a36Sopenharmony_ci int irq; 13662306a36Sopenharmony_ci void __iomem *io_base; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci spinlock_t lock; 13962306a36Sopenharmony_ci struct tasklet_struct done_task; 14062306a36Sopenharmony_ci struct tasklet_struct queue_task; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci unsigned long flags; 14362306a36Sopenharmony_ci struct crypto_queue queue; 14462306a36Sopenharmony_ci struct ahash_request *req; 14562306a36Sopenharmony_ci bool is_async; 14662306a36Sopenharmony_ci bool force_complete; 14762306a36Sopenharmony_ci atmel_sha_fn_t resume; 14862306a36Sopenharmony_ci atmel_sha_fn_t cpu_transfer_complete; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci struct atmel_sha_dma dma_lch_in; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci struct atmel_sha_caps caps; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci struct scatterlist tmp; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci u32 hw_version; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistruct atmel_sha_drv { 16062306a36Sopenharmony_ci struct list_head dev_list; 16162306a36Sopenharmony_ci spinlock_t lock; 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic struct atmel_sha_drv atmel_sha = { 16562306a36Sopenharmony_ci .dev_list = LIST_HEAD_INIT(atmel_sha.dev_list), 16662306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(atmel_sha.lock), 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#ifdef VERBOSE_DEBUG 17062306a36Sopenharmony_cistatic const char *atmel_sha_reg_name(u32 offset, char *tmp, size_t sz, bool wr) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci switch (offset) { 17362306a36Sopenharmony_ci case SHA_CR: 17462306a36Sopenharmony_ci return "CR"; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci case SHA_MR: 17762306a36Sopenharmony_ci return "MR"; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci case SHA_IER: 18062306a36Sopenharmony_ci return "IER"; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci case SHA_IDR: 18362306a36Sopenharmony_ci return "IDR"; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci case SHA_IMR: 18662306a36Sopenharmony_ci return "IMR"; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci case SHA_ISR: 18962306a36Sopenharmony_ci return "ISR"; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci case SHA_MSR: 19262306a36Sopenharmony_ci return "MSR"; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci case SHA_BCR: 19562306a36Sopenharmony_ci return "BCR"; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci case SHA_REG_DIN(0): 19862306a36Sopenharmony_ci case SHA_REG_DIN(1): 19962306a36Sopenharmony_ci case SHA_REG_DIN(2): 20062306a36Sopenharmony_ci case SHA_REG_DIN(3): 20162306a36Sopenharmony_ci case SHA_REG_DIN(4): 20262306a36Sopenharmony_ci case SHA_REG_DIN(5): 20362306a36Sopenharmony_ci case SHA_REG_DIN(6): 20462306a36Sopenharmony_ci case SHA_REG_DIN(7): 20562306a36Sopenharmony_ci case SHA_REG_DIN(8): 20662306a36Sopenharmony_ci case SHA_REG_DIN(9): 20762306a36Sopenharmony_ci case SHA_REG_DIN(10): 20862306a36Sopenharmony_ci case SHA_REG_DIN(11): 20962306a36Sopenharmony_ci case SHA_REG_DIN(12): 21062306a36Sopenharmony_ci case SHA_REG_DIN(13): 21162306a36Sopenharmony_ci case SHA_REG_DIN(14): 21262306a36Sopenharmony_ci case SHA_REG_DIN(15): 21362306a36Sopenharmony_ci snprintf(tmp, sz, "IDATAR[%u]", (offset - SHA_REG_DIN(0)) >> 2); 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci case SHA_REG_DIGEST(0): 21762306a36Sopenharmony_ci case SHA_REG_DIGEST(1): 21862306a36Sopenharmony_ci case SHA_REG_DIGEST(2): 21962306a36Sopenharmony_ci case SHA_REG_DIGEST(3): 22062306a36Sopenharmony_ci case SHA_REG_DIGEST(4): 22162306a36Sopenharmony_ci case SHA_REG_DIGEST(5): 22262306a36Sopenharmony_ci case SHA_REG_DIGEST(6): 22362306a36Sopenharmony_ci case SHA_REG_DIGEST(7): 22462306a36Sopenharmony_ci case SHA_REG_DIGEST(8): 22562306a36Sopenharmony_ci case SHA_REG_DIGEST(9): 22662306a36Sopenharmony_ci case SHA_REG_DIGEST(10): 22762306a36Sopenharmony_ci case SHA_REG_DIGEST(11): 22862306a36Sopenharmony_ci case SHA_REG_DIGEST(12): 22962306a36Sopenharmony_ci case SHA_REG_DIGEST(13): 23062306a36Sopenharmony_ci case SHA_REG_DIGEST(14): 23162306a36Sopenharmony_ci case SHA_REG_DIGEST(15): 23262306a36Sopenharmony_ci if (wr) 23362306a36Sopenharmony_ci snprintf(tmp, sz, "IDATAR[%u]", 23462306a36Sopenharmony_ci 16u + ((offset - SHA_REG_DIGEST(0)) >> 2)); 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci snprintf(tmp, sz, "ODATAR[%u]", 23762306a36Sopenharmony_ci (offset - SHA_REG_DIGEST(0)) >> 2); 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci case SHA_HW_VERSION: 24162306a36Sopenharmony_ci return "HWVER"; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci default: 24462306a36Sopenharmony_ci snprintf(tmp, sz, "0x%02x", offset); 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return tmp; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci#endif /* VERBOSE_DEBUG */ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic inline u32 atmel_sha_read(struct atmel_sha_dev *dd, u32 offset) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci u32 value = readl_relaxed(dd->io_base + offset); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci#ifdef VERBOSE_DEBUG 25862306a36Sopenharmony_ci if (dd->flags & SHA_FLAGS_DUMP_REG) { 25962306a36Sopenharmony_ci char tmp[16]; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci dev_vdbg(dd->dev, "read 0x%08x from %s\n", value, 26262306a36Sopenharmony_ci atmel_sha_reg_name(offset, tmp, sizeof(tmp), false)); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci#endif /* VERBOSE_DEBUG */ 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return value; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic inline void atmel_sha_write(struct atmel_sha_dev *dd, 27062306a36Sopenharmony_ci u32 offset, u32 value) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci#ifdef VERBOSE_DEBUG 27362306a36Sopenharmony_ci if (dd->flags & SHA_FLAGS_DUMP_REG) { 27462306a36Sopenharmony_ci char tmp[16]; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci dev_vdbg(dd->dev, "write 0x%08x into %s\n", value, 27762306a36Sopenharmony_ci atmel_sha_reg_name(offset, tmp, sizeof(tmp), true)); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci#endif /* VERBOSE_DEBUG */ 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci writel_relaxed(value, dd->io_base + offset); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic inline int atmel_sha_complete(struct atmel_sha_dev *dd, int err) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct ahash_request *req = dd->req; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci dd->flags &= ~(SHA_FLAGS_BUSY | SHA_FLAGS_FINAL | SHA_FLAGS_CPU | 28962306a36Sopenharmony_ci SHA_FLAGS_DMA_READY | SHA_FLAGS_OUTPUT_READY | 29062306a36Sopenharmony_ci SHA_FLAGS_DUMP_REG); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci clk_disable(dd->iclk); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if ((dd->is_async || dd->force_complete) && req->base.complete) 29562306a36Sopenharmony_ci ahash_request_complete(req, err); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* handle new request */ 29862306a36Sopenharmony_ci tasklet_schedule(&dd->queue_task); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return err; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic size_t atmel_sha_append_sg(struct atmel_sha_reqctx *ctx) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci size_t count; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci while ((ctx->bufcnt < ctx->buflen) && ctx->total) { 30862306a36Sopenharmony_ci count = min(ctx->sg->length - ctx->offset, ctx->total); 30962306a36Sopenharmony_ci count = min(count, ctx->buflen - ctx->bufcnt); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (count <= 0) { 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * Check if count <= 0 because the buffer is full or 31462306a36Sopenharmony_ci * because the sg length is 0. In the latest case, 31562306a36Sopenharmony_ci * check if there is another sg in the list, a 0 length 31662306a36Sopenharmony_ci * sg doesn't necessarily mean the end of the sg list. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci if ((ctx->sg->length == 0) && !sg_is_last(ctx->sg)) { 31962306a36Sopenharmony_ci ctx->sg = sg_next(ctx->sg); 32062306a36Sopenharmony_ci continue; 32162306a36Sopenharmony_ci } else { 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci scatterwalk_map_and_copy(ctx->buffer + ctx->bufcnt, ctx->sg, 32762306a36Sopenharmony_ci ctx->offset, count, 0); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ctx->bufcnt += count; 33062306a36Sopenharmony_ci ctx->offset += count; 33162306a36Sopenharmony_ci ctx->total -= count; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (ctx->offset == ctx->sg->length) { 33462306a36Sopenharmony_ci ctx->sg = sg_next(ctx->sg); 33562306a36Sopenharmony_ci if (ctx->sg) 33662306a36Sopenharmony_ci ctx->offset = 0; 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci ctx->total = 0; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * The purpose of this padding is to ensure that the padded message is a 34762306a36Sopenharmony_ci * multiple of 512 bits (SHA1/SHA224/SHA256) or 1024 bits (SHA384/SHA512). 34862306a36Sopenharmony_ci * The bit "1" is appended at the end of the message followed by 34962306a36Sopenharmony_ci * "padlen-1" zero bits. Then a 64 bits block (SHA1/SHA224/SHA256) or 35062306a36Sopenharmony_ci * 128 bits block (SHA384/SHA512) equals to the message length in bits 35162306a36Sopenharmony_ci * is appended. 35262306a36Sopenharmony_ci * 35362306a36Sopenharmony_ci * For SHA1/SHA224/SHA256, padlen is calculated as followed: 35462306a36Sopenharmony_ci * - if message length < 56 bytes then padlen = 56 - message length 35562306a36Sopenharmony_ci * - else padlen = 64 + 56 - message length 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * For SHA384/SHA512, padlen is calculated as followed: 35862306a36Sopenharmony_ci * - if message length < 112 bytes then padlen = 112 - message length 35962306a36Sopenharmony_ci * - else padlen = 128 + 112 - message length 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_cistatic void atmel_sha_fill_padding(struct atmel_sha_reqctx *ctx, int length) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci unsigned int index, padlen; 36462306a36Sopenharmony_ci __be64 bits[2]; 36562306a36Sopenharmony_ci u64 size[2]; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci size[0] = ctx->digcnt[0]; 36862306a36Sopenharmony_ci size[1] = ctx->digcnt[1]; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci size[0] += ctx->bufcnt; 37162306a36Sopenharmony_ci if (size[0] < ctx->bufcnt) 37262306a36Sopenharmony_ci size[1]++; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci size[0] += length; 37562306a36Sopenharmony_ci if (size[0] < length) 37662306a36Sopenharmony_ci size[1]++; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci bits[1] = cpu_to_be64(size[0] << 3); 37962306a36Sopenharmony_ci bits[0] = cpu_to_be64(size[1] << 3 | size[0] >> 61); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { 38262306a36Sopenharmony_ci case SHA_FLAGS_SHA384: 38362306a36Sopenharmony_ci case SHA_FLAGS_SHA512: 38462306a36Sopenharmony_ci index = ctx->bufcnt & 0x7f; 38562306a36Sopenharmony_ci padlen = (index < 112) ? (112 - index) : ((128+112) - index); 38662306a36Sopenharmony_ci *(ctx->buffer + ctx->bufcnt) = 0x80; 38762306a36Sopenharmony_ci memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen-1); 38862306a36Sopenharmony_ci memcpy(ctx->buffer + ctx->bufcnt + padlen, bits, 16); 38962306a36Sopenharmony_ci ctx->bufcnt += padlen + 16; 39062306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_PAD; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci default: 39462306a36Sopenharmony_ci index = ctx->bufcnt & 0x3f; 39562306a36Sopenharmony_ci padlen = (index < 56) ? (56 - index) : ((64+56) - index); 39662306a36Sopenharmony_ci *(ctx->buffer + ctx->bufcnt) = 0x80; 39762306a36Sopenharmony_ci memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen-1); 39862306a36Sopenharmony_ci memcpy(ctx->buffer + ctx->bufcnt + padlen, &bits[1], 8); 39962306a36Sopenharmony_ci ctx->bufcnt += padlen + 8; 40062306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_PAD; 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic struct atmel_sha_dev *atmel_sha_find_dev(struct atmel_sha_ctx *tctx) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct atmel_sha_dev *dd = NULL; 40862306a36Sopenharmony_ci struct atmel_sha_dev *tmp; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci spin_lock_bh(&atmel_sha.lock); 41162306a36Sopenharmony_ci if (!tctx->dd) { 41262306a36Sopenharmony_ci list_for_each_entry(tmp, &atmel_sha.dev_list, list) { 41362306a36Sopenharmony_ci dd = tmp; 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci tctx->dd = dd; 41762306a36Sopenharmony_ci } else { 41862306a36Sopenharmony_ci dd = tctx->dd; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci spin_unlock_bh(&atmel_sha.lock); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return dd; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int atmel_sha_init(struct ahash_request *req) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 42962306a36Sopenharmony_ci struct atmel_sha_ctx *tctx = crypto_ahash_ctx(tfm); 43062306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 43162306a36Sopenharmony_ci struct atmel_sha_dev *dd = atmel_sha_find_dev(tctx); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci ctx->dd = dd; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci ctx->flags = 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci dev_dbg(dd->dev, "init: digest size: %u\n", 43862306a36Sopenharmony_ci crypto_ahash_digestsize(tfm)); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci switch (crypto_ahash_digestsize(tfm)) { 44162306a36Sopenharmony_ci case SHA1_DIGEST_SIZE: 44262306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_SHA1; 44362306a36Sopenharmony_ci ctx->block_size = SHA1_BLOCK_SIZE; 44462306a36Sopenharmony_ci break; 44562306a36Sopenharmony_ci case SHA224_DIGEST_SIZE: 44662306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_SHA224; 44762306a36Sopenharmony_ci ctx->block_size = SHA224_BLOCK_SIZE; 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci case SHA256_DIGEST_SIZE: 45062306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_SHA256; 45162306a36Sopenharmony_ci ctx->block_size = SHA256_BLOCK_SIZE; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case SHA384_DIGEST_SIZE: 45462306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_SHA384; 45562306a36Sopenharmony_ci ctx->block_size = SHA384_BLOCK_SIZE; 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci case SHA512_DIGEST_SIZE: 45862306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_SHA512; 45962306a36Sopenharmony_ci ctx->block_size = SHA512_BLOCK_SIZE; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci default: 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ctx->bufcnt = 0; 46662306a36Sopenharmony_ci ctx->digcnt[0] = 0; 46762306a36Sopenharmony_ci ctx->digcnt[1] = 0; 46862306a36Sopenharmony_ci ctx->buflen = SHA_BUFFER_LEN; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic void atmel_sha_write_ctrl(struct atmel_sha_dev *dd, int dma) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); 47662306a36Sopenharmony_ci u32 valmr = SHA_MR_MODE_AUTO; 47762306a36Sopenharmony_ci unsigned int i, hashsize = 0; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (likely(dma)) { 48062306a36Sopenharmony_ci if (!dd->caps.has_dma) 48162306a36Sopenharmony_ci atmel_sha_write(dd, SHA_IER, SHA_INT_TXBUFE); 48262306a36Sopenharmony_ci valmr = SHA_MR_MODE_PDC; 48362306a36Sopenharmony_ci if (dd->caps.has_dualbuff) 48462306a36Sopenharmony_ci valmr |= SHA_MR_DUALBUFF; 48562306a36Sopenharmony_ci } else { 48662306a36Sopenharmony_ci atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY); 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { 49062306a36Sopenharmony_ci case SHA_FLAGS_SHA1: 49162306a36Sopenharmony_ci valmr |= SHA_MR_ALGO_SHA1; 49262306a36Sopenharmony_ci hashsize = SHA1_DIGEST_SIZE; 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci case SHA_FLAGS_SHA224: 49662306a36Sopenharmony_ci valmr |= SHA_MR_ALGO_SHA224; 49762306a36Sopenharmony_ci hashsize = SHA256_DIGEST_SIZE; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci case SHA_FLAGS_SHA256: 50162306a36Sopenharmony_ci valmr |= SHA_MR_ALGO_SHA256; 50262306a36Sopenharmony_ci hashsize = SHA256_DIGEST_SIZE; 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci case SHA_FLAGS_SHA384: 50662306a36Sopenharmony_ci valmr |= SHA_MR_ALGO_SHA384; 50762306a36Sopenharmony_ci hashsize = SHA512_DIGEST_SIZE; 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci case SHA_FLAGS_SHA512: 51162306a36Sopenharmony_ci valmr |= SHA_MR_ALGO_SHA512; 51262306a36Sopenharmony_ci hashsize = SHA512_DIGEST_SIZE; 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci default: 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Setting CR_FIRST only for the first iteration */ 52062306a36Sopenharmony_ci if (!(ctx->digcnt[0] || ctx->digcnt[1])) { 52162306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); 52262306a36Sopenharmony_ci } else if (dd->caps.has_uihv && (ctx->flags & SHA_FLAGS_RESTORE)) { 52362306a36Sopenharmony_ci const u32 *hash = (const u32 *)ctx->digest; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* 52662306a36Sopenharmony_ci * Restore the hardware context: update the User Initialize 52762306a36Sopenharmony_ci * Hash Value (UIHV) with the value saved when the latest 52862306a36Sopenharmony_ci * 'update' operation completed on this very same crypto 52962306a36Sopenharmony_ci * request. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci ctx->flags &= ~SHA_FLAGS_RESTORE; 53262306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_WUIHV); 53362306a36Sopenharmony_ci for (i = 0; i < hashsize / sizeof(u32); ++i) 53462306a36Sopenharmony_ci atmel_sha_write(dd, SHA_REG_DIN(i), hash[i]); 53562306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); 53662306a36Sopenharmony_ci valmr |= SHA_MR_UIHV; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci /* 53962306a36Sopenharmony_ci * WARNING: If the UIHV feature is not available, the hardware CANNOT 54062306a36Sopenharmony_ci * process concurrent requests: the internal registers used to store 54162306a36Sopenharmony_ci * the hash/digest are still set to the partial digest output values 54262306a36Sopenharmony_ci * computed during the latest round. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MR, valmr); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic inline int atmel_sha_wait_for_data_ready(struct atmel_sha_dev *dd, 54962306a36Sopenharmony_ci atmel_sha_fn_t resume) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci u32 isr = atmel_sha_read(dd, SHA_ISR); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (unlikely(isr & SHA_INT_DATARDY)) 55462306a36Sopenharmony_ci return resume(dd); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci dd->resume = resume; 55762306a36Sopenharmony_ci atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY); 55862306a36Sopenharmony_ci return -EINPROGRESS; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int atmel_sha_xmit_cpu(struct atmel_sha_dev *dd, const u8 *buf, 56262306a36Sopenharmony_ci size_t length, int final) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); 56562306a36Sopenharmony_ci int count, len32; 56662306a36Sopenharmony_ci const u32 *buffer = (const u32 *)buf; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci dev_dbg(dd->dev, "xmit_cpu: digcnt: 0x%llx 0x%llx, length: %zd, final: %d\n", 56962306a36Sopenharmony_ci ctx->digcnt[1], ctx->digcnt[0], length, final); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci atmel_sha_write_ctrl(dd, 0); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* should be non-zero before next lines to disable clocks later */ 57462306a36Sopenharmony_ci ctx->digcnt[0] += length; 57562306a36Sopenharmony_ci if (ctx->digcnt[0] < length) 57662306a36Sopenharmony_ci ctx->digcnt[1]++; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (final) 57962306a36Sopenharmony_ci dd->flags |= SHA_FLAGS_FINAL; /* catch last interrupt */ 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci len32 = DIV_ROUND_UP(length, sizeof(u32)); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci dd->flags |= SHA_FLAGS_CPU; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci for (count = 0; count < len32; count++) 58662306a36Sopenharmony_ci atmel_sha_write(dd, SHA_REG_DIN(count), buffer[count]); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return -EINPROGRESS; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int atmel_sha_xmit_pdc(struct atmel_sha_dev *dd, dma_addr_t dma_addr1, 59262306a36Sopenharmony_ci size_t length1, dma_addr_t dma_addr2, size_t length2, int final) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); 59562306a36Sopenharmony_ci int len32; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci dev_dbg(dd->dev, "xmit_pdc: digcnt: 0x%llx 0x%llx, length: %zd, final: %d\n", 59862306a36Sopenharmony_ci ctx->digcnt[1], ctx->digcnt[0], length1, final); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci len32 = DIV_ROUND_UP(length1, sizeof(u32)); 60162306a36Sopenharmony_ci atmel_sha_write(dd, SHA_PTCR, SHA_PTCR_TXTDIS); 60262306a36Sopenharmony_ci atmel_sha_write(dd, SHA_TPR, dma_addr1); 60362306a36Sopenharmony_ci atmel_sha_write(dd, SHA_TCR, len32); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci len32 = DIV_ROUND_UP(length2, sizeof(u32)); 60662306a36Sopenharmony_ci atmel_sha_write(dd, SHA_TNPR, dma_addr2); 60762306a36Sopenharmony_ci atmel_sha_write(dd, SHA_TNCR, len32); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci atmel_sha_write_ctrl(dd, 1); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* should be non-zero before next lines to disable clocks later */ 61262306a36Sopenharmony_ci ctx->digcnt[0] += length1; 61362306a36Sopenharmony_ci if (ctx->digcnt[0] < length1) 61462306a36Sopenharmony_ci ctx->digcnt[1]++; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (final) 61762306a36Sopenharmony_ci dd->flags |= SHA_FLAGS_FINAL; /* catch last interrupt */ 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci dd->flags |= SHA_FLAGS_DMA_ACTIVE; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Start DMA transfer */ 62262306a36Sopenharmony_ci atmel_sha_write(dd, SHA_PTCR, SHA_PTCR_TXTEN); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return -EINPROGRESS; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic void atmel_sha_dma_callback(void *data) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct atmel_sha_dev *dd = data; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci dd->is_async = true; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* dma_lch_in - completed - wait DATRDY */ 63462306a36Sopenharmony_ci atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY); 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int atmel_sha_xmit_dma(struct atmel_sha_dev *dd, dma_addr_t dma_addr1, 63862306a36Sopenharmony_ci size_t length1, dma_addr_t dma_addr2, size_t length2, int final) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); 64162306a36Sopenharmony_ci struct dma_async_tx_descriptor *in_desc; 64262306a36Sopenharmony_ci struct scatterlist sg[2]; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci dev_dbg(dd->dev, "xmit_dma: digcnt: 0x%llx 0x%llx, length: %zd, final: %d\n", 64562306a36Sopenharmony_ci ctx->digcnt[1], ctx->digcnt[0], length1, final); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci dd->dma_lch_in.dma_conf.src_maxburst = 16; 64862306a36Sopenharmony_ci dd->dma_lch_in.dma_conf.dst_maxburst = 16; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (length2) { 65362306a36Sopenharmony_ci sg_init_table(sg, 2); 65462306a36Sopenharmony_ci sg_dma_address(&sg[0]) = dma_addr1; 65562306a36Sopenharmony_ci sg_dma_len(&sg[0]) = length1; 65662306a36Sopenharmony_ci sg_dma_address(&sg[1]) = dma_addr2; 65762306a36Sopenharmony_ci sg_dma_len(&sg[1]) = length2; 65862306a36Sopenharmony_ci in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, sg, 2, 65962306a36Sopenharmony_ci DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 66062306a36Sopenharmony_ci } else { 66162306a36Sopenharmony_ci sg_init_table(sg, 1); 66262306a36Sopenharmony_ci sg_dma_address(&sg[0]) = dma_addr1; 66362306a36Sopenharmony_ci sg_dma_len(&sg[0]) = length1; 66462306a36Sopenharmony_ci in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, sg, 1, 66562306a36Sopenharmony_ci DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (!in_desc) 66862306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci in_desc->callback = atmel_sha_dma_callback; 67162306a36Sopenharmony_ci in_desc->callback_param = dd; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci atmel_sha_write_ctrl(dd, 1); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* should be non-zero before next lines to disable clocks later */ 67662306a36Sopenharmony_ci ctx->digcnt[0] += length1; 67762306a36Sopenharmony_ci if (ctx->digcnt[0] < length1) 67862306a36Sopenharmony_ci ctx->digcnt[1]++; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (final) 68162306a36Sopenharmony_ci dd->flags |= SHA_FLAGS_FINAL; /* catch last interrupt */ 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci dd->flags |= SHA_FLAGS_DMA_ACTIVE; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* Start DMA transfer */ 68662306a36Sopenharmony_ci dmaengine_submit(in_desc); 68762306a36Sopenharmony_ci dma_async_issue_pending(dd->dma_lch_in.chan); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return -EINPROGRESS; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int atmel_sha_xmit_start(struct atmel_sha_dev *dd, dma_addr_t dma_addr1, 69362306a36Sopenharmony_ci size_t length1, dma_addr_t dma_addr2, size_t length2, int final) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci if (dd->caps.has_dma) 69662306a36Sopenharmony_ci return atmel_sha_xmit_dma(dd, dma_addr1, length1, 69762306a36Sopenharmony_ci dma_addr2, length2, final); 69862306a36Sopenharmony_ci else 69962306a36Sopenharmony_ci return atmel_sha_xmit_pdc(dd, dma_addr1, length1, 70062306a36Sopenharmony_ci dma_addr2, length2, final); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int atmel_sha_update_cpu(struct atmel_sha_dev *dd) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); 70662306a36Sopenharmony_ci int bufcnt; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci atmel_sha_append_sg(ctx); 70962306a36Sopenharmony_ci atmel_sha_fill_padding(ctx, 0); 71062306a36Sopenharmony_ci bufcnt = ctx->bufcnt; 71162306a36Sopenharmony_ci ctx->bufcnt = 0; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return atmel_sha_xmit_cpu(dd, ctx->buffer, bufcnt, 1); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int atmel_sha_xmit_dma_map(struct atmel_sha_dev *dd, 71762306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx, 71862306a36Sopenharmony_ci size_t length, int final) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, 72162306a36Sopenharmony_ci ctx->buflen + ctx->block_size, DMA_TO_DEVICE); 72262306a36Sopenharmony_ci if (dma_mapping_error(dd->dev, ctx->dma_addr)) { 72362306a36Sopenharmony_ci dev_err(dd->dev, "dma %zu bytes error\n", ctx->buflen + 72462306a36Sopenharmony_ci ctx->block_size); 72562306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci ctx->flags &= ~SHA_FLAGS_SG; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* next call does not fail... so no unmap in the case of error */ 73162306a36Sopenharmony_ci return atmel_sha_xmit_start(dd, ctx->dma_addr, length, 0, 0, final); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic int atmel_sha_update_dma_slow(struct atmel_sha_dev *dd) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); 73762306a36Sopenharmony_ci unsigned int final; 73862306a36Sopenharmony_ci size_t count; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci atmel_sha_append_sg(ctx); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci final = (ctx->flags & SHA_FLAGS_FINUP) && !ctx->total; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci dev_dbg(dd->dev, "slow: bufcnt: %zu, digcnt: 0x%llx 0x%llx, final: %d\n", 74562306a36Sopenharmony_ci ctx->bufcnt, ctx->digcnt[1], ctx->digcnt[0], final); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (final) 74862306a36Sopenharmony_ci atmel_sha_fill_padding(ctx, 0); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (final || (ctx->bufcnt == ctx->buflen)) { 75162306a36Sopenharmony_ci count = ctx->bufcnt; 75262306a36Sopenharmony_ci ctx->bufcnt = 0; 75362306a36Sopenharmony_ci return atmel_sha_xmit_dma_map(dd, ctx, count, final); 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int atmel_sha_update_dma_start(struct atmel_sha_dev *dd) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); 76262306a36Sopenharmony_ci unsigned int length, final, tail; 76362306a36Sopenharmony_ci struct scatterlist *sg; 76462306a36Sopenharmony_ci unsigned int count; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (!ctx->total) 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (ctx->bufcnt || ctx->offset) 77062306a36Sopenharmony_ci return atmel_sha_update_dma_slow(dd); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci dev_dbg(dd->dev, "fast: digcnt: 0x%llx 0x%llx, bufcnt: %zd, total: %u\n", 77362306a36Sopenharmony_ci ctx->digcnt[1], ctx->digcnt[0], ctx->bufcnt, ctx->total); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci sg = ctx->sg; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!IS_ALIGNED(sg->offset, sizeof(u32))) 77862306a36Sopenharmony_ci return atmel_sha_update_dma_slow(dd); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (!sg_is_last(sg) && !IS_ALIGNED(sg->length, ctx->block_size)) 78162306a36Sopenharmony_ci /* size is not ctx->block_size aligned */ 78262306a36Sopenharmony_ci return atmel_sha_update_dma_slow(dd); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci length = min(ctx->total, sg->length); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (sg_is_last(sg)) { 78762306a36Sopenharmony_ci if (!(ctx->flags & SHA_FLAGS_FINUP)) { 78862306a36Sopenharmony_ci /* not last sg must be ctx->block_size aligned */ 78962306a36Sopenharmony_ci tail = length & (ctx->block_size - 1); 79062306a36Sopenharmony_ci length -= tail; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci ctx->total -= length; 79562306a36Sopenharmony_ci ctx->offset = length; /* offset where to start slow */ 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci final = (ctx->flags & SHA_FLAGS_FINUP) && !ctx->total; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* Add padding */ 80062306a36Sopenharmony_ci if (final) { 80162306a36Sopenharmony_ci tail = length & (ctx->block_size - 1); 80262306a36Sopenharmony_ci length -= tail; 80362306a36Sopenharmony_ci ctx->total += tail; 80462306a36Sopenharmony_ci ctx->offset = length; /* offset where to start slow */ 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci sg = ctx->sg; 80762306a36Sopenharmony_ci atmel_sha_append_sg(ctx); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci atmel_sha_fill_padding(ctx, length); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, 81262306a36Sopenharmony_ci ctx->buflen + ctx->block_size, DMA_TO_DEVICE); 81362306a36Sopenharmony_ci if (dma_mapping_error(dd->dev, ctx->dma_addr)) { 81462306a36Sopenharmony_ci dev_err(dd->dev, "dma %zu bytes error\n", 81562306a36Sopenharmony_ci ctx->buflen + ctx->block_size); 81662306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (length == 0) { 82062306a36Sopenharmony_ci ctx->flags &= ~SHA_FLAGS_SG; 82162306a36Sopenharmony_ci count = ctx->bufcnt; 82262306a36Sopenharmony_ci ctx->bufcnt = 0; 82362306a36Sopenharmony_ci return atmel_sha_xmit_start(dd, ctx->dma_addr, count, 0, 82462306a36Sopenharmony_ci 0, final); 82562306a36Sopenharmony_ci } else { 82662306a36Sopenharmony_ci ctx->sg = sg; 82762306a36Sopenharmony_ci if (!dma_map_sg(dd->dev, ctx->sg, 1, 82862306a36Sopenharmony_ci DMA_TO_DEVICE)) { 82962306a36Sopenharmony_ci dev_err(dd->dev, "dma_map_sg error\n"); 83062306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_SG; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci count = ctx->bufcnt; 83662306a36Sopenharmony_ci ctx->bufcnt = 0; 83762306a36Sopenharmony_ci return atmel_sha_xmit_start(dd, sg_dma_address(ctx->sg), 83862306a36Sopenharmony_ci length, ctx->dma_addr, count, final); 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (!dma_map_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE)) { 84362306a36Sopenharmony_ci dev_err(dd->dev, "dma_map_sg error\n"); 84462306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_SG; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* next call does not fail... so no unmap in the case of error */ 85062306a36Sopenharmony_ci return atmel_sha_xmit_start(dd, sg_dma_address(ctx->sg), length, 0, 85162306a36Sopenharmony_ci 0, final); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic void atmel_sha_update_dma_stop(struct atmel_sha_dev *dd) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (ctx->flags & SHA_FLAGS_SG) { 85962306a36Sopenharmony_ci dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); 86062306a36Sopenharmony_ci if (ctx->sg->length == ctx->offset) { 86162306a36Sopenharmony_ci ctx->sg = sg_next(ctx->sg); 86262306a36Sopenharmony_ci if (ctx->sg) 86362306a36Sopenharmony_ci ctx->offset = 0; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci if (ctx->flags & SHA_FLAGS_PAD) { 86662306a36Sopenharmony_ci dma_unmap_single(dd->dev, ctx->dma_addr, 86762306a36Sopenharmony_ci ctx->buflen + ctx->block_size, DMA_TO_DEVICE); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci } else { 87062306a36Sopenharmony_ci dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen + 87162306a36Sopenharmony_ci ctx->block_size, DMA_TO_DEVICE); 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int atmel_sha_update_req(struct atmel_sha_dev *dd) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct ahash_request *req = dd->req; 87862306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 87962306a36Sopenharmony_ci int err; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci dev_dbg(dd->dev, "update_req: total: %u, digcnt: 0x%llx 0x%llx\n", 88262306a36Sopenharmony_ci ctx->total, ctx->digcnt[1], ctx->digcnt[0]); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (ctx->flags & SHA_FLAGS_CPU) 88562306a36Sopenharmony_ci err = atmel_sha_update_cpu(dd); 88662306a36Sopenharmony_ci else 88762306a36Sopenharmony_ci err = atmel_sha_update_dma_start(dd); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* wait for dma completion before can take more data */ 89062306a36Sopenharmony_ci dev_dbg(dd->dev, "update: err: %d, digcnt: 0x%llx 0%llx\n", 89162306a36Sopenharmony_ci err, ctx->digcnt[1], ctx->digcnt[0]); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return err; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic int atmel_sha_final_req(struct atmel_sha_dev *dd) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci struct ahash_request *req = dd->req; 89962306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 90062306a36Sopenharmony_ci int err = 0; 90162306a36Sopenharmony_ci int count; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (ctx->bufcnt >= ATMEL_SHA_DMA_THRESHOLD) { 90462306a36Sopenharmony_ci atmel_sha_fill_padding(ctx, 0); 90562306a36Sopenharmony_ci count = ctx->bufcnt; 90662306a36Sopenharmony_ci ctx->bufcnt = 0; 90762306a36Sopenharmony_ci err = atmel_sha_xmit_dma_map(dd, ctx, count, 1); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci /* faster to handle last block with cpu */ 91062306a36Sopenharmony_ci else { 91162306a36Sopenharmony_ci atmel_sha_fill_padding(ctx, 0); 91262306a36Sopenharmony_ci count = ctx->bufcnt; 91362306a36Sopenharmony_ci ctx->bufcnt = 0; 91462306a36Sopenharmony_ci err = atmel_sha_xmit_cpu(dd, ctx->buffer, count, 1); 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci dev_dbg(dd->dev, "final_req: err: %d\n", err); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return err; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic void atmel_sha_copy_hash(struct ahash_request *req) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 92562306a36Sopenharmony_ci u32 *hash = (u32 *)ctx->digest; 92662306a36Sopenharmony_ci unsigned int i, hashsize; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { 92962306a36Sopenharmony_ci case SHA_FLAGS_SHA1: 93062306a36Sopenharmony_ci hashsize = SHA1_DIGEST_SIZE; 93162306a36Sopenharmony_ci break; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci case SHA_FLAGS_SHA224: 93462306a36Sopenharmony_ci case SHA_FLAGS_SHA256: 93562306a36Sopenharmony_ci hashsize = SHA256_DIGEST_SIZE; 93662306a36Sopenharmony_ci break; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci case SHA_FLAGS_SHA384: 93962306a36Sopenharmony_ci case SHA_FLAGS_SHA512: 94062306a36Sopenharmony_ci hashsize = SHA512_DIGEST_SIZE; 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci default: 94462306a36Sopenharmony_ci /* Should not happen... */ 94562306a36Sopenharmony_ci return; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci for (i = 0; i < hashsize / sizeof(u32); ++i) 94962306a36Sopenharmony_ci hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i)); 95062306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_RESTORE; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic void atmel_sha_copy_ready_hash(struct ahash_request *req) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (!req->result) 95862306a36Sopenharmony_ci return; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { 96162306a36Sopenharmony_ci default: 96262306a36Sopenharmony_ci case SHA_FLAGS_SHA1: 96362306a36Sopenharmony_ci memcpy(req->result, ctx->digest, SHA1_DIGEST_SIZE); 96462306a36Sopenharmony_ci break; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci case SHA_FLAGS_SHA224: 96762306a36Sopenharmony_ci memcpy(req->result, ctx->digest, SHA224_DIGEST_SIZE); 96862306a36Sopenharmony_ci break; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci case SHA_FLAGS_SHA256: 97162306a36Sopenharmony_ci memcpy(req->result, ctx->digest, SHA256_DIGEST_SIZE); 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci case SHA_FLAGS_SHA384: 97562306a36Sopenharmony_ci memcpy(req->result, ctx->digest, SHA384_DIGEST_SIZE); 97662306a36Sopenharmony_ci break; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci case SHA_FLAGS_SHA512: 97962306a36Sopenharmony_ci memcpy(req->result, ctx->digest, SHA512_DIGEST_SIZE); 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic int atmel_sha_finish(struct ahash_request *req) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 98762306a36Sopenharmony_ci struct atmel_sha_dev *dd = ctx->dd; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (ctx->digcnt[0] || ctx->digcnt[1]) 99062306a36Sopenharmony_ci atmel_sha_copy_ready_hash(req); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci dev_dbg(dd->dev, "digcnt: 0x%llx 0x%llx, bufcnt: %zd\n", ctx->digcnt[1], 99362306a36Sopenharmony_ci ctx->digcnt[0], ctx->bufcnt); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return 0; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic void atmel_sha_finish_req(struct ahash_request *req, int err) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 100162306a36Sopenharmony_ci struct atmel_sha_dev *dd = ctx->dd; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (!err) { 100462306a36Sopenharmony_ci atmel_sha_copy_hash(req); 100562306a36Sopenharmony_ci if (SHA_FLAGS_FINAL & dd->flags) 100662306a36Sopenharmony_ci err = atmel_sha_finish(req); 100762306a36Sopenharmony_ci } else { 100862306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_ERROR; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* atomic operation is not needed here */ 101262306a36Sopenharmony_ci (void)atmel_sha_complete(dd, err); 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic int atmel_sha_hw_init(struct atmel_sha_dev *dd) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci int err; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci err = clk_enable(dd->iclk); 102062306a36Sopenharmony_ci if (err) 102162306a36Sopenharmony_ci return err; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (!(SHA_FLAGS_INIT & dd->flags)) { 102462306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_SWRST); 102562306a36Sopenharmony_ci dd->flags |= SHA_FLAGS_INIT; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci return 0; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cistatic inline unsigned int atmel_sha_get_version(struct atmel_sha_dev *dd) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci return atmel_sha_read(dd, SHA_HW_VERSION) & 0x00000fff; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic int atmel_sha_hw_version_init(struct atmel_sha_dev *dd) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci int err; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci err = atmel_sha_hw_init(dd); 104162306a36Sopenharmony_ci if (err) 104262306a36Sopenharmony_ci return err; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci dd->hw_version = atmel_sha_get_version(dd); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci dev_info(dd->dev, 104762306a36Sopenharmony_ci "version: 0x%x\n", dd->hw_version); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci clk_disable(dd->iclk); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return 0; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int atmel_sha_handle_queue(struct atmel_sha_dev *dd, 105562306a36Sopenharmony_ci struct ahash_request *req) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct crypto_async_request *async_req, *backlog; 105862306a36Sopenharmony_ci struct atmel_sha_ctx *ctx; 105962306a36Sopenharmony_ci unsigned long flags; 106062306a36Sopenharmony_ci bool start_async; 106162306a36Sopenharmony_ci int err = 0, ret = 0; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci spin_lock_irqsave(&dd->lock, flags); 106462306a36Sopenharmony_ci if (req) 106562306a36Sopenharmony_ci ret = ahash_enqueue_request(&dd->queue, req); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (SHA_FLAGS_BUSY & dd->flags) { 106862306a36Sopenharmony_ci spin_unlock_irqrestore(&dd->lock, flags); 106962306a36Sopenharmony_ci return ret; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci backlog = crypto_get_backlog(&dd->queue); 107362306a36Sopenharmony_ci async_req = crypto_dequeue_request(&dd->queue); 107462306a36Sopenharmony_ci if (async_req) 107562306a36Sopenharmony_ci dd->flags |= SHA_FLAGS_BUSY; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci spin_unlock_irqrestore(&dd->lock, flags); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (!async_req) 108062306a36Sopenharmony_ci return ret; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (backlog) 108362306a36Sopenharmony_ci crypto_request_complete(backlog, -EINPROGRESS); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci ctx = crypto_tfm_ctx(async_req->tfm); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci dd->req = ahash_request_cast(async_req); 108862306a36Sopenharmony_ci start_async = (dd->req != req); 108962306a36Sopenharmony_ci dd->is_async = start_async; 109062306a36Sopenharmony_ci dd->force_complete = false; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* WARNING: ctx->start() MAY change dd->is_async. */ 109362306a36Sopenharmony_ci err = ctx->start(dd); 109462306a36Sopenharmony_ci return (start_async) ? ret : err; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic int atmel_sha_done(struct atmel_sha_dev *dd); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic int atmel_sha_start(struct atmel_sha_dev *dd) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci struct ahash_request *req = dd->req; 110262306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 110362306a36Sopenharmony_ci int err; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci dev_dbg(dd->dev, "handling new req, op: %lu, nbytes: %u\n", 110662306a36Sopenharmony_ci ctx->op, req->nbytes); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci err = atmel_sha_hw_init(dd); 110962306a36Sopenharmony_ci if (err) 111062306a36Sopenharmony_ci return atmel_sha_complete(dd, err); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci /* 111362306a36Sopenharmony_ci * atmel_sha_update_req() and atmel_sha_final_req() can return either: 111462306a36Sopenharmony_ci * -EINPROGRESS: the hardware is busy and the SHA driver will resume 111562306a36Sopenharmony_ci * its job later in the done_task. 111662306a36Sopenharmony_ci * This is the main path. 111762306a36Sopenharmony_ci * 111862306a36Sopenharmony_ci * 0: the SHA driver can continue its job then release the hardware 111962306a36Sopenharmony_ci * later, if needed, with atmel_sha_finish_req(). 112062306a36Sopenharmony_ci * This is the alternate path. 112162306a36Sopenharmony_ci * 112262306a36Sopenharmony_ci * < 0: an error has occurred so atmel_sha_complete(dd, err) has already 112362306a36Sopenharmony_ci * been called, hence the hardware has been released. 112462306a36Sopenharmony_ci * The SHA driver must stop its job without calling 112562306a36Sopenharmony_ci * atmel_sha_finish_req(), otherwise atmel_sha_complete() would be 112662306a36Sopenharmony_ci * called a second time. 112762306a36Sopenharmony_ci * 112862306a36Sopenharmony_ci * Please note that currently, atmel_sha_final_req() never returns 0. 112962306a36Sopenharmony_ci */ 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci dd->resume = atmel_sha_done; 113262306a36Sopenharmony_ci if (ctx->op == SHA_OP_UPDATE) { 113362306a36Sopenharmony_ci err = atmel_sha_update_req(dd); 113462306a36Sopenharmony_ci if (!err && (ctx->flags & SHA_FLAGS_FINUP)) 113562306a36Sopenharmony_ci /* no final() after finup() */ 113662306a36Sopenharmony_ci err = atmel_sha_final_req(dd); 113762306a36Sopenharmony_ci } else if (ctx->op == SHA_OP_FINAL) { 113862306a36Sopenharmony_ci err = atmel_sha_final_req(dd); 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (!err) 114262306a36Sopenharmony_ci /* done_task will not finish it, so do it here */ 114362306a36Sopenharmony_ci atmel_sha_finish_req(req, err); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci dev_dbg(dd->dev, "exit, err: %d\n", err); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci return err; 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic int atmel_sha_enqueue(struct ahash_request *req, unsigned int op) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 115362306a36Sopenharmony_ci struct atmel_sha_ctx *tctx = crypto_tfm_ctx(req->base.tfm); 115462306a36Sopenharmony_ci struct atmel_sha_dev *dd = tctx->dd; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci ctx->op = op; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci return atmel_sha_handle_queue(dd, req); 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic int atmel_sha_update(struct ahash_request *req) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (!req->nbytes) 116662306a36Sopenharmony_ci return 0; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci ctx->total = req->nbytes; 116962306a36Sopenharmony_ci ctx->sg = req->src; 117062306a36Sopenharmony_ci ctx->offset = 0; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (ctx->flags & SHA_FLAGS_FINUP) { 117362306a36Sopenharmony_ci if (ctx->bufcnt + ctx->total < ATMEL_SHA_DMA_THRESHOLD) 117462306a36Sopenharmony_ci /* faster to use CPU for short transfers */ 117562306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_CPU; 117662306a36Sopenharmony_ci } else if (ctx->bufcnt + ctx->total < ctx->buflen) { 117762306a36Sopenharmony_ci atmel_sha_append_sg(ctx); 117862306a36Sopenharmony_ci return 0; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci return atmel_sha_enqueue(req, SHA_OP_UPDATE); 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic int atmel_sha_final(struct ahash_request *req) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_FINUP; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (ctx->flags & SHA_FLAGS_ERROR) 119062306a36Sopenharmony_ci return 0; /* uncompleted hash is not needed */ 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (ctx->flags & SHA_FLAGS_PAD) 119362306a36Sopenharmony_ci /* copy ready hash (+ finalize hmac) */ 119462306a36Sopenharmony_ci return atmel_sha_finish(req); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return atmel_sha_enqueue(req, SHA_OP_FINAL); 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_cistatic int atmel_sha_finup(struct ahash_request *req) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 120262306a36Sopenharmony_ci int err1, err2; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_FINUP; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci err1 = atmel_sha_update(req); 120762306a36Sopenharmony_ci if (err1 == -EINPROGRESS || 120862306a36Sopenharmony_ci (err1 == -EBUSY && (ahash_request_flags(req) & 120962306a36Sopenharmony_ci CRYPTO_TFM_REQ_MAY_BACKLOG))) 121062306a36Sopenharmony_ci return err1; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* 121362306a36Sopenharmony_ci * final() has to be always called to cleanup resources 121462306a36Sopenharmony_ci * even if udpate() failed, except EINPROGRESS 121562306a36Sopenharmony_ci */ 121662306a36Sopenharmony_ci err2 = atmel_sha_final(req); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci return err1 ?: err2; 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic int atmel_sha_digest(struct ahash_request *req) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci return atmel_sha_init(req) ?: atmel_sha_finup(req); 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic int atmel_sha_export(struct ahash_request *req, void *out) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci const struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci memcpy(out, ctx, sizeof(*ctx)); 123262306a36Sopenharmony_ci return 0; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic int atmel_sha_import(struct ahash_request *req, const void *in) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci memcpy(ctx, in, sizeof(*ctx)); 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic int atmel_sha_cra_init(struct crypto_tfm *tfm) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci struct atmel_sha_ctx *ctx = crypto_tfm_ctx(tfm); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), 124862306a36Sopenharmony_ci sizeof(struct atmel_sha_reqctx)); 124962306a36Sopenharmony_ci ctx->start = atmel_sha_start; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci return 0; 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cistatic void atmel_sha_alg_init(struct ahash_alg *alg) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci alg->halg.base.cra_priority = ATMEL_SHA_PRIORITY; 125762306a36Sopenharmony_ci alg->halg.base.cra_flags = CRYPTO_ALG_ASYNC; 125862306a36Sopenharmony_ci alg->halg.base.cra_ctxsize = sizeof(struct atmel_sha_ctx); 125962306a36Sopenharmony_ci alg->halg.base.cra_module = THIS_MODULE; 126062306a36Sopenharmony_ci alg->halg.base.cra_init = atmel_sha_cra_init; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci alg->halg.statesize = sizeof(struct atmel_sha_reqctx); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci alg->init = atmel_sha_init; 126562306a36Sopenharmony_ci alg->update = atmel_sha_update; 126662306a36Sopenharmony_ci alg->final = atmel_sha_final; 126762306a36Sopenharmony_ci alg->finup = atmel_sha_finup; 126862306a36Sopenharmony_ci alg->digest = atmel_sha_digest; 126962306a36Sopenharmony_ci alg->export = atmel_sha_export; 127062306a36Sopenharmony_ci alg->import = atmel_sha_import; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic struct ahash_alg sha_1_256_algs[] = { 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci .halg.base.cra_name = "sha1", 127662306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-sha1", 127762306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA1_BLOCK_SIZE, 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci .halg.digestsize = SHA1_DIGEST_SIZE, 128062306a36Sopenharmony_ci}, 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci .halg.base.cra_name = "sha256", 128362306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-sha256", 128462306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA256_BLOCK_SIZE, 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci .halg.digestsize = SHA256_DIGEST_SIZE, 128762306a36Sopenharmony_ci}, 128862306a36Sopenharmony_ci}; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic struct ahash_alg sha_224_alg = { 129162306a36Sopenharmony_ci .halg.base.cra_name = "sha224", 129262306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-sha224", 129362306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA224_BLOCK_SIZE, 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci .halg.digestsize = SHA224_DIGEST_SIZE, 129662306a36Sopenharmony_ci}; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic struct ahash_alg sha_384_512_algs[] = { 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci .halg.base.cra_name = "sha384", 130162306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-sha384", 130262306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA384_BLOCK_SIZE, 130362306a36Sopenharmony_ci .halg.base.cra_alignmask = 0x3, 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci .halg.digestsize = SHA384_DIGEST_SIZE, 130662306a36Sopenharmony_ci}, 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci .halg.base.cra_name = "sha512", 130962306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-sha512", 131062306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA512_BLOCK_SIZE, 131162306a36Sopenharmony_ci .halg.base.cra_alignmask = 0x3, 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci .halg.digestsize = SHA512_DIGEST_SIZE, 131462306a36Sopenharmony_ci}, 131562306a36Sopenharmony_ci}; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic void atmel_sha_queue_task(unsigned long data) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci struct atmel_sha_dev *dd = (struct atmel_sha_dev *)data; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci atmel_sha_handle_queue(dd, NULL); 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic int atmel_sha_done(struct atmel_sha_dev *dd) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci int err = 0; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (SHA_FLAGS_CPU & dd->flags) { 132962306a36Sopenharmony_ci if (SHA_FLAGS_OUTPUT_READY & dd->flags) { 133062306a36Sopenharmony_ci dd->flags &= ~SHA_FLAGS_OUTPUT_READY; 133162306a36Sopenharmony_ci goto finish; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci } else if (SHA_FLAGS_DMA_READY & dd->flags) { 133462306a36Sopenharmony_ci if (SHA_FLAGS_DMA_ACTIVE & dd->flags) { 133562306a36Sopenharmony_ci dd->flags &= ~SHA_FLAGS_DMA_ACTIVE; 133662306a36Sopenharmony_ci atmel_sha_update_dma_stop(dd); 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci if (SHA_FLAGS_OUTPUT_READY & dd->flags) { 133962306a36Sopenharmony_ci /* hash or semi-hash ready */ 134062306a36Sopenharmony_ci dd->flags &= ~(SHA_FLAGS_DMA_READY | 134162306a36Sopenharmony_ci SHA_FLAGS_OUTPUT_READY); 134262306a36Sopenharmony_ci err = atmel_sha_update_dma_start(dd); 134362306a36Sopenharmony_ci if (err != -EINPROGRESS) 134462306a36Sopenharmony_ci goto finish; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci return err; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_cifinish: 135062306a36Sopenharmony_ci /* finish curent request */ 135162306a36Sopenharmony_ci atmel_sha_finish_req(dd->req, err); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return err; 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic void atmel_sha_done_task(unsigned long data) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci struct atmel_sha_dev *dd = (struct atmel_sha_dev *)data; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci dd->is_async = true; 136162306a36Sopenharmony_ci (void)dd->resume(dd); 136262306a36Sopenharmony_ci} 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_cistatic irqreturn_t atmel_sha_irq(int irq, void *dev_id) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci struct atmel_sha_dev *sha_dd = dev_id; 136762306a36Sopenharmony_ci u32 reg; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci reg = atmel_sha_read(sha_dd, SHA_ISR); 137062306a36Sopenharmony_ci if (reg & atmel_sha_read(sha_dd, SHA_IMR)) { 137162306a36Sopenharmony_ci atmel_sha_write(sha_dd, SHA_IDR, reg); 137262306a36Sopenharmony_ci if (SHA_FLAGS_BUSY & sha_dd->flags) { 137362306a36Sopenharmony_ci sha_dd->flags |= SHA_FLAGS_OUTPUT_READY; 137462306a36Sopenharmony_ci if (!(SHA_FLAGS_CPU & sha_dd->flags)) 137562306a36Sopenharmony_ci sha_dd->flags |= SHA_FLAGS_DMA_READY; 137662306a36Sopenharmony_ci tasklet_schedule(&sha_dd->done_task); 137762306a36Sopenharmony_ci } else { 137862306a36Sopenharmony_ci dev_warn(sha_dd->dev, "SHA interrupt when no active requests.\n"); 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci return IRQ_HANDLED; 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci return IRQ_NONE; 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci/* DMA transfer functions */ 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_cistatic bool atmel_sha_dma_check_aligned(struct atmel_sha_dev *dd, 139062306a36Sopenharmony_ci struct scatterlist *sg, 139162306a36Sopenharmony_ci size_t len) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci struct atmel_sha_dma *dma = &dd->dma_lch_in; 139462306a36Sopenharmony_ci struct ahash_request *req = dd->req; 139562306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 139662306a36Sopenharmony_ci size_t bs = ctx->block_size; 139762306a36Sopenharmony_ci int nents; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci for (nents = 0; sg; sg = sg_next(sg), ++nents) { 140062306a36Sopenharmony_ci if (!IS_ALIGNED(sg->offset, sizeof(u32))) 140162306a36Sopenharmony_ci return false; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* 140462306a36Sopenharmony_ci * This is the last sg, the only one that is allowed to 140562306a36Sopenharmony_ci * have an unaligned length. 140662306a36Sopenharmony_ci */ 140762306a36Sopenharmony_ci if (len <= sg->length) { 140862306a36Sopenharmony_ci dma->nents = nents + 1; 140962306a36Sopenharmony_ci dma->last_sg_length = sg->length; 141062306a36Sopenharmony_ci sg->length = ALIGN(len, sizeof(u32)); 141162306a36Sopenharmony_ci return true; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* All other sg lengths MUST be aligned to the block size. */ 141562306a36Sopenharmony_ci if (!IS_ALIGNED(sg->length, bs)) 141662306a36Sopenharmony_ci return false; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci len -= sg->length; 141962306a36Sopenharmony_ci } 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci return false; 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic void atmel_sha_dma_callback2(void *data) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci struct atmel_sha_dev *dd = data; 142762306a36Sopenharmony_ci struct atmel_sha_dma *dma = &dd->dma_lch_in; 142862306a36Sopenharmony_ci struct scatterlist *sg; 142962306a36Sopenharmony_ci int nents; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci dma_unmap_sg(dd->dev, dma->sg, dma->nents, DMA_TO_DEVICE); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci sg = dma->sg; 143462306a36Sopenharmony_ci for (nents = 0; nents < dma->nents - 1; ++nents) 143562306a36Sopenharmony_ci sg = sg_next(sg); 143662306a36Sopenharmony_ci sg->length = dma->last_sg_length; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci dd->is_async = true; 143962306a36Sopenharmony_ci (void)atmel_sha_wait_for_data_ready(dd, dd->resume); 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic int atmel_sha_dma_start(struct atmel_sha_dev *dd, 144362306a36Sopenharmony_ci struct scatterlist *src, 144462306a36Sopenharmony_ci size_t len, 144562306a36Sopenharmony_ci atmel_sha_fn_t resume) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci struct atmel_sha_dma *dma = &dd->dma_lch_in; 144862306a36Sopenharmony_ci struct dma_slave_config *config = &dma->dma_conf; 144962306a36Sopenharmony_ci struct dma_chan *chan = dma->chan; 145062306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 145162306a36Sopenharmony_ci dma_cookie_t cookie; 145262306a36Sopenharmony_ci unsigned int sg_len; 145362306a36Sopenharmony_ci int err; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci dd->resume = resume; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci /* 145862306a36Sopenharmony_ci * dma->nents has already been initialized by 145962306a36Sopenharmony_ci * atmel_sha_dma_check_aligned(). 146062306a36Sopenharmony_ci */ 146162306a36Sopenharmony_ci dma->sg = src; 146262306a36Sopenharmony_ci sg_len = dma_map_sg(dd->dev, dma->sg, dma->nents, DMA_TO_DEVICE); 146362306a36Sopenharmony_ci if (!sg_len) { 146462306a36Sopenharmony_ci err = -ENOMEM; 146562306a36Sopenharmony_ci goto exit; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci config->src_maxburst = 16; 146962306a36Sopenharmony_ci config->dst_maxburst = 16; 147062306a36Sopenharmony_ci err = dmaengine_slave_config(chan, config); 147162306a36Sopenharmony_ci if (err) 147262306a36Sopenharmony_ci goto unmap_sg; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(chan, dma->sg, sg_len, DMA_MEM_TO_DEV, 147562306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 147662306a36Sopenharmony_ci if (!desc) { 147762306a36Sopenharmony_ci err = -ENOMEM; 147862306a36Sopenharmony_ci goto unmap_sg; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci desc->callback = atmel_sha_dma_callback2; 148262306a36Sopenharmony_ci desc->callback_param = dd; 148362306a36Sopenharmony_ci cookie = dmaengine_submit(desc); 148462306a36Sopenharmony_ci err = dma_submit_error(cookie); 148562306a36Sopenharmony_ci if (err) 148662306a36Sopenharmony_ci goto unmap_sg; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci dma_async_issue_pending(chan); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci return -EINPROGRESS; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ciunmap_sg: 149362306a36Sopenharmony_ci dma_unmap_sg(dd->dev, dma->sg, dma->nents, DMA_TO_DEVICE); 149462306a36Sopenharmony_ciexit: 149562306a36Sopenharmony_ci return atmel_sha_complete(dd, err); 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci/* CPU transfer functions */ 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_cistatic int atmel_sha_cpu_transfer(struct atmel_sha_dev *dd) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci struct ahash_request *req = dd->req; 150462306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 150562306a36Sopenharmony_ci const u32 *words = (const u32 *)ctx->buffer; 150662306a36Sopenharmony_ci size_t i, num_words; 150762306a36Sopenharmony_ci u32 isr, din, din_inc; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci din_inc = (ctx->flags & SHA_FLAGS_IDATAR0) ? 0 : 1; 151062306a36Sopenharmony_ci for (;;) { 151162306a36Sopenharmony_ci /* Write data into the Input Data Registers. */ 151262306a36Sopenharmony_ci num_words = DIV_ROUND_UP(ctx->bufcnt, sizeof(u32)); 151362306a36Sopenharmony_ci for (i = 0, din = 0; i < num_words; ++i, din += din_inc) 151462306a36Sopenharmony_ci atmel_sha_write(dd, SHA_REG_DIN(din), words[i]); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci ctx->offset += ctx->bufcnt; 151762306a36Sopenharmony_ci ctx->total -= ctx->bufcnt; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (!ctx->total) 152062306a36Sopenharmony_ci break; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* 152362306a36Sopenharmony_ci * Prepare next block: 152462306a36Sopenharmony_ci * Fill ctx->buffer now with the next data to be written into 152562306a36Sopenharmony_ci * IDATARx: it gives time for the SHA hardware to process 152662306a36Sopenharmony_ci * the current data so the SHA_INT_DATARDY flag might be set 152762306a36Sopenharmony_ci * in SHA_ISR when polling this register at the beginning of 152862306a36Sopenharmony_ci * the next loop. 152962306a36Sopenharmony_ci */ 153062306a36Sopenharmony_ci ctx->bufcnt = min_t(size_t, ctx->block_size, ctx->total); 153162306a36Sopenharmony_ci scatterwalk_map_and_copy(ctx->buffer, ctx->sg, 153262306a36Sopenharmony_ci ctx->offset, ctx->bufcnt, 0); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci /* Wait for hardware to be ready again. */ 153562306a36Sopenharmony_ci isr = atmel_sha_read(dd, SHA_ISR); 153662306a36Sopenharmony_ci if (!(isr & SHA_INT_DATARDY)) { 153762306a36Sopenharmony_ci /* Not ready yet. */ 153862306a36Sopenharmony_ci dd->resume = atmel_sha_cpu_transfer; 153962306a36Sopenharmony_ci atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY); 154062306a36Sopenharmony_ci return -EINPROGRESS; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci if (unlikely(!(ctx->flags & SHA_FLAGS_WAIT_DATARDY))) 154562306a36Sopenharmony_ci return dd->cpu_transfer_complete(dd); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci return atmel_sha_wait_for_data_ready(dd, dd->cpu_transfer_complete); 154862306a36Sopenharmony_ci} 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_cistatic int atmel_sha_cpu_start(struct atmel_sha_dev *dd, 155162306a36Sopenharmony_ci struct scatterlist *sg, 155262306a36Sopenharmony_ci unsigned int len, 155362306a36Sopenharmony_ci bool idatar0_only, 155462306a36Sopenharmony_ci bool wait_data_ready, 155562306a36Sopenharmony_ci atmel_sha_fn_t resume) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci struct ahash_request *req = dd->req; 155862306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci if (!len) 156162306a36Sopenharmony_ci return resume(dd); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci ctx->flags &= ~(SHA_FLAGS_IDATAR0 | SHA_FLAGS_WAIT_DATARDY); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (idatar0_only) 156662306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_IDATAR0; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (wait_data_ready) 156962306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_WAIT_DATARDY; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci ctx->sg = sg; 157262306a36Sopenharmony_ci ctx->total = len; 157362306a36Sopenharmony_ci ctx->offset = 0; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci /* Prepare the first block to be written. */ 157662306a36Sopenharmony_ci ctx->bufcnt = min_t(size_t, ctx->block_size, ctx->total); 157762306a36Sopenharmony_ci scatterwalk_map_and_copy(ctx->buffer, ctx->sg, 157862306a36Sopenharmony_ci ctx->offset, ctx->bufcnt, 0); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci dd->cpu_transfer_complete = resume; 158162306a36Sopenharmony_ci return atmel_sha_cpu_transfer(dd); 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cistatic int atmel_sha_cpu_hash(struct atmel_sha_dev *dd, 158562306a36Sopenharmony_ci const void *data, unsigned int datalen, 158662306a36Sopenharmony_ci bool auto_padding, 158762306a36Sopenharmony_ci atmel_sha_fn_t resume) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci struct ahash_request *req = dd->req; 159062306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 159162306a36Sopenharmony_ci u32 msglen = (auto_padding) ? datalen : 0; 159262306a36Sopenharmony_ci u32 mr = SHA_MR_MODE_AUTO; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (!(IS_ALIGNED(datalen, ctx->block_size) || auto_padding)) 159562306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci mr |= (ctx->flags & SHA_FLAGS_ALGO_MASK); 159862306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MR, mr); 159962306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MSR, msglen); 160062306a36Sopenharmony_ci atmel_sha_write(dd, SHA_BCR, msglen); 160162306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci sg_init_one(&dd->tmp, data, datalen); 160462306a36Sopenharmony_ci return atmel_sha_cpu_start(dd, &dd->tmp, datalen, false, true, resume); 160562306a36Sopenharmony_ci} 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci/* hmac functions */ 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_cistruct atmel_sha_hmac_key { 161162306a36Sopenharmony_ci bool valid; 161262306a36Sopenharmony_ci unsigned int keylen; 161362306a36Sopenharmony_ci u8 buffer[SHA512_BLOCK_SIZE]; 161462306a36Sopenharmony_ci u8 *keydup; 161562306a36Sopenharmony_ci}; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_cistatic inline void atmel_sha_hmac_key_init(struct atmel_sha_hmac_key *hkey) 161862306a36Sopenharmony_ci{ 161962306a36Sopenharmony_ci memset(hkey, 0, sizeof(*hkey)); 162062306a36Sopenharmony_ci} 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_cistatic inline void atmel_sha_hmac_key_release(struct atmel_sha_hmac_key *hkey) 162362306a36Sopenharmony_ci{ 162462306a36Sopenharmony_ci kfree(hkey->keydup); 162562306a36Sopenharmony_ci memset(hkey, 0, sizeof(*hkey)); 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_cistatic inline int atmel_sha_hmac_key_set(struct atmel_sha_hmac_key *hkey, 162962306a36Sopenharmony_ci const u8 *key, 163062306a36Sopenharmony_ci unsigned int keylen) 163162306a36Sopenharmony_ci{ 163262306a36Sopenharmony_ci atmel_sha_hmac_key_release(hkey); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci if (keylen > sizeof(hkey->buffer)) { 163562306a36Sopenharmony_ci hkey->keydup = kmemdup(key, keylen, GFP_KERNEL); 163662306a36Sopenharmony_ci if (!hkey->keydup) 163762306a36Sopenharmony_ci return -ENOMEM; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci } else { 164062306a36Sopenharmony_ci memcpy(hkey->buffer, key, keylen); 164162306a36Sopenharmony_ci } 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci hkey->valid = true; 164462306a36Sopenharmony_ci hkey->keylen = keylen; 164562306a36Sopenharmony_ci return 0; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic inline bool atmel_sha_hmac_key_get(const struct atmel_sha_hmac_key *hkey, 164962306a36Sopenharmony_ci const u8 **key, 165062306a36Sopenharmony_ci unsigned int *keylen) 165162306a36Sopenharmony_ci{ 165262306a36Sopenharmony_ci if (!hkey->valid) 165362306a36Sopenharmony_ci return false; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci *keylen = hkey->keylen; 165662306a36Sopenharmony_ci *key = (hkey->keydup) ? hkey->keydup : hkey->buffer; 165762306a36Sopenharmony_ci return true; 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_cistruct atmel_sha_hmac_ctx { 166262306a36Sopenharmony_ci struct atmel_sha_ctx base; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci struct atmel_sha_hmac_key hkey; 166562306a36Sopenharmony_ci u32 ipad[SHA512_BLOCK_SIZE / sizeof(u32)]; 166662306a36Sopenharmony_ci u32 opad[SHA512_BLOCK_SIZE / sizeof(u32)]; 166762306a36Sopenharmony_ci atmel_sha_fn_t resume; 166862306a36Sopenharmony_ci}; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_cistatic int atmel_sha_hmac_setup(struct atmel_sha_dev *dd, 167162306a36Sopenharmony_ci atmel_sha_fn_t resume); 167262306a36Sopenharmony_cistatic int atmel_sha_hmac_prehash_key(struct atmel_sha_dev *dd, 167362306a36Sopenharmony_ci const u8 *key, unsigned int keylen); 167462306a36Sopenharmony_cistatic int atmel_sha_hmac_prehash_key_done(struct atmel_sha_dev *dd); 167562306a36Sopenharmony_cistatic int atmel_sha_hmac_compute_ipad_hash(struct atmel_sha_dev *dd); 167662306a36Sopenharmony_cistatic int atmel_sha_hmac_compute_opad_hash(struct atmel_sha_dev *dd); 167762306a36Sopenharmony_cistatic int atmel_sha_hmac_setup_done(struct atmel_sha_dev *dd); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cistatic int atmel_sha_hmac_init_done(struct atmel_sha_dev *dd); 168062306a36Sopenharmony_cistatic int atmel_sha_hmac_final(struct atmel_sha_dev *dd); 168162306a36Sopenharmony_cistatic int atmel_sha_hmac_final_done(struct atmel_sha_dev *dd); 168262306a36Sopenharmony_cistatic int atmel_sha_hmac_digest2(struct atmel_sha_dev *dd); 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic int atmel_sha_hmac_setup(struct atmel_sha_dev *dd, 168562306a36Sopenharmony_ci atmel_sha_fn_t resume) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci struct ahash_request *req = dd->req; 168862306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 168962306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 169062306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 169162306a36Sopenharmony_ci unsigned int keylen; 169262306a36Sopenharmony_ci const u8 *key; 169362306a36Sopenharmony_ci size_t bs; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci hmac->resume = resume; 169662306a36Sopenharmony_ci switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { 169762306a36Sopenharmony_ci case SHA_FLAGS_SHA1: 169862306a36Sopenharmony_ci ctx->block_size = SHA1_BLOCK_SIZE; 169962306a36Sopenharmony_ci ctx->hash_size = SHA1_DIGEST_SIZE; 170062306a36Sopenharmony_ci break; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci case SHA_FLAGS_SHA224: 170362306a36Sopenharmony_ci ctx->block_size = SHA224_BLOCK_SIZE; 170462306a36Sopenharmony_ci ctx->hash_size = SHA256_DIGEST_SIZE; 170562306a36Sopenharmony_ci break; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci case SHA_FLAGS_SHA256: 170862306a36Sopenharmony_ci ctx->block_size = SHA256_BLOCK_SIZE; 170962306a36Sopenharmony_ci ctx->hash_size = SHA256_DIGEST_SIZE; 171062306a36Sopenharmony_ci break; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci case SHA_FLAGS_SHA384: 171362306a36Sopenharmony_ci ctx->block_size = SHA384_BLOCK_SIZE; 171462306a36Sopenharmony_ci ctx->hash_size = SHA512_DIGEST_SIZE; 171562306a36Sopenharmony_ci break; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci case SHA_FLAGS_SHA512: 171862306a36Sopenharmony_ci ctx->block_size = SHA512_BLOCK_SIZE; 171962306a36Sopenharmony_ci ctx->hash_size = SHA512_DIGEST_SIZE; 172062306a36Sopenharmony_ci break; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci default: 172362306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci bs = ctx->block_size; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci if (likely(!atmel_sha_hmac_key_get(&hmac->hkey, &key, &keylen))) 172862306a36Sopenharmony_ci return resume(dd); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci /* Compute K' from K. */ 173162306a36Sopenharmony_ci if (unlikely(keylen > bs)) 173262306a36Sopenharmony_ci return atmel_sha_hmac_prehash_key(dd, key, keylen); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci /* Prepare ipad. */ 173562306a36Sopenharmony_ci memcpy((u8 *)hmac->ipad, key, keylen); 173662306a36Sopenharmony_ci memset((u8 *)hmac->ipad + keylen, 0, bs - keylen); 173762306a36Sopenharmony_ci return atmel_sha_hmac_compute_ipad_hash(dd); 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_cistatic int atmel_sha_hmac_prehash_key(struct atmel_sha_dev *dd, 174162306a36Sopenharmony_ci const u8 *key, unsigned int keylen) 174262306a36Sopenharmony_ci{ 174362306a36Sopenharmony_ci return atmel_sha_cpu_hash(dd, key, keylen, true, 174462306a36Sopenharmony_ci atmel_sha_hmac_prehash_key_done); 174562306a36Sopenharmony_ci} 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_cistatic int atmel_sha_hmac_prehash_key_done(struct atmel_sha_dev *dd) 174862306a36Sopenharmony_ci{ 174962306a36Sopenharmony_ci struct ahash_request *req = dd->req; 175062306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 175162306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 175262306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 175362306a36Sopenharmony_ci size_t ds = crypto_ahash_digestsize(tfm); 175462306a36Sopenharmony_ci size_t bs = ctx->block_size; 175562306a36Sopenharmony_ci size_t i, num_words = ds / sizeof(u32); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* Prepare ipad. */ 175862306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 175962306a36Sopenharmony_ci hmac->ipad[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); 176062306a36Sopenharmony_ci memset((u8 *)hmac->ipad + ds, 0, bs - ds); 176162306a36Sopenharmony_ci return atmel_sha_hmac_compute_ipad_hash(dd); 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_cistatic int atmel_sha_hmac_compute_ipad_hash(struct atmel_sha_dev *dd) 176562306a36Sopenharmony_ci{ 176662306a36Sopenharmony_ci struct ahash_request *req = dd->req; 176762306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 176862306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 176962306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 177062306a36Sopenharmony_ci size_t bs = ctx->block_size; 177162306a36Sopenharmony_ci size_t i, num_words = bs / sizeof(u32); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci unsafe_memcpy(hmac->opad, hmac->ipad, bs, 177462306a36Sopenharmony_ci "fortified memcpy causes -Wrestrict warning"); 177562306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) { 177662306a36Sopenharmony_ci hmac->ipad[i] ^= 0x36363636; 177762306a36Sopenharmony_ci hmac->opad[i] ^= 0x5c5c5c5c; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci return atmel_sha_cpu_hash(dd, hmac->ipad, bs, false, 178162306a36Sopenharmony_ci atmel_sha_hmac_compute_opad_hash); 178262306a36Sopenharmony_ci} 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_cistatic int atmel_sha_hmac_compute_opad_hash(struct atmel_sha_dev *dd) 178562306a36Sopenharmony_ci{ 178662306a36Sopenharmony_ci struct ahash_request *req = dd->req; 178762306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 178862306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 178962306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 179062306a36Sopenharmony_ci size_t bs = ctx->block_size; 179162306a36Sopenharmony_ci size_t hs = ctx->hash_size; 179262306a36Sopenharmony_ci size_t i, num_words = hs / sizeof(u32); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 179562306a36Sopenharmony_ci hmac->ipad[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); 179662306a36Sopenharmony_ci return atmel_sha_cpu_hash(dd, hmac->opad, bs, false, 179762306a36Sopenharmony_ci atmel_sha_hmac_setup_done); 179862306a36Sopenharmony_ci} 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_cistatic int atmel_sha_hmac_setup_done(struct atmel_sha_dev *dd) 180162306a36Sopenharmony_ci{ 180262306a36Sopenharmony_ci struct ahash_request *req = dd->req; 180362306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 180462306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 180562306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 180662306a36Sopenharmony_ci size_t hs = ctx->hash_size; 180762306a36Sopenharmony_ci size_t i, num_words = hs / sizeof(u32); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 181062306a36Sopenharmony_ci hmac->opad[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); 181162306a36Sopenharmony_ci atmel_sha_hmac_key_release(&hmac->hkey); 181262306a36Sopenharmony_ci return hmac->resume(dd); 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_cistatic int atmel_sha_hmac_start(struct atmel_sha_dev *dd) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci struct ahash_request *req = dd->req; 181862306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 181962306a36Sopenharmony_ci int err; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci err = atmel_sha_hw_init(dd); 182262306a36Sopenharmony_ci if (err) 182362306a36Sopenharmony_ci return atmel_sha_complete(dd, err); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci switch (ctx->op) { 182662306a36Sopenharmony_ci case SHA_OP_INIT: 182762306a36Sopenharmony_ci err = atmel_sha_hmac_setup(dd, atmel_sha_hmac_init_done); 182862306a36Sopenharmony_ci break; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci case SHA_OP_UPDATE: 183162306a36Sopenharmony_ci dd->resume = atmel_sha_done; 183262306a36Sopenharmony_ci err = atmel_sha_update_req(dd); 183362306a36Sopenharmony_ci break; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci case SHA_OP_FINAL: 183662306a36Sopenharmony_ci dd->resume = atmel_sha_hmac_final; 183762306a36Sopenharmony_ci err = atmel_sha_final_req(dd); 183862306a36Sopenharmony_ci break; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci case SHA_OP_DIGEST: 184162306a36Sopenharmony_ci err = atmel_sha_hmac_setup(dd, atmel_sha_hmac_digest2); 184262306a36Sopenharmony_ci break; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci default: 184562306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 184662306a36Sopenharmony_ci } 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci return err; 184962306a36Sopenharmony_ci} 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_cistatic int atmel_sha_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, 185262306a36Sopenharmony_ci unsigned int keylen) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci return atmel_sha_hmac_key_set(&hmac->hkey, key, keylen); 185762306a36Sopenharmony_ci} 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_cistatic int atmel_sha_hmac_init(struct ahash_request *req) 186062306a36Sopenharmony_ci{ 186162306a36Sopenharmony_ci int err; 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci err = atmel_sha_init(req); 186462306a36Sopenharmony_ci if (err) 186562306a36Sopenharmony_ci return err; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci return atmel_sha_enqueue(req, SHA_OP_INIT); 186862306a36Sopenharmony_ci} 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_cistatic int atmel_sha_hmac_init_done(struct atmel_sha_dev *dd) 187162306a36Sopenharmony_ci{ 187262306a36Sopenharmony_ci struct ahash_request *req = dd->req; 187362306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 187462306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 187562306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 187662306a36Sopenharmony_ci size_t bs = ctx->block_size; 187762306a36Sopenharmony_ci size_t hs = ctx->hash_size; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci ctx->bufcnt = 0; 188062306a36Sopenharmony_ci ctx->digcnt[0] = bs; 188162306a36Sopenharmony_ci ctx->digcnt[1] = 0; 188262306a36Sopenharmony_ci ctx->flags |= SHA_FLAGS_RESTORE; 188362306a36Sopenharmony_ci memcpy(ctx->digest, hmac->ipad, hs); 188462306a36Sopenharmony_ci return atmel_sha_complete(dd, 0); 188562306a36Sopenharmony_ci} 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_cistatic int atmel_sha_hmac_final(struct atmel_sha_dev *dd) 188862306a36Sopenharmony_ci{ 188962306a36Sopenharmony_ci struct ahash_request *req = dd->req; 189062306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 189162306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 189262306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 189362306a36Sopenharmony_ci u32 *digest = (u32 *)ctx->digest; 189462306a36Sopenharmony_ci size_t ds = crypto_ahash_digestsize(tfm); 189562306a36Sopenharmony_ci size_t bs = ctx->block_size; 189662306a36Sopenharmony_ci size_t hs = ctx->hash_size; 189762306a36Sopenharmony_ci size_t i, num_words; 189862306a36Sopenharmony_ci u32 mr; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci /* Save d = SHA((K' + ipad) | msg). */ 190162306a36Sopenharmony_ci num_words = ds / sizeof(u32); 190262306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 190362306a36Sopenharmony_ci digest[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci /* Restore context to finish computing SHA((K' + opad) | d). */ 190662306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_WUIHV); 190762306a36Sopenharmony_ci num_words = hs / sizeof(u32); 190862306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 190962306a36Sopenharmony_ci atmel_sha_write(dd, SHA_REG_DIN(i), hmac->opad[i]); 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci mr = SHA_MR_MODE_AUTO | SHA_MR_UIHV; 191262306a36Sopenharmony_ci mr |= (ctx->flags & SHA_FLAGS_ALGO_MASK); 191362306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MR, mr); 191462306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MSR, bs + ds); 191562306a36Sopenharmony_ci atmel_sha_write(dd, SHA_BCR, ds); 191662306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci sg_init_one(&dd->tmp, digest, ds); 191962306a36Sopenharmony_ci return atmel_sha_cpu_start(dd, &dd->tmp, ds, false, true, 192062306a36Sopenharmony_ci atmel_sha_hmac_final_done); 192162306a36Sopenharmony_ci} 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_cistatic int atmel_sha_hmac_final_done(struct atmel_sha_dev *dd) 192462306a36Sopenharmony_ci{ 192562306a36Sopenharmony_ci /* 192662306a36Sopenharmony_ci * req->result might not be sizeof(u32) aligned, so copy the 192762306a36Sopenharmony_ci * digest into ctx->digest[] before memcpy() the data into 192862306a36Sopenharmony_ci * req->result. 192962306a36Sopenharmony_ci */ 193062306a36Sopenharmony_ci atmel_sha_copy_hash(dd->req); 193162306a36Sopenharmony_ci atmel_sha_copy_ready_hash(dd->req); 193262306a36Sopenharmony_ci return atmel_sha_complete(dd, 0); 193362306a36Sopenharmony_ci} 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_cistatic int atmel_sha_hmac_digest(struct ahash_request *req) 193662306a36Sopenharmony_ci{ 193762306a36Sopenharmony_ci int err; 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci err = atmel_sha_init(req); 194062306a36Sopenharmony_ci if (err) 194162306a36Sopenharmony_ci return err; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci return atmel_sha_enqueue(req, SHA_OP_DIGEST); 194462306a36Sopenharmony_ci} 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_cistatic int atmel_sha_hmac_digest2(struct atmel_sha_dev *dd) 194762306a36Sopenharmony_ci{ 194862306a36Sopenharmony_ci struct ahash_request *req = dd->req; 194962306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); 195062306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 195162306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 195262306a36Sopenharmony_ci struct scatterlist *sgbuf; 195362306a36Sopenharmony_ci size_t hs = ctx->hash_size; 195462306a36Sopenharmony_ci size_t i, num_words = hs / sizeof(u32); 195562306a36Sopenharmony_ci bool use_dma = false; 195662306a36Sopenharmony_ci u32 mr; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci /* Special case for empty message. */ 195962306a36Sopenharmony_ci if (!req->nbytes) { 196062306a36Sopenharmony_ci req->nbytes = 0; 196162306a36Sopenharmony_ci ctx->bufcnt = 0; 196262306a36Sopenharmony_ci ctx->digcnt[0] = 0; 196362306a36Sopenharmony_ci ctx->digcnt[1] = 0; 196462306a36Sopenharmony_ci switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { 196562306a36Sopenharmony_ci case SHA_FLAGS_SHA1: 196662306a36Sopenharmony_ci case SHA_FLAGS_SHA224: 196762306a36Sopenharmony_ci case SHA_FLAGS_SHA256: 196862306a36Sopenharmony_ci atmel_sha_fill_padding(ctx, 64); 196962306a36Sopenharmony_ci break; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci case SHA_FLAGS_SHA384: 197262306a36Sopenharmony_ci case SHA_FLAGS_SHA512: 197362306a36Sopenharmony_ci atmel_sha_fill_padding(ctx, 128); 197462306a36Sopenharmony_ci break; 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci sg_init_one(&dd->tmp, ctx->buffer, ctx->bufcnt); 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci /* Check DMA threshold and alignment. */ 198062306a36Sopenharmony_ci if (req->nbytes > ATMEL_SHA_DMA_THRESHOLD && 198162306a36Sopenharmony_ci atmel_sha_dma_check_aligned(dd, req->src, req->nbytes)) 198262306a36Sopenharmony_ci use_dma = true; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci /* Write both initial hash values to compute a HMAC. */ 198562306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_WUIHV); 198662306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 198762306a36Sopenharmony_ci atmel_sha_write(dd, SHA_REG_DIN(i), hmac->ipad[i]); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_WUIEHV); 199062306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 199162306a36Sopenharmony_ci atmel_sha_write(dd, SHA_REG_DIN(i), hmac->opad[i]); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci /* Write the Mode, Message Size, Bytes Count then Control Registers. */ 199462306a36Sopenharmony_ci mr = (SHA_MR_HMAC | SHA_MR_DUALBUFF); 199562306a36Sopenharmony_ci mr |= ctx->flags & SHA_FLAGS_ALGO_MASK; 199662306a36Sopenharmony_ci if (use_dma) 199762306a36Sopenharmony_ci mr |= SHA_MR_MODE_IDATAR0; 199862306a36Sopenharmony_ci else 199962306a36Sopenharmony_ci mr |= SHA_MR_MODE_AUTO; 200062306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MR, mr); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MSR, req->nbytes); 200362306a36Sopenharmony_ci atmel_sha_write(dd, SHA_BCR, req->nbytes); 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci /* Special case for empty message. */ 200862306a36Sopenharmony_ci if (!req->nbytes) { 200962306a36Sopenharmony_ci sgbuf = &dd->tmp; 201062306a36Sopenharmony_ci req->nbytes = ctx->bufcnt; 201162306a36Sopenharmony_ci } else { 201262306a36Sopenharmony_ci sgbuf = req->src; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci /* Process data. */ 201662306a36Sopenharmony_ci if (use_dma) 201762306a36Sopenharmony_ci return atmel_sha_dma_start(dd, sgbuf, req->nbytes, 201862306a36Sopenharmony_ci atmel_sha_hmac_final_done); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci return atmel_sha_cpu_start(dd, sgbuf, req->nbytes, false, true, 202162306a36Sopenharmony_ci atmel_sha_hmac_final_done); 202262306a36Sopenharmony_ci} 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_cistatic int atmel_sha_hmac_cra_init(struct crypto_tfm *tfm) 202562306a36Sopenharmony_ci{ 202662306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_tfm_ctx(tfm); 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), 202962306a36Sopenharmony_ci sizeof(struct atmel_sha_reqctx)); 203062306a36Sopenharmony_ci hmac->base.start = atmel_sha_hmac_start; 203162306a36Sopenharmony_ci atmel_sha_hmac_key_init(&hmac->hkey); 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci return 0; 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_cistatic void atmel_sha_hmac_cra_exit(struct crypto_tfm *tfm) 203762306a36Sopenharmony_ci{ 203862306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_tfm_ctx(tfm); 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci atmel_sha_hmac_key_release(&hmac->hkey); 204162306a36Sopenharmony_ci} 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_cistatic void atmel_sha_hmac_alg_init(struct ahash_alg *alg) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci alg->halg.base.cra_priority = ATMEL_SHA_PRIORITY; 204662306a36Sopenharmony_ci alg->halg.base.cra_flags = CRYPTO_ALG_ASYNC; 204762306a36Sopenharmony_ci alg->halg.base.cra_ctxsize = sizeof(struct atmel_sha_hmac_ctx); 204862306a36Sopenharmony_ci alg->halg.base.cra_module = THIS_MODULE; 204962306a36Sopenharmony_ci alg->halg.base.cra_init = atmel_sha_hmac_cra_init; 205062306a36Sopenharmony_ci alg->halg.base.cra_exit = atmel_sha_hmac_cra_exit; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci alg->halg.statesize = sizeof(struct atmel_sha_reqctx); 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci alg->init = atmel_sha_hmac_init; 205562306a36Sopenharmony_ci alg->update = atmel_sha_update; 205662306a36Sopenharmony_ci alg->final = atmel_sha_final; 205762306a36Sopenharmony_ci alg->digest = atmel_sha_hmac_digest; 205862306a36Sopenharmony_ci alg->setkey = atmel_sha_hmac_setkey; 205962306a36Sopenharmony_ci alg->export = atmel_sha_export; 206062306a36Sopenharmony_ci alg->import = atmel_sha_import; 206162306a36Sopenharmony_ci} 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_cistatic struct ahash_alg sha_hmac_algs[] = { 206462306a36Sopenharmony_ci{ 206562306a36Sopenharmony_ci .halg.base.cra_name = "hmac(sha1)", 206662306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-hmac-sha1", 206762306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA1_BLOCK_SIZE, 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci .halg.digestsize = SHA1_DIGEST_SIZE, 207062306a36Sopenharmony_ci}, 207162306a36Sopenharmony_ci{ 207262306a36Sopenharmony_ci .halg.base.cra_name = "hmac(sha224)", 207362306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-hmac-sha224", 207462306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA224_BLOCK_SIZE, 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci .halg.digestsize = SHA224_DIGEST_SIZE, 207762306a36Sopenharmony_ci}, 207862306a36Sopenharmony_ci{ 207962306a36Sopenharmony_ci .halg.base.cra_name = "hmac(sha256)", 208062306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-hmac-sha256", 208162306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA256_BLOCK_SIZE, 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci .halg.digestsize = SHA256_DIGEST_SIZE, 208462306a36Sopenharmony_ci}, 208562306a36Sopenharmony_ci{ 208662306a36Sopenharmony_ci .halg.base.cra_name = "hmac(sha384)", 208762306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-hmac-sha384", 208862306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA384_BLOCK_SIZE, 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci .halg.digestsize = SHA384_DIGEST_SIZE, 209162306a36Sopenharmony_ci}, 209262306a36Sopenharmony_ci{ 209362306a36Sopenharmony_ci .halg.base.cra_name = "hmac(sha512)", 209462306a36Sopenharmony_ci .halg.base.cra_driver_name = "atmel-hmac-sha512", 209562306a36Sopenharmony_ci .halg.base.cra_blocksize = SHA512_BLOCK_SIZE, 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci .halg.digestsize = SHA512_DIGEST_SIZE, 209862306a36Sopenharmony_ci}, 209962306a36Sopenharmony_ci}; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CRYPTO_DEV_ATMEL_AUTHENC) 210262306a36Sopenharmony_ci/* authenc functions */ 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_cistatic int atmel_sha_authenc_init2(struct atmel_sha_dev *dd); 210562306a36Sopenharmony_cistatic int atmel_sha_authenc_init_done(struct atmel_sha_dev *dd); 210662306a36Sopenharmony_cistatic int atmel_sha_authenc_final_done(struct atmel_sha_dev *dd); 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_cistruct atmel_sha_authenc_ctx { 211062306a36Sopenharmony_ci struct crypto_ahash *tfm; 211162306a36Sopenharmony_ci}; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_cistruct atmel_sha_authenc_reqctx { 211462306a36Sopenharmony_ci struct atmel_sha_reqctx base; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci atmel_aes_authenc_fn_t cb; 211762306a36Sopenharmony_ci struct atmel_aes_dev *aes_dev; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci /* _init() parameters. */ 212062306a36Sopenharmony_ci struct scatterlist *assoc; 212162306a36Sopenharmony_ci u32 assoclen; 212262306a36Sopenharmony_ci u32 textlen; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci /* _final() parameters. */ 212562306a36Sopenharmony_ci u32 *digest; 212662306a36Sopenharmony_ci unsigned int digestlen; 212762306a36Sopenharmony_ci}; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_cistatic void atmel_sha_authenc_complete(void *data, int err) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci struct ahash_request *req = data; 213262306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci authctx->cb(authctx->aes_dev, err, authctx->base.dd->is_async); 213562306a36Sopenharmony_ci} 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_cistatic int atmel_sha_authenc_start(struct atmel_sha_dev *dd) 213862306a36Sopenharmony_ci{ 213962306a36Sopenharmony_ci struct ahash_request *req = dd->req; 214062306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 214162306a36Sopenharmony_ci int err; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci /* 214462306a36Sopenharmony_ci * Force atmel_sha_complete() to call req->base.complete(), ie 214562306a36Sopenharmony_ci * atmel_sha_authenc_complete(), which in turn calls authctx->cb(). 214662306a36Sopenharmony_ci */ 214762306a36Sopenharmony_ci dd->force_complete = true; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci err = atmel_sha_hw_init(dd); 215062306a36Sopenharmony_ci return authctx->cb(authctx->aes_dev, err, dd->is_async); 215162306a36Sopenharmony_ci} 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_cibool atmel_sha_authenc_is_ready(void) 215462306a36Sopenharmony_ci{ 215562306a36Sopenharmony_ci struct atmel_sha_ctx dummy; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci dummy.dd = NULL; 215862306a36Sopenharmony_ci return (atmel_sha_find_dev(&dummy) != NULL); 215962306a36Sopenharmony_ci} 216062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_is_ready); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ciunsigned int atmel_sha_authenc_get_reqsize(void) 216362306a36Sopenharmony_ci{ 216462306a36Sopenharmony_ci return sizeof(struct atmel_sha_authenc_reqctx); 216562306a36Sopenharmony_ci} 216662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_get_reqsize); 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_cistruct atmel_sha_authenc_ctx *atmel_sha_authenc_spawn(unsigned long mode) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci struct atmel_sha_authenc_ctx *auth; 217162306a36Sopenharmony_ci struct crypto_ahash *tfm; 217262306a36Sopenharmony_ci struct atmel_sha_ctx *tctx; 217362306a36Sopenharmony_ci const char *name; 217462306a36Sopenharmony_ci int err = -EINVAL; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci switch (mode & SHA_FLAGS_MODE_MASK) { 217762306a36Sopenharmony_ci case SHA_FLAGS_HMAC_SHA1: 217862306a36Sopenharmony_ci name = "atmel-hmac-sha1"; 217962306a36Sopenharmony_ci break; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci case SHA_FLAGS_HMAC_SHA224: 218262306a36Sopenharmony_ci name = "atmel-hmac-sha224"; 218362306a36Sopenharmony_ci break; 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci case SHA_FLAGS_HMAC_SHA256: 218662306a36Sopenharmony_ci name = "atmel-hmac-sha256"; 218762306a36Sopenharmony_ci break; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci case SHA_FLAGS_HMAC_SHA384: 219062306a36Sopenharmony_ci name = "atmel-hmac-sha384"; 219162306a36Sopenharmony_ci break; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci case SHA_FLAGS_HMAC_SHA512: 219462306a36Sopenharmony_ci name = "atmel-hmac-sha512"; 219562306a36Sopenharmony_ci break; 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci default: 219862306a36Sopenharmony_ci goto error; 219962306a36Sopenharmony_ci } 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci tfm = crypto_alloc_ahash(name, 0, 0); 220262306a36Sopenharmony_ci if (IS_ERR(tfm)) { 220362306a36Sopenharmony_ci err = PTR_ERR(tfm); 220462306a36Sopenharmony_ci goto error; 220562306a36Sopenharmony_ci } 220662306a36Sopenharmony_ci tctx = crypto_ahash_ctx(tfm); 220762306a36Sopenharmony_ci tctx->start = atmel_sha_authenc_start; 220862306a36Sopenharmony_ci tctx->flags = mode; 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci auth = kzalloc(sizeof(*auth), GFP_KERNEL); 221162306a36Sopenharmony_ci if (!auth) { 221262306a36Sopenharmony_ci err = -ENOMEM; 221362306a36Sopenharmony_ci goto err_free_ahash; 221462306a36Sopenharmony_ci } 221562306a36Sopenharmony_ci auth->tfm = tfm; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci return auth; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_cierr_free_ahash: 222062306a36Sopenharmony_ci crypto_free_ahash(tfm); 222162306a36Sopenharmony_cierror: 222262306a36Sopenharmony_ci return ERR_PTR(err); 222362306a36Sopenharmony_ci} 222462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_spawn); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_civoid atmel_sha_authenc_free(struct atmel_sha_authenc_ctx *auth) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci if (auth) 222962306a36Sopenharmony_ci crypto_free_ahash(auth->tfm); 223062306a36Sopenharmony_ci kfree(auth); 223162306a36Sopenharmony_ci} 223262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_free); 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ciint atmel_sha_authenc_setkey(struct atmel_sha_authenc_ctx *auth, 223562306a36Sopenharmony_ci const u8 *key, unsigned int keylen, u32 flags) 223662306a36Sopenharmony_ci{ 223762306a36Sopenharmony_ci struct crypto_ahash *tfm = auth->tfm; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci crypto_ahash_clear_flags(tfm, CRYPTO_TFM_REQ_MASK); 224062306a36Sopenharmony_ci crypto_ahash_set_flags(tfm, flags & CRYPTO_TFM_REQ_MASK); 224162306a36Sopenharmony_ci return crypto_ahash_setkey(tfm, key, keylen); 224262306a36Sopenharmony_ci} 224362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_setkey); 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ciint atmel_sha_authenc_schedule(struct ahash_request *req, 224662306a36Sopenharmony_ci struct atmel_sha_authenc_ctx *auth, 224762306a36Sopenharmony_ci atmel_aes_authenc_fn_t cb, 224862306a36Sopenharmony_ci struct atmel_aes_dev *aes_dev) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 225162306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = &authctx->base; 225262306a36Sopenharmony_ci struct crypto_ahash *tfm = auth->tfm; 225362306a36Sopenharmony_ci struct atmel_sha_ctx *tctx = crypto_ahash_ctx(tfm); 225462306a36Sopenharmony_ci struct atmel_sha_dev *dd; 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci /* Reset request context (MUST be done first). */ 225762306a36Sopenharmony_ci memset(authctx, 0, sizeof(*authctx)); 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci /* Get SHA device. */ 226062306a36Sopenharmony_ci dd = atmel_sha_find_dev(tctx); 226162306a36Sopenharmony_ci if (!dd) 226262306a36Sopenharmony_ci return cb(aes_dev, -ENODEV, false); 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci /* Init request context. */ 226562306a36Sopenharmony_ci ctx->dd = dd; 226662306a36Sopenharmony_ci ctx->buflen = SHA_BUFFER_LEN; 226762306a36Sopenharmony_ci authctx->cb = cb; 226862306a36Sopenharmony_ci authctx->aes_dev = aes_dev; 226962306a36Sopenharmony_ci ahash_request_set_tfm(req, tfm); 227062306a36Sopenharmony_ci ahash_request_set_callback(req, 0, atmel_sha_authenc_complete, req); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci return atmel_sha_handle_queue(dd, req); 227362306a36Sopenharmony_ci} 227462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_schedule); 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ciint atmel_sha_authenc_init(struct ahash_request *req, 227762306a36Sopenharmony_ci struct scatterlist *assoc, unsigned int assoclen, 227862306a36Sopenharmony_ci unsigned int textlen, 227962306a36Sopenharmony_ci atmel_aes_authenc_fn_t cb, 228062306a36Sopenharmony_ci struct atmel_aes_dev *aes_dev) 228162306a36Sopenharmony_ci{ 228262306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 228362306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = &authctx->base; 228462306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 228562306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 228662306a36Sopenharmony_ci struct atmel_sha_dev *dd = ctx->dd; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci if (unlikely(!IS_ALIGNED(assoclen, sizeof(u32)))) 228962306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci authctx->cb = cb; 229262306a36Sopenharmony_ci authctx->aes_dev = aes_dev; 229362306a36Sopenharmony_ci authctx->assoc = assoc; 229462306a36Sopenharmony_ci authctx->assoclen = assoclen; 229562306a36Sopenharmony_ci authctx->textlen = textlen; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci ctx->flags = hmac->base.flags; 229862306a36Sopenharmony_ci return atmel_sha_hmac_setup(dd, atmel_sha_authenc_init2); 229962306a36Sopenharmony_ci} 230062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_init); 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_cistatic int atmel_sha_authenc_init2(struct atmel_sha_dev *dd) 230362306a36Sopenharmony_ci{ 230462306a36Sopenharmony_ci struct ahash_request *req = dd->req; 230562306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 230662306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = &authctx->base; 230762306a36Sopenharmony_ci struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); 230862306a36Sopenharmony_ci struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); 230962306a36Sopenharmony_ci size_t hs = ctx->hash_size; 231062306a36Sopenharmony_ci size_t i, num_words = hs / sizeof(u32); 231162306a36Sopenharmony_ci u32 mr, msg_size; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_WUIHV); 231462306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 231562306a36Sopenharmony_ci atmel_sha_write(dd, SHA_REG_DIN(i), hmac->ipad[i]); 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_WUIEHV); 231862306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 231962306a36Sopenharmony_ci atmel_sha_write(dd, SHA_REG_DIN(i), hmac->opad[i]); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci mr = (SHA_MR_MODE_IDATAR0 | 232262306a36Sopenharmony_ci SHA_MR_HMAC | 232362306a36Sopenharmony_ci SHA_MR_DUALBUFF); 232462306a36Sopenharmony_ci mr |= ctx->flags & SHA_FLAGS_ALGO_MASK; 232562306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MR, mr); 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci msg_size = authctx->assoclen + authctx->textlen; 232862306a36Sopenharmony_ci atmel_sha_write(dd, SHA_MSR, msg_size); 232962306a36Sopenharmony_ci atmel_sha_write(dd, SHA_BCR, msg_size); 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci /* Process assoc data. */ 233462306a36Sopenharmony_ci return atmel_sha_cpu_start(dd, authctx->assoc, authctx->assoclen, 233562306a36Sopenharmony_ci true, false, 233662306a36Sopenharmony_ci atmel_sha_authenc_init_done); 233762306a36Sopenharmony_ci} 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_cistatic int atmel_sha_authenc_init_done(struct atmel_sha_dev *dd) 234062306a36Sopenharmony_ci{ 234162306a36Sopenharmony_ci struct ahash_request *req = dd->req; 234262306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci return authctx->cb(authctx->aes_dev, 0, dd->is_async); 234562306a36Sopenharmony_ci} 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ciint atmel_sha_authenc_final(struct ahash_request *req, 234862306a36Sopenharmony_ci u32 *digest, unsigned int digestlen, 234962306a36Sopenharmony_ci atmel_aes_authenc_fn_t cb, 235062306a36Sopenharmony_ci struct atmel_aes_dev *aes_dev) 235162306a36Sopenharmony_ci{ 235262306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 235362306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = &authctx->base; 235462306a36Sopenharmony_ci struct atmel_sha_dev *dd = ctx->dd; 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { 235762306a36Sopenharmony_ci case SHA_FLAGS_SHA1: 235862306a36Sopenharmony_ci authctx->digestlen = SHA1_DIGEST_SIZE; 235962306a36Sopenharmony_ci break; 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci case SHA_FLAGS_SHA224: 236262306a36Sopenharmony_ci authctx->digestlen = SHA224_DIGEST_SIZE; 236362306a36Sopenharmony_ci break; 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci case SHA_FLAGS_SHA256: 236662306a36Sopenharmony_ci authctx->digestlen = SHA256_DIGEST_SIZE; 236762306a36Sopenharmony_ci break; 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci case SHA_FLAGS_SHA384: 237062306a36Sopenharmony_ci authctx->digestlen = SHA384_DIGEST_SIZE; 237162306a36Sopenharmony_ci break; 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci case SHA_FLAGS_SHA512: 237462306a36Sopenharmony_ci authctx->digestlen = SHA512_DIGEST_SIZE; 237562306a36Sopenharmony_ci break; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci default: 237862306a36Sopenharmony_ci return atmel_sha_complete(dd, -EINVAL); 237962306a36Sopenharmony_ci } 238062306a36Sopenharmony_ci if (authctx->digestlen > digestlen) 238162306a36Sopenharmony_ci authctx->digestlen = digestlen; 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci authctx->cb = cb; 238462306a36Sopenharmony_ci authctx->aes_dev = aes_dev; 238562306a36Sopenharmony_ci authctx->digest = digest; 238662306a36Sopenharmony_ci return atmel_sha_wait_for_data_ready(dd, 238762306a36Sopenharmony_ci atmel_sha_authenc_final_done); 238862306a36Sopenharmony_ci} 238962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_final); 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_cistatic int atmel_sha_authenc_final_done(struct atmel_sha_dev *dd) 239262306a36Sopenharmony_ci{ 239362306a36Sopenharmony_ci struct ahash_request *req = dd->req; 239462306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 239562306a36Sopenharmony_ci size_t i, num_words = authctx->digestlen / sizeof(u32); 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci for (i = 0; i < num_words; ++i) 239862306a36Sopenharmony_ci authctx->digest[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci return atmel_sha_complete(dd, 0); 240162306a36Sopenharmony_ci} 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_civoid atmel_sha_authenc_abort(struct ahash_request *req) 240462306a36Sopenharmony_ci{ 240562306a36Sopenharmony_ci struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); 240662306a36Sopenharmony_ci struct atmel_sha_reqctx *ctx = &authctx->base; 240762306a36Sopenharmony_ci struct atmel_sha_dev *dd = ctx->dd; 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci /* Prevent atmel_sha_complete() from calling req->base.complete(). */ 241062306a36Sopenharmony_ci dd->is_async = false; 241162306a36Sopenharmony_ci dd->force_complete = false; 241262306a36Sopenharmony_ci (void)atmel_sha_complete(dd, 0); 241362306a36Sopenharmony_ci} 241462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_sha_authenc_abort); 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci#endif /* CONFIG_CRYPTO_DEV_ATMEL_AUTHENC */ 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_cistatic void atmel_sha_unregister_algs(struct atmel_sha_dev *dd) 242062306a36Sopenharmony_ci{ 242162306a36Sopenharmony_ci int i; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci if (dd->caps.has_hmac) 242462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sha_hmac_algs); i++) 242562306a36Sopenharmony_ci crypto_unregister_ahash(&sha_hmac_algs[i]); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sha_1_256_algs); i++) 242862306a36Sopenharmony_ci crypto_unregister_ahash(&sha_1_256_algs[i]); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci if (dd->caps.has_sha224) 243162306a36Sopenharmony_ci crypto_unregister_ahash(&sha_224_alg); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci if (dd->caps.has_sha_384_512) { 243462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sha_384_512_algs); i++) 243562306a36Sopenharmony_ci crypto_unregister_ahash(&sha_384_512_algs[i]); 243662306a36Sopenharmony_ci } 243762306a36Sopenharmony_ci} 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_cistatic int atmel_sha_register_algs(struct atmel_sha_dev *dd) 244062306a36Sopenharmony_ci{ 244162306a36Sopenharmony_ci int err, i, j; 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sha_1_256_algs); i++) { 244462306a36Sopenharmony_ci atmel_sha_alg_init(&sha_1_256_algs[i]); 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci err = crypto_register_ahash(&sha_1_256_algs[i]); 244762306a36Sopenharmony_ci if (err) 244862306a36Sopenharmony_ci goto err_sha_1_256_algs; 244962306a36Sopenharmony_ci } 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci if (dd->caps.has_sha224) { 245262306a36Sopenharmony_ci atmel_sha_alg_init(&sha_224_alg); 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci err = crypto_register_ahash(&sha_224_alg); 245562306a36Sopenharmony_ci if (err) 245662306a36Sopenharmony_ci goto err_sha_224_algs; 245762306a36Sopenharmony_ci } 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci if (dd->caps.has_sha_384_512) { 246062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sha_384_512_algs); i++) { 246162306a36Sopenharmony_ci atmel_sha_alg_init(&sha_384_512_algs[i]); 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci err = crypto_register_ahash(&sha_384_512_algs[i]); 246462306a36Sopenharmony_ci if (err) 246562306a36Sopenharmony_ci goto err_sha_384_512_algs; 246662306a36Sopenharmony_ci } 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci if (dd->caps.has_hmac) { 247062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sha_hmac_algs); i++) { 247162306a36Sopenharmony_ci atmel_sha_hmac_alg_init(&sha_hmac_algs[i]); 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci err = crypto_register_ahash(&sha_hmac_algs[i]); 247462306a36Sopenharmony_ci if (err) 247562306a36Sopenharmony_ci goto err_sha_hmac_algs; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci } 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci return 0; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci /*i = ARRAY_SIZE(sha_hmac_algs);*/ 248262306a36Sopenharmony_cierr_sha_hmac_algs: 248362306a36Sopenharmony_ci for (j = 0; j < i; j++) 248462306a36Sopenharmony_ci crypto_unregister_ahash(&sha_hmac_algs[j]); 248562306a36Sopenharmony_ci i = ARRAY_SIZE(sha_384_512_algs); 248662306a36Sopenharmony_cierr_sha_384_512_algs: 248762306a36Sopenharmony_ci for (j = 0; j < i; j++) 248862306a36Sopenharmony_ci crypto_unregister_ahash(&sha_384_512_algs[j]); 248962306a36Sopenharmony_ci crypto_unregister_ahash(&sha_224_alg); 249062306a36Sopenharmony_cierr_sha_224_algs: 249162306a36Sopenharmony_ci i = ARRAY_SIZE(sha_1_256_algs); 249262306a36Sopenharmony_cierr_sha_1_256_algs: 249362306a36Sopenharmony_ci for (j = 0; j < i; j++) 249462306a36Sopenharmony_ci crypto_unregister_ahash(&sha_1_256_algs[j]); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci return err; 249762306a36Sopenharmony_ci} 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_cistatic int atmel_sha_dma_init(struct atmel_sha_dev *dd) 250062306a36Sopenharmony_ci{ 250162306a36Sopenharmony_ci dd->dma_lch_in.chan = dma_request_chan(dd->dev, "tx"); 250262306a36Sopenharmony_ci if (IS_ERR(dd->dma_lch_in.chan)) { 250362306a36Sopenharmony_ci return dev_err_probe(dd->dev, PTR_ERR(dd->dma_lch_in.chan), 250462306a36Sopenharmony_ci "DMA channel is not available\n"); 250562306a36Sopenharmony_ci } 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci dd->dma_lch_in.dma_conf.dst_addr = dd->phys_base + 250862306a36Sopenharmony_ci SHA_REG_DIN(0); 250962306a36Sopenharmony_ci dd->dma_lch_in.dma_conf.src_maxburst = 1; 251062306a36Sopenharmony_ci dd->dma_lch_in.dma_conf.src_addr_width = 251162306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_4_BYTES; 251262306a36Sopenharmony_ci dd->dma_lch_in.dma_conf.dst_maxburst = 1; 251362306a36Sopenharmony_ci dd->dma_lch_in.dma_conf.dst_addr_width = 251462306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_4_BYTES; 251562306a36Sopenharmony_ci dd->dma_lch_in.dma_conf.device_fc = false; 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci return 0; 251862306a36Sopenharmony_ci} 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_cistatic void atmel_sha_dma_cleanup(struct atmel_sha_dev *dd) 252162306a36Sopenharmony_ci{ 252262306a36Sopenharmony_ci dma_release_channel(dd->dma_lch_in.chan); 252362306a36Sopenharmony_ci} 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_cistatic void atmel_sha_get_cap(struct atmel_sha_dev *dd) 252662306a36Sopenharmony_ci{ 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci dd->caps.has_dma = 0; 252962306a36Sopenharmony_ci dd->caps.has_dualbuff = 0; 253062306a36Sopenharmony_ci dd->caps.has_sha224 = 0; 253162306a36Sopenharmony_ci dd->caps.has_sha_384_512 = 0; 253262306a36Sopenharmony_ci dd->caps.has_uihv = 0; 253362306a36Sopenharmony_ci dd->caps.has_hmac = 0; 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci /* keep only major version number */ 253662306a36Sopenharmony_ci switch (dd->hw_version & 0xff0) { 253762306a36Sopenharmony_ci case 0x700: 253862306a36Sopenharmony_ci case 0x600: 253962306a36Sopenharmony_ci case 0x510: 254062306a36Sopenharmony_ci dd->caps.has_dma = 1; 254162306a36Sopenharmony_ci dd->caps.has_dualbuff = 1; 254262306a36Sopenharmony_ci dd->caps.has_sha224 = 1; 254362306a36Sopenharmony_ci dd->caps.has_sha_384_512 = 1; 254462306a36Sopenharmony_ci dd->caps.has_uihv = 1; 254562306a36Sopenharmony_ci dd->caps.has_hmac = 1; 254662306a36Sopenharmony_ci break; 254762306a36Sopenharmony_ci case 0x420: 254862306a36Sopenharmony_ci dd->caps.has_dma = 1; 254962306a36Sopenharmony_ci dd->caps.has_dualbuff = 1; 255062306a36Sopenharmony_ci dd->caps.has_sha224 = 1; 255162306a36Sopenharmony_ci dd->caps.has_sha_384_512 = 1; 255262306a36Sopenharmony_ci dd->caps.has_uihv = 1; 255362306a36Sopenharmony_ci break; 255462306a36Sopenharmony_ci case 0x410: 255562306a36Sopenharmony_ci dd->caps.has_dma = 1; 255662306a36Sopenharmony_ci dd->caps.has_dualbuff = 1; 255762306a36Sopenharmony_ci dd->caps.has_sha224 = 1; 255862306a36Sopenharmony_ci dd->caps.has_sha_384_512 = 1; 255962306a36Sopenharmony_ci break; 256062306a36Sopenharmony_ci case 0x400: 256162306a36Sopenharmony_ci dd->caps.has_dma = 1; 256262306a36Sopenharmony_ci dd->caps.has_dualbuff = 1; 256362306a36Sopenharmony_ci dd->caps.has_sha224 = 1; 256462306a36Sopenharmony_ci break; 256562306a36Sopenharmony_ci case 0x320: 256662306a36Sopenharmony_ci break; 256762306a36Sopenharmony_ci default: 256862306a36Sopenharmony_ci dev_warn(dd->dev, 256962306a36Sopenharmony_ci "Unmanaged sha version, set minimum capabilities\n"); 257062306a36Sopenharmony_ci break; 257162306a36Sopenharmony_ci } 257262306a36Sopenharmony_ci} 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_cistatic const struct of_device_id atmel_sha_dt_ids[] = { 257562306a36Sopenharmony_ci { .compatible = "atmel,at91sam9g46-sha" }, 257662306a36Sopenharmony_ci { /* sentinel */ } 257762306a36Sopenharmony_ci}; 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_sha_dt_ids); 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_cistatic int atmel_sha_probe(struct platform_device *pdev) 258262306a36Sopenharmony_ci{ 258362306a36Sopenharmony_ci struct atmel_sha_dev *sha_dd; 258462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 258562306a36Sopenharmony_ci struct resource *sha_res; 258662306a36Sopenharmony_ci int err; 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci sha_dd = devm_kzalloc(&pdev->dev, sizeof(*sha_dd), GFP_KERNEL); 258962306a36Sopenharmony_ci if (!sha_dd) 259062306a36Sopenharmony_ci return -ENOMEM; 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci sha_dd->dev = dev; 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci platform_set_drvdata(pdev, sha_dd); 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci INIT_LIST_HEAD(&sha_dd->list); 259762306a36Sopenharmony_ci spin_lock_init(&sha_dd->lock); 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci tasklet_init(&sha_dd->done_task, atmel_sha_done_task, 260062306a36Sopenharmony_ci (unsigned long)sha_dd); 260162306a36Sopenharmony_ci tasklet_init(&sha_dd->queue_task, atmel_sha_queue_task, 260262306a36Sopenharmony_ci (unsigned long)sha_dd); 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci crypto_init_queue(&sha_dd->queue, ATMEL_SHA_QUEUE_LENGTH); 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci sha_dd->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &sha_res); 260762306a36Sopenharmony_ci if (IS_ERR(sha_dd->io_base)) { 260862306a36Sopenharmony_ci err = PTR_ERR(sha_dd->io_base); 260962306a36Sopenharmony_ci goto err_tasklet_kill; 261062306a36Sopenharmony_ci } 261162306a36Sopenharmony_ci sha_dd->phys_base = sha_res->start; 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci /* Get the IRQ */ 261462306a36Sopenharmony_ci sha_dd->irq = platform_get_irq(pdev, 0); 261562306a36Sopenharmony_ci if (sha_dd->irq < 0) { 261662306a36Sopenharmony_ci err = sha_dd->irq; 261762306a36Sopenharmony_ci goto err_tasklet_kill; 261862306a36Sopenharmony_ci } 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, sha_dd->irq, atmel_sha_irq, 262162306a36Sopenharmony_ci IRQF_SHARED, "atmel-sha", sha_dd); 262262306a36Sopenharmony_ci if (err) { 262362306a36Sopenharmony_ci dev_err(dev, "unable to request sha irq.\n"); 262462306a36Sopenharmony_ci goto err_tasklet_kill; 262562306a36Sopenharmony_ci } 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci /* Initializing the clock */ 262862306a36Sopenharmony_ci sha_dd->iclk = devm_clk_get(&pdev->dev, "sha_clk"); 262962306a36Sopenharmony_ci if (IS_ERR(sha_dd->iclk)) { 263062306a36Sopenharmony_ci dev_err(dev, "clock initialization failed.\n"); 263162306a36Sopenharmony_ci err = PTR_ERR(sha_dd->iclk); 263262306a36Sopenharmony_ci goto err_tasklet_kill; 263362306a36Sopenharmony_ci } 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci err = clk_prepare(sha_dd->iclk); 263662306a36Sopenharmony_ci if (err) 263762306a36Sopenharmony_ci goto err_tasklet_kill; 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci err = atmel_sha_hw_version_init(sha_dd); 264062306a36Sopenharmony_ci if (err) 264162306a36Sopenharmony_ci goto err_iclk_unprepare; 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci atmel_sha_get_cap(sha_dd); 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci if (sha_dd->caps.has_dma) { 264662306a36Sopenharmony_ci err = atmel_sha_dma_init(sha_dd); 264762306a36Sopenharmony_ci if (err) 264862306a36Sopenharmony_ci goto err_iclk_unprepare; 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci dev_info(dev, "using %s for DMA transfers\n", 265162306a36Sopenharmony_ci dma_chan_name(sha_dd->dma_lch_in.chan)); 265262306a36Sopenharmony_ci } 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci spin_lock(&atmel_sha.lock); 265562306a36Sopenharmony_ci list_add_tail(&sha_dd->list, &atmel_sha.dev_list); 265662306a36Sopenharmony_ci spin_unlock(&atmel_sha.lock); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci err = atmel_sha_register_algs(sha_dd); 265962306a36Sopenharmony_ci if (err) 266062306a36Sopenharmony_ci goto err_algs; 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci dev_info(dev, "Atmel SHA1/SHA256%s%s\n", 266362306a36Sopenharmony_ci sha_dd->caps.has_sha224 ? "/SHA224" : "", 266462306a36Sopenharmony_ci sha_dd->caps.has_sha_384_512 ? "/SHA384/SHA512" : ""); 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci return 0; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_cierr_algs: 266962306a36Sopenharmony_ci spin_lock(&atmel_sha.lock); 267062306a36Sopenharmony_ci list_del(&sha_dd->list); 267162306a36Sopenharmony_ci spin_unlock(&atmel_sha.lock); 267262306a36Sopenharmony_ci if (sha_dd->caps.has_dma) 267362306a36Sopenharmony_ci atmel_sha_dma_cleanup(sha_dd); 267462306a36Sopenharmony_cierr_iclk_unprepare: 267562306a36Sopenharmony_ci clk_unprepare(sha_dd->iclk); 267662306a36Sopenharmony_cierr_tasklet_kill: 267762306a36Sopenharmony_ci tasklet_kill(&sha_dd->queue_task); 267862306a36Sopenharmony_ci tasklet_kill(&sha_dd->done_task); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci return err; 268162306a36Sopenharmony_ci} 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_cistatic int atmel_sha_remove(struct platform_device *pdev) 268462306a36Sopenharmony_ci{ 268562306a36Sopenharmony_ci struct atmel_sha_dev *sha_dd = platform_get_drvdata(pdev); 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci spin_lock(&atmel_sha.lock); 268862306a36Sopenharmony_ci list_del(&sha_dd->list); 268962306a36Sopenharmony_ci spin_unlock(&atmel_sha.lock); 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci atmel_sha_unregister_algs(sha_dd); 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci tasklet_kill(&sha_dd->queue_task); 269462306a36Sopenharmony_ci tasklet_kill(&sha_dd->done_task); 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci if (sha_dd->caps.has_dma) 269762306a36Sopenharmony_ci atmel_sha_dma_cleanup(sha_dd); 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci clk_unprepare(sha_dd->iclk); 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci return 0; 270262306a36Sopenharmony_ci} 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_cistatic struct platform_driver atmel_sha_driver = { 270562306a36Sopenharmony_ci .probe = atmel_sha_probe, 270662306a36Sopenharmony_ci .remove = atmel_sha_remove, 270762306a36Sopenharmony_ci .driver = { 270862306a36Sopenharmony_ci .name = "atmel_sha", 270962306a36Sopenharmony_ci .of_match_table = atmel_sha_dt_ids, 271062306a36Sopenharmony_ci }, 271162306a36Sopenharmony_ci}; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_cimodule_platform_driver(atmel_sha_driver); 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ciMODULE_DESCRIPTION("Atmel SHA (1/256/224/384/512) hw acceleration support."); 271662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 271762306a36Sopenharmony_ciMODULE_AUTHOR("Nicolas Royer - Eukréa Electromatique"); 2718