mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
        kmalloc(a * b, gfp)
with:
        kmalloc_array(a * b, gfp)
as well as handling cases of:
        kmalloc(a * b * c, gfp)
with:
        kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
        kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
        kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
  kmalloc(
-	(sizeof(TYPE)) * E
+	sizeof(TYPE) * E
  , ...)
|
  kmalloc(
-	(sizeof(THING)) * E
+	sizeof(THING) * E
  , ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
  kmalloc(
-	sizeof(u8) * (COUNT)
+	COUNT
  , ...)
|
  kmalloc(
-	sizeof(__u8) * (COUNT)
+	COUNT
  , ...)
|
  kmalloc(
-	sizeof(char) * (COUNT)
+	COUNT
  , ...)
|
  kmalloc(
-	sizeof(unsigned char) * (COUNT)
+	COUNT
  , ...)
|
  kmalloc(
-	sizeof(u8) * COUNT
+	COUNT
  , ...)
|
  kmalloc(
-	sizeof(__u8) * COUNT
+	COUNT
  , ...)
|
  kmalloc(
-	sizeof(char) * COUNT
+	COUNT
  , ...)
|
  kmalloc(
-	sizeof(unsigned char) * COUNT
+	COUNT
  , ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
  (
-	sizeof(TYPE) * (COUNT_ID)
+	COUNT_ID, sizeof(TYPE)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(TYPE) * COUNT_ID
+	COUNT_ID, sizeof(TYPE)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(TYPE) * (COUNT_CONST)
+	COUNT_CONST, sizeof(TYPE)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(TYPE) * COUNT_CONST
+	COUNT_CONST, sizeof(TYPE)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(THING) * (COUNT_ID)
+	COUNT_ID, sizeof(THING)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(THING) * COUNT_ID
+	COUNT_ID, sizeof(THING)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(THING) * (COUNT_CONST)
+	COUNT_CONST, sizeof(THING)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(THING) * COUNT_CONST
+	COUNT_CONST, sizeof(THING)
  , ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
  (
-	SIZE * COUNT
+	COUNT, SIZE
  , ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
  kmalloc(
-	sizeof(TYPE) * (COUNT) * (STRIDE)
+	array3_size(COUNT, STRIDE, sizeof(TYPE))
  , ...)
|
  kmalloc(
-	sizeof(TYPE) * (COUNT) * STRIDE
+	array3_size(COUNT, STRIDE, sizeof(TYPE))
  , ...)
|
  kmalloc(
-	sizeof(TYPE) * COUNT * (STRIDE)
+	array3_size(COUNT, STRIDE, sizeof(TYPE))
  , ...)
|
  kmalloc(
-	sizeof(TYPE) * COUNT * STRIDE
+	array3_size(COUNT, STRIDE, sizeof(TYPE))
  , ...)
|
  kmalloc(
-	sizeof(THING) * (COUNT) * (STRIDE)
+	array3_size(COUNT, STRIDE, sizeof(THING))
  , ...)
|
  kmalloc(
-	sizeof(THING) * (COUNT) * STRIDE
+	array3_size(COUNT, STRIDE, sizeof(THING))
  , ...)
|
  kmalloc(
-	sizeof(THING) * COUNT * (STRIDE)
+	array3_size(COUNT, STRIDE, sizeof(THING))
  , ...)
|
  kmalloc(
-	sizeof(THING) * COUNT * STRIDE
+	array3_size(COUNT, STRIDE, sizeof(THING))
  , ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
  kmalloc(
-	sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+	array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
  , ...)
|
  kmalloc(
-	sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+	array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
  , ...)
|
  kmalloc(
-	sizeof(THING1) * sizeof(THING2) * COUNT
+	array3_size(COUNT, sizeof(THING1), sizeof(THING2))
  , ...)
|
  kmalloc(
-	sizeof(THING1) * sizeof(THING2) * (COUNT)
+	array3_size(COUNT, sizeof(THING1), sizeof(THING2))
  , ...)
|
  kmalloc(
-	sizeof(TYPE1) * sizeof(THING2) * COUNT
+	array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
  , ...)
|
  kmalloc(
-	sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+	array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
  , ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
  kmalloc(
-	(COUNT) * STRIDE * SIZE
+	array3_size(COUNT, STRIDE, SIZE)
  , ...)
|
  kmalloc(
-	COUNT * (STRIDE) * SIZE
+	array3_size(COUNT, STRIDE, SIZE)
  , ...)
|
  kmalloc(
-	COUNT * STRIDE * (SIZE)
+	array3_size(COUNT, STRIDE, SIZE)
  , ...)
|
  kmalloc(
-	(COUNT) * (STRIDE) * SIZE
+	array3_size(COUNT, STRIDE, SIZE)
  , ...)
|
  kmalloc(
-	COUNT * (STRIDE) * (SIZE)
+	array3_size(COUNT, STRIDE, SIZE)
  , ...)
|
  kmalloc(
-	(COUNT) * STRIDE * (SIZE)
+	array3_size(COUNT, STRIDE, SIZE)
  , ...)
|
  kmalloc(
-	(COUNT) * (STRIDE) * (SIZE)
+	array3_size(COUNT, STRIDE, SIZE)
  , ...)
|
  kmalloc(
-	COUNT * STRIDE * SIZE
+	array3_size(COUNT, STRIDE, SIZE)
  , ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
  kmalloc(C1 * C2 * C3, ...)
|
  kmalloc(
-	(E1) * E2 * E3
+	array3_size(E1, E2, E3)
  , ...)
|
  kmalloc(
-	(E1) * (E2) * E3
+	array3_size(E1, E2, E3)
  , ...)
|
  kmalloc(
-	(E1) * (E2) * (E3)
+	array3_size(E1, E2, E3)
  , ...)
|
  kmalloc(
-	E1 * E2 * E3
+	array3_size(E1, E2, E3)
  , ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
  kmalloc(sizeof(THING) * C2, ...)
|
  kmalloc(sizeof(TYPE) * C2, ...)
|
  kmalloc(C1 * C2 * C3, ...)
|
  kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(TYPE) * (E2)
+	E2, sizeof(TYPE)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(TYPE) * E2
+	E2, sizeof(TYPE)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(THING) * (E2)
+	E2, sizeof(THING)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	sizeof(THING) * E2
+	E2, sizeof(THING)
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	(E1) * E2
+	E1, E2
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	(E1) * (E2)
+	E1, E2
  , ...)
|
- kmalloc
+ kmalloc_array
  (
-	E1 * E2
+	E1, E2
  , ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
		
	
			
		
			
				
	
	
		
			1580 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1580 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This file is part of STM32 Crypto driver for Linux.
 | 
						|
 *
 | 
						|
 * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
 | 
						|
 * Author(s): Lionel DEBIEVE <lionel.debieve@st.com> for STMicroelectronics.
 | 
						|
 *
 | 
						|
 * License terms: GPL V2.0.
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License version 2 as published by
 | 
						|
 * the Free Software Foundation.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but
 | 
						|
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 | 
						|
 * details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License along with
 | 
						|
 * this program. If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/clk.h>
 | 
						|
#include <linux/crypto.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/dmaengine.h>
 | 
						|
#include <linux/interrupt.h>
 | 
						|
#include <linux/io.h>
 | 
						|
#include <linux/iopoll.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/of_device.h>
 | 
						|
#include <linux/platform_device.h>
 | 
						|
#include <linux/reset.h>
 | 
						|
 | 
						|
#include <crypto/engine.h>
 | 
						|
#include <crypto/hash.h>
 | 
						|
#include <crypto/md5.h>
 | 
						|
#include <crypto/scatterwalk.h>
 | 
						|
#include <crypto/sha.h>
 | 
						|
#include <crypto/internal/hash.h>
 | 
						|
 | 
						|
#define HASH_CR				0x00
 | 
						|
#define HASH_DIN			0x04
 | 
						|
#define HASH_STR			0x08
 | 
						|
#define HASH_IMR			0x20
 | 
						|
#define HASH_SR				0x24
 | 
						|
#define HASH_CSR(x)			(0x0F8 + ((x) * 0x04))
 | 
						|
#define HASH_HREG(x)			(0x310 + ((x) * 0x04))
 | 
						|
#define HASH_HWCFGR			0x3F0
 | 
						|
#define HASH_VER			0x3F4
 | 
						|
#define HASH_ID				0x3F8
 | 
						|
 | 
						|
/* Control Register */
 | 
						|
#define HASH_CR_INIT			BIT(2)
 | 
						|
#define HASH_CR_DMAE			BIT(3)
 | 
						|
#define HASH_CR_DATATYPE_POS		4
 | 
						|
#define HASH_CR_MODE			BIT(6)
 | 
						|
#define HASH_CR_MDMAT			BIT(13)
 | 
						|
#define HASH_CR_DMAA			BIT(14)
 | 
						|
#define HASH_CR_LKEY			BIT(16)
 | 
						|
 | 
						|
#define HASH_CR_ALGO_SHA1		0x0
 | 
						|
#define HASH_CR_ALGO_MD5		0x80
 | 
						|
#define HASH_CR_ALGO_SHA224		0x40000
 | 
						|
#define HASH_CR_ALGO_SHA256		0x40080
 | 
						|
 | 
						|
/* Interrupt */
 | 
						|
#define HASH_DINIE			BIT(0)
 | 
						|
#define HASH_DCIE			BIT(1)
 | 
						|
 | 
						|
/* Interrupt Mask */
 | 
						|
#define HASH_MASK_CALC_COMPLETION	BIT(0)
 | 
						|
#define HASH_MASK_DATA_INPUT		BIT(1)
 | 
						|
 | 
						|
/* Context swap register */
 | 
						|
#define HASH_CSR_REGISTER_NUMBER	53
 | 
						|
 | 
						|
/* Status Flags */
 | 
						|
#define HASH_SR_DATA_INPUT_READY	BIT(0)
 | 
						|
#define HASH_SR_OUTPUT_READY		BIT(1)
 | 
						|
#define HASH_SR_DMA_ACTIVE		BIT(2)
 | 
						|
#define HASH_SR_BUSY			BIT(3)
 | 
						|
 | 
						|
/* STR Register */
 | 
						|
#define HASH_STR_NBLW_MASK		GENMASK(4, 0)
 | 
						|
#define HASH_STR_DCAL			BIT(8)
 | 
						|
 | 
						|
#define HASH_FLAGS_INIT			BIT(0)
 | 
						|
#define HASH_FLAGS_OUTPUT_READY		BIT(1)
 | 
						|
#define HASH_FLAGS_CPU			BIT(2)
 | 
						|
#define HASH_FLAGS_DMA_READY		BIT(3)
 | 
						|
#define HASH_FLAGS_DMA_ACTIVE		BIT(4)
 | 
						|
#define HASH_FLAGS_HMAC_INIT		BIT(5)
 | 
						|
#define HASH_FLAGS_HMAC_FINAL		BIT(6)
 | 
						|
#define HASH_FLAGS_HMAC_KEY		BIT(7)
 | 
						|
 | 
						|
#define HASH_FLAGS_FINAL		BIT(15)
 | 
						|
#define HASH_FLAGS_FINUP		BIT(16)
 | 
						|
#define HASH_FLAGS_ALGO_MASK		GENMASK(21, 18)
 | 
						|
#define HASH_FLAGS_MD5			BIT(18)
 | 
						|
#define HASH_FLAGS_SHA1			BIT(19)
 | 
						|
#define HASH_FLAGS_SHA224		BIT(20)
 | 
						|
#define HASH_FLAGS_SHA256		BIT(21)
 | 
						|
#define HASH_FLAGS_ERRORS		BIT(22)
 | 
						|
#define HASH_FLAGS_HMAC			BIT(23)
 | 
						|
 | 
						|
#define HASH_OP_UPDATE			1
 | 
						|
#define HASH_OP_FINAL			2
 | 
						|
 | 
						|
enum stm32_hash_data_format {
 | 
						|
	HASH_DATA_32_BITS		= 0x0,
 | 
						|
	HASH_DATA_16_BITS		= 0x1,
 | 
						|
	HASH_DATA_8_BITS		= 0x2,
 | 
						|
	HASH_DATA_1_BIT			= 0x3
 | 
						|
};
 | 
						|
 | 
						|
#define HASH_BUFLEN			256
 | 
						|
#define HASH_LONG_KEY			64
 | 
						|
#define HASH_MAX_KEY_SIZE		(SHA256_BLOCK_SIZE * 8)
 | 
						|
#define HASH_QUEUE_LENGTH		16
 | 
						|
#define HASH_DMA_THRESHOLD		50
 | 
						|
 | 
						|
struct stm32_hash_ctx {
 | 
						|
	struct crypto_engine_ctx enginectx;
 | 
						|
	struct stm32_hash_dev	*hdev;
 | 
						|
	unsigned long		flags;
 | 
						|
 | 
						|
	u8			key[HASH_MAX_KEY_SIZE];
 | 
						|
	int			keylen;
 | 
						|
};
 | 
						|
 | 
						|
struct stm32_hash_request_ctx {
 | 
						|
	struct stm32_hash_dev	*hdev;
 | 
						|
	unsigned long		flags;
 | 
						|
	unsigned long		op;
 | 
						|
 | 
						|
	u8 digest[SHA256_DIGEST_SIZE] __aligned(sizeof(u32));
 | 
						|
	size_t			digcnt;
 | 
						|
	size_t			bufcnt;
 | 
						|
	size_t			buflen;
 | 
						|
 | 
						|
	/* DMA */
 | 
						|
	struct scatterlist	*sg;
 | 
						|
	unsigned int		offset;
 | 
						|
	unsigned int		total;
 | 
						|
	struct scatterlist	sg_key;
 | 
						|
 | 
						|
	dma_addr_t		dma_addr;
 | 
						|
	size_t			dma_ct;
 | 
						|
	int			nents;
 | 
						|
 | 
						|
	u8			data_type;
 | 
						|
 | 
						|
	u8 buffer[HASH_BUFLEN] __aligned(sizeof(u32));
 | 
						|
 | 
						|
	/* Export Context */
 | 
						|
	u32			*hw_context;
 | 
						|
};
 | 
						|
 | 
						|
struct stm32_hash_algs_info {
 | 
						|
	struct ahash_alg	*algs_list;
 | 
						|
	size_t			size;
 | 
						|
};
 | 
						|
 | 
						|
struct stm32_hash_pdata {
 | 
						|
	struct stm32_hash_algs_info	*algs_info;
 | 
						|
	size_t				algs_info_size;
 | 
						|
};
 | 
						|
 | 
						|
struct stm32_hash_dev {
 | 
						|
	struct list_head	list;
 | 
						|
	struct device		*dev;
 | 
						|
	struct clk		*clk;
 | 
						|
	struct reset_control	*rst;
 | 
						|
	void __iomem		*io_base;
 | 
						|
	phys_addr_t		phys_base;
 | 
						|
	u32			dma_mode;
 | 
						|
	u32			dma_maxburst;
 | 
						|
 | 
						|
	spinlock_t		lock; /* lock to protect queue */
 | 
						|
 | 
						|
	struct ahash_request	*req;
 | 
						|
	struct crypto_engine	*engine;
 | 
						|
 | 
						|
	int			err;
 | 
						|
	unsigned long		flags;
 | 
						|
 | 
						|
	struct dma_chan		*dma_lch;
 | 
						|
	struct completion	dma_completion;
 | 
						|
 | 
						|
	const struct stm32_hash_pdata	*pdata;
 | 
						|
};
 | 
						|
 | 
						|
struct stm32_hash_drv {
 | 
						|
	struct list_head	dev_list;
 | 
						|
	spinlock_t		lock; /* List protection access */
 | 
						|
};
 | 
						|
 | 
						|
static struct stm32_hash_drv stm32_hash = {
 | 
						|
	.dev_list = LIST_HEAD_INIT(stm32_hash.dev_list),
 | 
						|
	.lock = __SPIN_LOCK_UNLOCKED(stm32_hash.lock),
 | 
						|
};
 | 
						|
 | 
						|
static void stm32_hash_dma_callback(void *param);
 | 
						|
 | 
						|
static inline u32 stm32_hash_read(struct stm32_hash_dev *hdev, u32 offset)
 | 
						|
{
 | 
						|
	return readl_relaxed(hdev->io_base + offset);
 | 
						|
}
 | 
						|
 | 
						|
static inline void stm32_hash_write(struct stm32_hash_dev *hdev,
 | 
						|
				    u32 offset, u32 value)
 | 
						|
{
 | 
						|
	writel_relaxed(value, hdev->io_base + offset);
 | 
						|
}
 | 
						|
 | 
						|
static inline int stm32_hash_wait_busy(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	u32 status;
 | 
						|
 | 
						|
	return readl_relaxed_poll_timeout(hdev->io_base + HASH_SR, status,
 | 
						|
				   !(status & HASH_SR_BUSY), 10, 10000);
 | 
						|
}
 | 
						|
 | 
						|
static void stm32_hash_set_nblw(struct stm32_hash_dev *hdev, int length)
 | 
						|
{
 | 
						|
	u32 reg;
 | 
						|
 | 
						|
	reg = stm32_hash_read(hdev, HASH_STR);
 | 
						|
	reg &= ~(HASH_STR_NBLW_MASK);
 | 
						|
	reg |= (8U * ((length) % 4U));
 | 
						|
	stm32_hash_write(hdev, HASH_STR, reg);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_write_key(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	struct crypto_ahash *tfm = crypto_ahash_reqtfm(hdev->req);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(tfm);
 | 
						|
	u32 reg;
 | 
						|
	int keylen = ctx->keylen;
 | 
						|
	void *key = ctx->key;
 | 
						|
 | 
						|
	if (keylen) {
 | 
						|
		stm32_hash_set_nblw(hdev, keylen);
 | 
						|
 | 
						|
		while (keylen > 0) {
 | 
						|
			stm32_hash_write(hdev, HASH_DIN, *(u32 *)key);
 | 
						|
			keylen -= 4;
 | 
						|
			key += 4;
 | 
						|
		}
 | 
						|
 | 
						|
		reg = stm32_hash_read(hdev, HASH_STR);
 | 
						|
		reg |= HASH_STR_DCAL;
 | 
						|
		stm32_hash_write(hdev, HASH_STR, reg);
 | 
						|
 | 
						|
		return -EINPROGRESS;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void stm32_hash_write_ctrl(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(hdev->req);
 | 
						|
	struct crypto_ahash *tfm = crypto_ahash_reqtfm(hdev->req);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(tfm);
 | 
						|
 | 
						|
	u32 reg = HASH_CR_INIT;
 | 
						|
 | 
						|
	if (!(hdev->flags & HASH_FLAGS_INIT)) {
 | 
						|
		switch (rctx->flags & HASH_FLAGS_ALGO_MASK) {
 | 
						|
		case HASH_FLAGS_MD5:
 | 
						|
			reg |= HASH_CR_ALGO_MD5;
 | 
						|
			break;
 | 
						|
		case HASH_FLAGS_SHA1:
 | 
						|
			reg |= HASH_CR_ALGO_SHA1;
 | 
						|
			break;
 | 
						|
		case HASH_FLAGS_SHA224:
 | 
						|
			reg |= HASH_CR_ALGO_SHA224;
 | 
						|
			break;
 | 
						|
		case HASH_FLAGS_SHA256:
 | 
						|
			reg |= HASH_CR_ALGO_SHA256;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			reg |= HASH_CR_ALGO_MD5;
 | 
						|
		}
 | 
						|
 | 
						|
		reg |= (rctx->data_type << HASH_CR_DATATYPE_POS);
 | 
						|
 | 
						|
		if (rctx->flags & HASH_FLAGS_HMAC) {
 | 
						|
			hdev->flags |= HASH_FLAGS_HMAC;
 | 
						|
			reg |= HASH_CR_MODE;
 | 
						|
			if (ctx->keylen > HASH_LONG_KEY)
 | 
						|
				reg |= HASH_CR_LKEY;
 | 
						|
		}
 | 
						|
 | 
						|
		stm32_hash_write(hdev, HASH_IMR, HASH_DCIE);
 | 
						|
 | 
						|
		stm32_hash_write(hdev, HASH_CR, reg);
 | 
						|
 | 
						|
		hdev->flags |= HASH_FLAGS_INIT;
 | 
						|
 | 
						|
		dev_dbg(hdev->dev, "Write Control %x\n", reg);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void stm32_hash_append_sg(struct stm32_hash_request_ctx *rctx)
 | 
						|
{
 | 
						|
	size_t count;
 | 
						|
 | 
						|
	while ((rctx->bufcnt < rctx->buflen) && rctx->total) {
 | 
						|
		count = min(rctx->sg->length - rctx->offset, rctx->total);
 | 
						|
		count = min(count, rctx->buflen - rctx->bufcnt);
 | 
						|
 | 
						|
		if (count <= 0) {
 | 
						|
			if ((rctx->sg->length == 0) && !sg_is_last(rctx->sg)) {
 | 
						|
				rctx->sg = sg_next(rctx->sg);
 | 
						|
				continue;
 | 
						|
			} else {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		scatterwalk_map_and_copy(rctx->buffer + rctx->bufcnt, rctx->sg,
 | 
						|
					 rctx->offset, count, 0);
 | 
						|
 | 
						|
		rctx->bufcnt += count;
 | 
						|
		rctx->offset += count;
 | 
						|
		rctx->total -= count;
 | 
						|
 | 
						|
		if (rctx->offset == rctx->sg->length) {
 | 
						|
			rctx->sg = sg_next(rctx->sg);
 | 
						|
			if (rctx->sg)
 | 
						|
				rctx->offset = 0;
 | 
						|
			else
 | 
						|
				rctx->total = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_xmit_cpu(struct stm32_hash_dev *hdev,
 | 
						|
			       const u8 *buf, size_t length, int final)
 | 
						|
{
 | 
						|
	unsigned int count, len32;
 | 
						|
	const u32 *buffer = (const u32 *)buf;
 | 
						|
	u32 reg;
 | 
						|
 | 
						|
	if (final)
 | 
						|
		hdev->flags |= HASH_FLAGS_FINAL;
 | 
						|
 | 
						|
	len32 = DIV_ROUND_UP(length, sizeof(u32));
 | 
						|
 | 
						|
	dev_dbg(hdev->dev, "%s: length: %d, final: %x len32 %i\n",
 | 
						|
		__func__, length, final, len32);
 | 
						|
 | 
						|
	hdev->flags |= HASH_FLAGS_CPU;
 | 
						|
 | 
						|
	stm32_hash_write_ctrl(hdev);
 | 
						|
 | 
						|
	if (stm32_hash_wait_busy(hdev))
 | 
						|
		return -ETIMEDOUT;
 | 
						|
 | 
						|
	if ((hdev->flags & HASH_FLAGS_HMAC) &&
 | 
						|
	    (hdev->flags & ~HASH_FLAGS_HMAC_KEY)) {
 | 
						|
		hdev->flags |= HASH_FLAGS_HMAC_KEY;
 | 
						|
		stm32_hash_write_key(hdev);
 | 
						|
		if (stm32_hash_wait_busy(hdev))
 | 
						|
			return -ETIMEDOUT;
 | 
						|
	}
 | 
						|
 | 
						|
	for (count = 0; count < len32; count++)
 | 
						|
		stm32_hash_write(hdev, HASH_DIN, buffer[count]);
 | 
						|
 | 
						|
	if (final) {
 | 
						|
		stm32_hash_set_nblw(hdev, length);
 | 
						|
		reg = stm32_hash_read(hdev, HASH_STR);
 | 
						|
		reg |= HASH_STR_DCAL;
 | 
						|
		stm32_hash_write(hdev, HASH_STR, reg);
 | 
						|
		if (hdev->flags & HASH_FLAGS_HMAC) {
 | 
						|
			if (stm32_hash_wait_busy(hdev))
 | 
						|
				return -ETIMEDOUT;
 | 
						|
			stm32_hash_write_key(hdev);
 | 
						|
		}
 | 
						|
		return -EINPROGRESS;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_update_cpu(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(hdev->req);
 | 
						|
	int bufcnt, err = 0, final;
 | 
						|
 | 
						|
	dev_dbg(hdev->dev, "%s flags %lx\n", __func__, rctx->flags);
 | 
						|
 | 
						|
	final = (rctx->flags & HASH_FLAGS_FINUP);
 | 
						|
 | 
						|
	while ((rctx->total >= rctx->buflen) ||
 | 
						|
	       (rctx->bufcnt + rctx->total >= rctx->buflen)) {
 | 
						|
		stm32_hash_append_sg(rctx);
 | 
						|
		bufcnt = rctx->bufcnt;
 | 
						|
		rctx->bufcnt = 0;
 | 
						|
		err = stm32_hash_xmit_cpu(hdev, rctx->buffer, bufcnt, 0);
 | 
						|
	}
 | 
						|
 | 
						|
	stm32_hash_append_sg(rctx);
 | 
						|
 | 
						|
	if (final) {
 | 
						|
		bufcnt = rctx->bufcnt;
 | 
						|
		rctx->bufcnt = 0;
 | 
						|
		err = stm32_hash_xmit_cpu(hdev, rctx->buffer, bufcnt,
 | 
						|
					  (rctx->flags & HASH_FLAGS_FINUP));
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_xmit_dma(struct stm32_hash_dev *hdev,
 | 
						|
			       struct scatterlist *sg, int length, int mdma)
 | 
						|
{
 | 
						|
	struct dma_async_tx_descriptor *in_desc;
 | 
						|
	dma_cookie_t cookie;
 | 
						|
	u32 reg;
 | 
						|
	int err;
 | 
						|
 | 
						|
	in_desc = dmaengine_prep_slave_sg(hdev->dma_lch, sg, 1,
 | 
						|
					  DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT |
 | 
						|
					  DMA_CTRL_ACK);
 | 
						|
	if (!in_desc) {
 | 
						|
		dev_err(hdev->dev, "dmaengine_prep_slave error\n");
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	reinit_completion(&hdev->dma_completion);
 | 
						|
	in_desc->callback = stm32_hash_dma_callback;
 | 
						|
	in_desc->callback_param = hdev;
 | 
						|
 | 
						|
	hdev->flags |= HASH_FLAGS_FINAL;
 | 
						|
	hdev->flags |= HASH_FLAGS_DMA_ACTIVE;
 | 
						|
 | 
						|
	reg = stm32_hash_read(hdev, HASH_CR);
 | 
						|
 | 
						|
	if (mdma)
 | 
						|
		reg |= HASH_CR_MDMAT;
 | 
						|
	else
 | 
						|
		reg &= ~HASH_CR_MDMAT;
 | 
						|
 | 
						|
	reg |= HASH_CR_DMAE;
 | 
						|
 | 
						|
	stm32_hash_write(hdev, HASH_CR, reg);
 | 
						|
 | 
						|
	stm32_hash_set_nblw(hdev, length);
 | 
						|
 | 
						|
	cookie = dmaengine_submit(in_desc);
 | 
						|
	err = dma_submit_error(cookie);
 | 
						|
	if (err)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	dma_async_issue_pending(hdev->dma_lch);
 | 
						|
 | 
						|
	if (!wait_for_completion_interruptible_timeout(&hdev->dma_completion,
 | 
						|
						       msecs_to_jiffies(100)))
 | 
						|
		err = -ETIMEDOUT;
 | 
						|
 | 
						|
	if (dma_async_is_tx_complete(hdev->dma_lch, cookie,
 | 
						|
				     NULL, NULL) != DMA_COMPLETE)
 | 
						|
		err = -ETIMEDOUT;
 | 
						|
 | 
						|
	if (err) {
 | 
						|
		dev_err(hdev->dev, "DMA Error %i\n", err);
 | 
						|
		dmaengine_terminate_all(hdev->dma_lch);
 | 
						|
		return err;
 | 
						|
	}
 | 
						|
 | 
						|
	return -EINPROGRESS;
 | 
						|
}
 | 
						|
 | 
						|
static void stm32_hash_dma_callback(void *param)
 | 
						|
{
 | 
						|
	struct stm32_hash_dev *hdev = param;
 | 
						|
 | 
						|
	complete(&hdev->dma_completion);
 | 
						|
 | 
						|
	hdev->flags |= HASH_FLAGS_DMA_READY;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_hmac_dma_send(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(hdev->req);
 | 
						|
	struct crypto_ahash *tfm = crypto_ahash_reqtfm(hdev->req);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(tfm);
 | 
						|
	int err;
 | 
						|
 | 
						|
	if (ctx->keylen < HASH_DMA_THRESHOLD || (hdev->dma_mode == 1)) {
 | 
						|
		err = stm32_hash_write_key(hdev);
 | 
						|
		if (stm32_hash_wait_busy(hdev))
 | 
						|
			return -ETIMEDOUT;
 | 
						|
	} else {
 | 
						|
		if (!(hdev->flags & HASH_FLAGS_HMAC_KEY))
 | 
						|
			sg_init_one(&rctx->sg_key, ctx->key,
 | 
						|
				    ALIGN(ctx->keylen, sizeof(u32)));
 | 
						|
 | 
						|
		rctx->dma_ct = dma_map_sg(hdev->dev, &rctx->sg_key, 1,
 | 
						|
					  DMA_TO_DEVICE);
 | 
						|
		if (rctx->dma_ct == 0) {
 | 
						|
			dev_err(hdev->dev, "dma_map_sg error\n");
 | 
						|
			return -ENOMEM;
 | 
						|
		}
 | 
						|
 | 
						|
		err = stm32_hash_xmit_dma(hdev, &rctx->sg_key, ctx->keylen, 0);
 | 
						|
 | 
						|
		dma_unmap_sg(hdev->dev, &rctx->sg_key, 1, DMA_TO_DEVICE);
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_dma_init(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	struct dma_slave_config dma_conf;
 | 
						|
	int err;
 | 
						|
 | 
						|
	memset(&dma_conf, 0, sizeof(dma_conf));
 | 
						|
 | 
						|
	dma_conf.direction = DMA_MEM_TO_DEV;
 | 
						|
	dma_conf.dst_addr = hdev->phys_base + HASH_DIN;
 | 
						|
	dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 | 
						|
	dma_conf.src_maxburst = hdev->dma_maxburst;
 | 
						|
	dma_conf.dst_maxburst = hdev->dma_maxburst;
 | 
						|
	dma_conf.device_fc = false;
 | 
						|
 | 
						|
	hdev->dma_lch = dma_request_slave_channel(hdev->dev, "in");
 | 
						|
	if (!hdev->dma_lch) {
 | 
						|
		dev_err(hdev->dev, "Couldn't acquire a slave DMA channel.\n");
 | 
						|
		return -EBUSY;
 | 
						|
	}
 | 
						|
 | 
						|
	err = dmaengine_slave_config(hdev->dma_lch, &dma_conf);
 | 
						|
	if (err) {
 | 
						|
		dma_release_channel(hdev->dma_lch);
 | 
						|
		hdev->dma_lch = NULL;
 | 
						|
		dev_err(hdev->dev, "Couldn't configure DMA slave.\n");
 | 
						|
		return err;
 | 
						|
	}
 | 
						|
 | 
						|
	init_completion(&hdev->dma_completion);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_dma_send(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(hdev->req);
 | 
						|
	struct scatterlist sg[1], *tsg;
 | 
						|
	int err = 0, len = 0, reg, ncp = 0;
 | 
						|
	unsigned int i;
 | 
						|
	u32 *buffer = (void *)rctx->buffer;
 | 
						|
 | 
						|
	rctx->sg = hdev->req->src;
 | 
						|
	rctx->total = hdev->req->nbytes;
 | 
						|
 | 
						|
	rctx->nents = sg_nents(rctx->sg);
 | 
						|
 | 
						|
	if (rctx->nents < 0)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	stm32_hash_write_ctrl(hdev);
 | 
						|
 | 
						|
	if (hdev->flags & HASH_FLAGS_HMAC) {
 | 
						|
		err = stm32_hash_hmac_dma_send(hdev);
 | 
						|
		if (err != -EINPROGRESS)
 | 
						|
			return err;
 | 
						|
	}
 | 
						|
 | 
						|
	for_each_sg(rctx->sg, tsg, rctx->nents, i) {
 | 
						|
		len = sg->length;
 | 
						|
 | 
						|
		sg[0] = *tsg;
 | 
						|
		if (sg_is_last(sg)) {
 | 
						|
			if (hdev->dma_mode == 1) {
 | 
						|
				len = (ALIGN(sg->length, 16) - 16);
 | 
						|
 | 
						|
				ncp = sg_pcopy_to_buffer(
 | 
						|
					rctx->sg, rctx->nents,
 | 
						|
					rctx->buffer, sg->length - len,
 | 
						|
					rctx->total - sg->length + len);
 | 
						|
 | 
						|
				sg->length = len;
 | 
						|
			} else {
 | 
						|
				if (!(IS_ALIGNED(sg->length, sizeof(u32)))) {
 | 
						|
					len = sg->length;
 | 
						|
					sg->length = ALIGN(sg->length,
 | 
						|
							   sizeof(u32));
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		rctx->dma_ct = dma_map_sg(hdev->dev, sg, 1,
 | 
						|
					  DMA_TO_DEVICE);
 | 
						|
		if (rctx->dma_ct == 0) {
 | 
						|
			dev_err(hdev->dev, "dma_map_sg error\n");
 | 
						|
			return -ENOMEM;
 | 
						|
		}
 | 
						|
 | 
						|
		err = stm32_hash_xmit_dma(hdev, sg, len,
 | 
						|
					  !sg_is_last(sg));
 | 
						|
 | 
						|
		dma_unmap_sg(hdev->dev, sg, 1, DMA_TO_DEVICE);
 | 
						|
 | 
						|
		if (err == -ENOMEM)
 | 
						|
			return err;
 | 
						|
	}
 | 
						|
 | 
						|
	if (hdev->dma_mode == 1) {
 | 
						|
		if (stm32_hash_wait_busy(hdev))
 | 
						|
			return -ETIMEDOUT;
 | 
						|
		reg = stm32_hash_read(hdev, HASH_CR);
 | 
						|
		reg &= ~HASH_CR_DMAE;
 | 
						|
		reg |= HASH_CR_DMAA;
 | 
						|
		stm32_hash_write(hdev, HASH_CR, reg);
 | 
						|
 | 
						|
		if (ncp) {
 | 
						|
			memset(buffer + ncp, 0,
 | 
						|
			       DIV_ROUND_UP(ncp, sizeof(u32)) - ncp);
 | 
						|
			writesl(hdev->io_base + HASH_DIN, buffer,
 | 
						|
				DIV_ROUND_UP(ncp, sizeof(u32)));
 | 
						|
		}
 | 
						|
		stm32_hash_set_nblw(hdev, ncp);
 | 
						|
		reg = stm32_hash_read(hdev, HASH_STR);
 | 
						|
		reg |= HASH_STR_DCAL;
 | 
						|
		stm32_hash_write(hdev, HASH_STR, reg);
 | 
						|
		err = -EINPROGRESS;
 | 
						|
	}
 | 
						|
 | 
						|
	if (hdev->flags & HASH_FLAGS_HMAC) {
 | 
						|
		if (stm32_hash_wait_busy(hdev))
 | 
						|
			return -ETIMEDOUT;
 | 
						|
		err = stm32_hash_hmac_dma_send(hdev);
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static struct stm32_hash_dev *stm32_hash_find_dev(struct stm32_hash_ctx *ctx)
 | 
						|
{
 | 
						|
	struct stm32_hash_dev *hdev = NULL, *tmp;
 | 
						|
 | 
						|
	spin_lock_bh(&stm32_hash.lock);
 | 
						|
	if (!ctx->hdev) {
 | 
						|
		list_for_each_entry(tmp, &stm32_hash.dev_list, list) {
 | 
						|
			hdev = tmp;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		ctx->hdev = hdev;
 | 
						|
	} else {
 | 
						|
		hdev = ctx->hdev;
 | 
						|
	}
 | 
						|
 | 
						|
	spin_unlock_bh(&stm32_hash.lock);
 | 
						|
 | 
						|
	return hdev;
 | 
						|
}
 | 
						|
 | 
						|
static bool stm32_hash_dma_aligned_data(struct ahash_request *req)
 | 
						|
{
 | 
						|
	struct scatterlist *sg;
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
 | 
						|
	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
 | 
						|
	int i;
 | 
						|
 | 
						|
	if (req->nbytes <= HASH_DMA_THRESHOLD)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (sg_nents(req->src) > 1) {
 | 
						|
		if (hdev->dma_mode == 1)
 | 
						|
			return false;
 | 
						|
		for_each_sg(req->src, sg, sg_nents(req->src), i) {
 | 
						|
			if ((!IS_ALIGNED(sg->length, sizeof(u32))) &&
 | 
						|
			    (!sg_is_last(sg)))
 | 
						|
				return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (req->src->offset % 4)
 | 
						|
		return false;
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_init(struct ahash_request *req)
 | 
						|
{
 | 
						|
	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(tfm);
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
 | 
						|
 | 
						|
	rctx->hdev = hdev;
 | 
						|
 | 
						|
	rctx->flags = HASH_FLAGS_CPU;
 | 
						|
 | 
						|
	rctx->digcnt = crypto_ahash_digestsize(tfm);
 | 
						|
	switch (rctx->digcnt) {
 | 
						|
	case MD5_DIGEST_SIZE:
 | 
						|
		rctx->flags |= HASH_FLAGS_MD5;
 | 
						|
		break;
 | 
						|
	case SHA1_DIGEST_SIZE:
 | 
						|
		rctx->flags |= HASH_FLAGS_SHA1;
 | 
						|
		break;
 | 
						|
	case SHA224_DIGEST_SIZE:
 | 
						|
		rctx->flags |= HASH_FLAGS_SHA224;
 | 
						|
		break;
 | 
						|
	case SHA256_DIGEST_SIZE:
 | 
						|
		rctx->flags |= HASH_FLAGS_SHA256;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	rctx->bufcnt = 0;
 | 
						|
	rctx->buflen = HASH_BUFLEN;
 | 
						|
	rctx->total = 0;
 | 
						|
	rctx->offset = 0;
 | 
						|
	rctx->data_type = HASH_DATA_8_BITS;
 | 
						|
 | 
						|
	memset(rctx->buffer, 0, HASH_BUFLEN);
 | 
						|
 | 
						|
	if (ctx->flags & HASH_FLAGS_HMAC)
 | 
						|
		rctx->flags |= HASH_FLAGS_HMAC;
 | 
						|
 | 
						|
	dev_dbg(hdev->dev, "%s Flags %lx\n", __func__, rctx->flags);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_update_req(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	return stm32_hash_update_cpu(hdev);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_final_req(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	struct ahash_request *req = hdev->req;
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
	int err;
 | 
						|
	int buflen = rctx->bufcnt;
 | 
						|
 | 
						|
	rctx->bufcnt = 0;
 | 
						|
 | 
						|
	if (!(rctx->flags & HASH_FLAGS_CPU))
 | 
						|
		err = stm32_hash_dma_send(hdev);
 | 
						|
	else
 | 
						|
		err = stm32_hash_xmit_cpu(hdev, rctx->buffer, buflen, 1);
 | 
						|
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static void stm32_hash_copy_hash(struct ahash_request *req)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
	u32 *hash = (u32 *)rctx->digest;
 | 
						|
	unsigned int i, hashsize;
 | 
						|
 | 
						|
	switch (rctx->flags & HASH_FLAGS_ALGO_MASK) {
 | 
						|
	case HASH_FLAGS_MD5:
 | 
						|
		hashsize = MD5_DIGEST_SIZE;
 | 
						|
		break;
 | 
						|
	case HASH_FLAGS_SHA1:
 | 
						|
		hashsize = SHA1_DIGEST_SIZE;
 | 
						|
		break;
 | 
						|
	case HASH_FLAGS_SHA224:
 | 
						|
		hashsize = SHA224_DIGEST_SIZE;
 | 
						|
		break;
 | 
						|
	case HASH_FLAGS_SHA256:
 | 
						|
		hashsize = SHA256_DIGEST_SIZE;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < hashsize / sizeof(u32); i++)
 | 
						|
		hash[i] = be32_to_cpu(stm32_hash_read(rctx->hdev,
 | 
						|
						      HASH_HREG(i)));
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_finish(struct ahash_request *req)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
 | 
						|
	if (!req->result)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	memcpy(req->result, rctx->digest, rctx->digcnt);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void stm32_hash_finish_req(struct ahash_request *req, int err)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
	struct stm32_hash_dev *hdev = rctx->hdev;
 | 
						|
 | 
						|
	if (!err && (HASH_FLAGS_FINAL & hdev->flags)) {
 | 
						|
		stm32_hash_copy_hash(req);
 | 
						|
		err = stm32_hash_finish(req);
 | 
						|
		hdev->flags &= ~(HASH_FLAGS_FINAL | HASH_FLAGS_CPU |
 | 
						|
				 HASH_FLAGS_INIT | HASH_FLAGS_DMA_READY |
 | 
						|
				 HASH_FLAGS_OUTPUT_READY | HASH_FLAGS_HMAC |
 | 
						|
				 HASH_FLAGS_HMAC_INIT | HASH_FLAGS_HMAC_FINAL |
 | 
						|
				 HASH_FLAGS_HMAC_KEY);
 | 
						|
	} else {
 | 
						|
		rctx->flags |= HASH_FLAGS_ERRORS;
 | 
						|
	}
 | 
						|
 | 
						|
	crypto_finalize_hash_request(hdev->engine, req, err);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_hw_init(struct stm32_hash_dev *hdev,
 | 
						|
			      struct stm32_hash_request_ctx *rctx)
 | 
						|
{
 | 
						|
	if (!(HASH_FLAGS_INIT & hdev->flags)) {
 | 
						|
		stm32_hash_write(hdev, HASH_CR, HASH_CR_INIT);
 | 
						|
		stm32_hash_write(hdev, HASH_STR, 0);
 | 
						|
		stm32_hash_write(hdev, HASH_DIN, 0);
 | 
						|
		stm32_hash_write(hdev, HASH_IMR, 0);
 | 
						|
		hdev->err = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_one_request(struct crypto_engine *engine, void *areq);
 | 
						|
static int stm32_hash_prepare_req(struct crypto_engine *engine, void *areq);
 | 
						|
 | 
						|
static int stm32_hash_handle_queue(struct stm32_hash_dev *hdev,
 | 
						|
				   struct ahash_request *req)
 | 
						|
{
 | 
						|
	return crypto_transfer_hash_request_to_engine(hdev->engine, req);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_prepare_req(struct crypto_engine *engine, void *areq)
 | 
						|
{
 | 
						|
	struct ahash_request *req = container_of(areq, struct ahash_request,
 | 
						|
						 base);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
 | 
						|
	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
 | 
						|
	struct stm32_hash_request_ctx *rctx;
 | 
						|
 | 
						|
	if (!hdev)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	hdev->req = req;
 | 
						|
 | 
						|
	rctx = ahash_request_ctx(req);
 | 
						|
 | 
						|
	dev_dbg(hdev->dev, "processing new req, op: %lu, nbytes %d\n",
 | 
						|
		rctx->op, req->nbytes);
 | 
						|
 | 
						|
	return stm32_hash_hw_init(hdev, rctx);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_one_request(struct crypto_engine *engine, void *areq)
 | 
						|
{
 | 
						|
	struct ahash_request *req = container_of(areq, struct ahash_request,
 | 
						|
						 base);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
 | 
						|
	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
 | 
						|
	struct stm32_hash_request_ctx *rctx;
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	if (!hdev)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	hdev->req = req;
 | 
						|
 | 
						|
	rctx = ahash_request_ctx(req);
 | 
						|
 | 
						|
	if (rctx->op == HASH_OP_UPDATE)
 | 
						|
		err = stm32_hash_update_req(hdev);
 | 
						|
	else if (rctx->op == HASH_OP_FINAL)
 | 
						|
		err = stm32_hash_final_req(hdev);
 | 
						|
 | 
						|
	if (err != -EINPROGRESS)
 | 
						|
	/* done task will not finish it, so do it here */
 | 
						|
		stm32_hash_finish_req(req, err);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_enqueue(struct ahash_request *req, unsigned int op)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
 | 
						|
	struct stm32_hash_dev *hdev = ctx->hdev;
 | 
						|
 | 
						|
	rctx->op = op;
 | 
						|
 | 
						|
	return stm32_hash_handle_queue(hdev, req);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_update(struct ahash_request *req)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
 | 
						|
	if (!req->nbytes || !(rctx->flags & HASH_FLAGS_CPU))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	rctx->total = req->nbytes;
 | 
						|
	rctx->sg = req->src;
 | 
						|
	rctx->offset = 0;
 | 
						|
 | 
						|
	if ((rctx->bufcnt + rctx->total < rctx->buflen)) {
 | 
						|
		stm32_hash_append_sg(rctx);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return stm32_hash_enqueue(req, HASH_OP_UPDATE);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_final(struct ahash_request *req)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
 | 
						|
	rctx->flags |= HASH_FLAGS_FINUP;
 | 
						|
 | 
						|
	return stm32_hash_enqueue(req, HASH_OP_FINAL);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_finup(struct ahash_request *req)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
 | 
						|
	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
 | 
						|
	int err1, err2;
 | 
						|
 | 
						|
	rctx->flags |= HASH_FLAGS_FINUP;
 | 
						|
 | 
						|
	if (hdev->dma_lch && stm32_hash_dma_aligned_data(req))
 | 
						|
		rctx->flags &= ~HASH_FLAGS_CPU;
 | 
						|
 | 
						|
	err1 = stm32_hash_update(req);
 | 
						|
 | 
						|
	if (err1 == -EINPROGRESS || err1 == -EBUSY)
 | 
						|
		return err1;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * final() has to be always called to cleanup resources
 | 
						|
	 * even if update() failed, except EINPROGRESS
 | 
						|
	 */
 | 
						|
	err2 = stm32_hash_final(req);
 | 
						|
 | 
						|
	return err1 ?: err2;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_digest(struct ahash_request *req)
 | 
						|
{
 | 
						|
	return stm32_hash_init(req) ?: stm32_hash_finup(req);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_export(struct ahash_request *req, void *out)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
 | 
						|
	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
 | 
						|
	u32 *preg;
 | 
						|
	unsigned int i;
 | 
						|
 | 
						|
	while (!(stm32_hash_read(hdev, HASH_SR) & HASH_SR_DATA_INPUT_READY))
 | 
						|
		cpu_relax();
 | 
						|
 | 
						|
	rctx->hw_context = kmalloc_array(3 + HASH_CSR_REGISTER_NUMBER,
 | 
						|
					 sizeof(u32),
 | 
						|
					 GFP_KERNEL);
 | 
						|
 | 
						|
	preg = rctx->hw_context;
 | 
						|
 | 
						|
	*preg++ = stm32_hash_read(hdev, HASH_IMR);
 | 
						|
	*preg++ = stm32_hash_read(hdev, HASH_STR);
 | 
						|
	*preg++ = stm32_hash_read(hdev, HASH_CR);
 | 
						|
	for (i = 0; i < HASH_CSR_REGISTER_NUMBER; i++)
 | 
						|
		*preg++ = stm32_hash_read(hdev, HASH_CSR(i));
 | 
						|
 | 
						|
	memcpy(out, rctx, sizeof(*rctx));
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_import(struct ahash_request *req, const void *in)
 | 
						|
{
 | 
						|
	struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
 | 
						|
	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
 | 
						|
	const u32 *preg = in;
 | 
						|
	u32 reg;
 | 
						|
	unsigned int i;
 | 
						|
 | 
						|
	memcpy(rctx, in, sizeof(*rctx));
 | 
						|
 | 
						|
	preg = rctx->hw_context;
 | 
						|
 | 
						|
	stm32_hash_write(hdev, HASH_IMR, *preg++);
 | 
						|
	stm32_hash_write(hdev, HASH_STR, *preg++);
 | 
						|
	stm32_hash_write(hdev, HASH_CR, *preg);
 | 
						|
	reg = *preg++ | HASH_CR_INIT;
 | 
						|
	stm32_hash_write(hdev, HASH_CR, reg);
 | 
						|
 | 
						|
	for (i = 0; i < HASH_CSR_REGISTER_NUMBER; i++)
 | 
						|
		stm32_hash_write(hdev, HASH_CSR(i), *preg++);
 | 
						|
 | 
						|
	kfree(rctx->hw_context);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_setkey(struct crypto_ahash *tfm,
 | 
						|
			     const u8 *key, unsigned int keylen)
 | 
						|
{
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(tfm);
 | 
						|
 | 
						|
	if (keylen <= HASH_MAX_KEY_SIZE) {
 | 
						|
		memcpy(ctx->key, key, keylen);
 | 
						|
		ctx->keylen = keylen;
 | 
						|
	} else {
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_cra_init_algs(struct crypto_tfm *tfm,
 | 
						|
				    const char *algs_hmac_name)
 | 
						|
{
 | 
						|
	struct stm32_hash_ctx *ctx = crypto_tfm_ctx(tfm);
 | 
						|
 | 
						|
	crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
 | 
						|
				 sizeof(struct stm32_hash_request_ctx));
 | 
						|
 | 
						|
	ctx->keylen = 0;
 | 
						|
 | 
						|
	if (algs_hmac_name)
 | 
						|
		ctx->flags |= HASH_FLAGS_HMAC;
 | 
						|
 | 
						|
	ctx->enginectx.op.do_one_request = stm32_hash_one_request;
 | 
						|
	ctx->enginectx.op.prepare_request = stm32_hash_prepare_req;
 | 
						|
	ctx->enginectx.op.unprepare_request = NULL;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_cra_init(struct crypto_tfm *tfm)
 | 
						|
{
 | 
						|
	return stm32_hash_cra_init_algs(tfm, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_cra_md5_init(struct crypto_tfm *tfm)
 | 
						|
{
 | 
						|
	return stm32_hash_cra_init_algs(tfm, "md5");
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_cra_sha1_init(struct crypto_tfm *tfm)
 | 
						|
{
 | 
						|
	return stm32_hash_cra_init_algs(tfm, "sha1");
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_cra_sha224_init(struct crypto_tfm *tfm)
 | 
						|
{
 | 
						|
	return stm32_hash_cra_init_algs(tfm, "sha224");
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_cra_sha256_init(struct crypto_tfm *tfm)
 | 
						|
{
 | 
						|
	return stm32_hash_cra_init_algs(tfm, "sha256");
 | 
						|
}
 | 
						|
 | 
						|
static irqreturn_t stm32_hash_irq_thread(int irq, void *dev_id)
 | 
						|
{
 | 
						|
	struct stm32_hash_dev *hdev = dev_id;
 | 
						|
 | 
						|
	if (HASH_FLAGS_CPU & hdev->flags) {
 | 
						|
		if (HASH_FLAGS_OUTPUT_READY & hdev->flags) {
 | 
						|
			hdev->flags &= ~HASH_FLAGS_OUTPUT_READY;
 | 
						|
			goto finish;
 | 
						|
		}
 | 
						|
	} else if (HASH_FLAGS_DMA_READY & hdev->flags) {
 | 
						|
		if (HASH_FLAGS_DMA_ACTIVE & hdev->flags) {
 | 
						|
			hdev->flags &= ~HASH_FLAGS_DMA_ACTIVE;
 | 
						|
				goto finish;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return IRQ_HANDLED;
 | 
						|
 | 
						|
finish:
 | 
						|
	/* Finish current request */
 | 
						|
	stm32_hash_finish_req(hdev->req, 0);
 | 
						|
 | 
						|
	return IRQ_HANDLED;
 | 
						|
}
 | 
						|
 | 
						|
static irqreturn_t stm32_hash_irq_handler(int irq, void *dev_id)
 | 
						|
{
 | 
						|
	struct stm32_hash_dev *hdev = dev_id;
 | 
						|
	u32 reg;
 | 
						|
 | 
						|
	reg = stm32_hash_read(hdev, HASH_SR);
 | 
						|
	if (reg & HASH_SR_OUTPUT_READY) {
 | 
						|
		reg &= ~HASH_SR_OUTPUT_READY;
 | 
						|
		stm32_hash_write(hdev, HASH_SR, reg);
 | 
						|
		hdev->flags |= HASH_FLAGS_OUTPUT_READY;
 | 
						|
		/* Disable IT*/
 | 
						|
		stm32_hash_write(hdev, HASH_IMR, 0);
 | 
						|
		return IRQ_WAKE_THREAD;
 | 
						|
	}
 | 
						|
 | 
						|
	return IRQ_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static struct ahash_alg algs_md5_sha1[] = {
 | 
						|
	{
 | 
						|
		.init = stm32_hash_init,
 | 
						|
		.update = stm32_hash_update,
 | 
						|
		.final = stm32_hash_final,
 | 
						|
		.finup = stm32_hash_finup,
 | 
						|
		.digest = stm32_hash_digest,
 | 
						|
		.export = stm32_hash_export,
 | 
						|
		.import = stm32_hash_import,
 | 
						|
		.halg = {
 | 
						|
			.digestsize = MD5_DIGEST_SIZE,
 | 
						|
			.statesize = sizeof(struct stm32_hash_request_ctx),
 | 
						|
			.base = {
 | 
						|
				.cra_name = "md5",
 | 
						|
				.cra_driver_name = "stm32-md5",
 | 
						|
				.cra_priority = 200,
 | 
						|
				.cra_flags = CRYPTO_ALG_TYPE_AHASH |
 | 
						|
					CRYPTO_ALG_ASYNC |
 | 
						|
					CRYPTO_ALG_KERN_DRIVER_ONLY,
 | 
						|
				.cra_blocksize = MD5_HMAC_BLOCK_SIZE,
 | 
						|
				.cra_ctxsize = sizeof(struct stm32_hash_ctx),
 | 
						|
				.cra_alignmask = 3,
 | 
						|
				.cra_init = stm32_hash_cra_init,
 | 
						|
				.cra_module = THIS_MODULE,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.init = stm32_hash_init,
 | 
						|
		.update = stm32_hash_update,
 | 
						|
		.final = stm32_hash_final,
 | 
						|
		.finup = stm32_hash_finup,
 | 
						|
		.digest = stm32_hash_digest,
 | 
						|
		.export = stm32_hash_export,
 | 
						|
		.import = stm32_hash_import,
 | 
						|
		.setkey = stm32_hash_setkey,
 | 
						|
		.halg = {
 | 
						|
			.digestsize = MD5_DIGEST_SIZE,
 | 
						|
			.statesize = sizeof(struct stm32_hash_request_ctx),
 | 
						|
			.base = {
 | 
						|
				.cra_name = "hmac(md5)",
 | 
						|
				.cra_driver_name = "stm32-hmac-md5",
 | 
						|
				.cra_priority = 200,
 | 
						|
				.cra_flags = CRYPTO_ALG_TYPE_AHASH |
 | 
						|
					CRYPTO_ALG_ASYNC |
 | 
						|
					CRYPTO_ALG_KERN_DRIVER_ONLY,
 | 
						|
				.cra_blocksize = MD5_HMAC_BLOCK_SIZE,
 | 
						|
				.cra_ctxsize = sizeof(struct stm32_hash_ctx),
 | 
						|
				.cra_alignmask = 3,
 | 
						|
				.cra_init = stm32_hash_cra_md5_init,
 | 
						|
				.cra_module = THIS_MODULE,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.init = stm32_hash_init,
 | 
						|
		.update = stm32_hash_update,
 | 
						|
		.final = stm32_hash_final,
 | 
						|
		.finup = stm32_hash_finup,
 | 
						|
		.digest = stm32_hash_digest,
 | 
						|
		.export = stm32_hash_export,
 | 
						|
		.import = stm32_hash_import,
 | 
						|
		.halg = {
 | 
						|
			.digestsize = SHA1_DIGEST_SIZE,
 | 
						|
			.statesize = sizeof(struct stm32_hash_request_ctx),
 | 
						|
			.base = {
 | 
						|
				.cra_name = "sha1",
 | 
						|
				.cra_driver_name = "stm32-sha1",
 | 
						|
				.cra_priority = 200,
 | 
						|
				.cra_flags = CRYPTO_ALG_TYPE_AHASH |
 | 
						|
					CRYPTO_ALG_ASYNC |
 | 
						|
					CRYPTO_ALG_KERN_DRIVER_ONLY,
 | 
						|
				.cra_blocksize = SHA1_BLOCK_SIZE,
 | 
						|
				.cra_ctxsize = sizeof(struct stm32_hash_ctx),
 | 
						|
				.cra_alignmask = 3,
 | 
						|
				.cra_init = stm32_hash_cra_init,
 | 
						|
				.cra_module = THIS_MODULE,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.init = stm32_hash_init,
 | 
						|
		.update = stm32_hash_update,
 | 
						|
		.final = stm32_hash_final,
 | 
						|
		.finup = stm32_hash_finup,
 | 
						|
		.digest = stm32_hash_digest,
 | 
						|
		.export = stm32_hash_export,
 | 
						|
		.import = stm32_hash_import,
 | 
						|
		.setkey = stm32_hash_setkey,
 | 
						|
		.halg = {
 | 
						|
			.digestsize = SHA1_DIGEST_SIZE,
 | 
						|
			.statesize = sizeof(struct stm32_hash_request_ctx),
 | 
						|
			.base = {
 | 
						|
				.cra_name = "hmac(sha1)",
 | 
						|
				.cra_driver_name = "stm32-hmac-sha1",
 | 
						|
				.cra_priority = 200,
 | 
						|
				.cra_flags = CRYPTO_ALG_TYPE_AHASH |
 | 
						|
					CRYPTO_ALG_ASYNC |
 | 
						|
					CRYPTO_ALG_KERN_DRIVER_ONLY,
 | 
						|
				.cra_blocksize = SHA1_BLOCK_SIZE,
 | 
						|
				.cra_ctxsize = sizeof(struct stm32_hash_ctx),
 | 
						|
				.cra_alignmask = 3,
 | 
						|
				.cra_init = stm32_hash_cra_sha1_init,
 | 
						|
				.cra_module = THIS_MODULE,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
static struct ahash_alg algs_sha224_sha256[] = {
 | 
						|
	{
 | 
						|
		.init = stm32_hash_init,
 | 
						|
		.update = stm32_hash_update,
 | 
						|
		.final = stm32_hash_final,
 | 
						|
		.finup = stm32_hash_finup,
 | 
						|
		.digest = stm32_hash_digest,
 | 
						|
		.export = stm32_hash_export,
 | 
						|
		.import = stm32_hash_import,
 | 
						|
		.halg = {
 | 
						|
			.digestsize = SHA224_DIGEST_SIZE,
 | 
						|
			.statesize = sizeof(struct stm32_hash_request_ctx),
 | 
						|
			.base = {
 | 
						|
				.cra_name = "sha224",
 | 
						|
				.cra_driver_name = "stm32-sha224",
 | 
						|
				.cra_priority = 200,
 | 
						|
				.cra_flags = CRYPTO_ALG_TYPE_AHASH |
 | 
						|
					CRYPTO_ALG_ASYNC |
 | 
						|
					CRYPTO_ALG_KERN_DRIVER_ONLY,
 | 
						|
				.cra_blocksize = SHA224_BLOCK_SIZE,
 | 
						|
				.cra_ctxsize = sizeof(struct stm32_hash_ctx),
 | 
						|
				.cra_alignmask = 3,
 | 
						|
				.cra_init = stm32_hash_cra_init,
 | 
						|
				.cra_module = THIS_MODULE,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.init = stm32_hash_init,
 | 
						|
		.update = stm32_hash_update,
 | 
						|
		.final = stm32_hash_final,
 | 
						|
		.finup = stm32_hash_finup,
 | 
						|
		.digest = stm32_hash_digest,
 | 
						|
		.setkey = stm32_hash_setkey,
 | 
						|
		.export = stm32_hash_export,
 | 
						|
		.import = stm32_hash_import,
 | 
						|
		.halg = {
 | 
						|
			.digestsize = SHA224_DIGEST_SIZE,
 | 
						|
			.statesize = sizeof(struct stm32_hash_request_ctx),
 | 
						|
			.base = {
 | 
						|
				.cra_name = "hmac(sha224)",
 | 
						|
				.cra_driver_name = "stm32-hmac-sha224",
 | 
						|
				.cra_priority = 200,
 | 
						|
				.cra_flags = CRYPTO_ALG_TYPE_AHASH |
 | 
						|
					CRYPTO_ALG_ASYNC |
 | 
						|
					CRYPTO_ALG_KERN_DRIVER_ONLY,
 | 
						|
				.cra_blocksize = SHA224_BLOCK_SIZE,
 | 
						|
				.cra_ctxsize = sizeof(struct stm32_hash_ctx),
 | 
						|
				.cra_alignmask = 3,
 | 
						|
				.cra_init = stm32_hash_cra_sha224_init,
 | 
						|
				.cra_module = THIS_MODULE,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.init = stm32_hash_init,
 | 
						|
		.update = stm32_hash_update,
 | 
						|
		.final = stm32_hash_final,
 | 
						|
		.finup = stm32_hash_finup,
 | 
						|
		.digest = stm32_hash_digest,
 | 
						|
		.export = stm32_hash_export,
 | 
						|
		.import = stm32_hash_import,
 | 
						|
		.halg = {
 | 
						|
			.digestsize = SHA256_DIGEST_SIZE,
 | 
						|
			.statesize = sizeof(struct stm32_hash_request_ctx),
 | 
						|
			.base = {
 | 
						|
				.cra_name = "sha256",
 | 
						|
				.cra_driver_name = "stm32-sha256",
 | 
						|
				.cra_priority = 200,
 | 
						|
				.cra_flags = CRYPTO_ALG_TYPE_AHASH |
 | 
						|
					CRYPTO_ALG_ASYNC |
 | 
						|
					CRYPTO_ALG_KERN_DRIVER_ONLY,
 | 
						|
				.cra_blocksize = SHA256_BLOCK_SIZE,
 | 
						|
				.cra_ctxsize = sizeof(struct stm32_hash_ctx),
 | 
						|
				.cra_alignmask = 3,
 | 
						|
				.cra_init = stm32_hash_cra_init,
 | 
						|
				.cra_module = THIS_MODULE,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.init = stm32_hash_init,
 | 
						|
		.update = stm32_hash_update,
 | 
						|
		.final = stm32_hash_final,
 | 
						|
		.finup = stm32_hash_finup,
 | 
						|
		.digest = stm32_hash_digest,
 | 
						|
		.export = stm32_hash_export,
 | 
						|
		.import = stm32_hash_import,
 | 
						|
		.setkey = stm32_hash_setkey,
 | 
						|
		.halg = {
 | 
						|
			.digestsize = SHA256_DIGEST_SIZE,
 | 
						|
			.statesize = sizeof(struct stm32_hash_request_ctx),
 | 
						|
			.base = {
 | 
						|
				.cra_name = "hmac(sha256)",
 | 
						|
				.cra_driver_name = "stm32-hmac-sha256",
 | 
						|
				.cra_priority = 200,
 | 
						|
				.cra_flags = CRYPTO_ALG_TYPE_AHASH |
 | 
						|
					CRYPTO_ALG_ASYNC |
 | 
						|
					CRYPTO_ALG_KERN_DRIVER_ONLY,
 | 
						|
				.cra_blocksize = SHA256_BLOCK_SIZE,
 | 
						|
				.cra_ctxsize = sizeof(struct stm32_hash_ctx),
 | 
						|
				.cra_alignmask = 3,
 | 
						|
				.cra_init = stm32_hash_cra_sha256_init,
 | 
						|
				.cra_module = THIS_MODULE,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
static int stm32_hash_register_algs(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	unsigned int i, j;
 | 
						|
	int err;
 | 
						|
 | 
						|
	for (i = 0; i < hdev->pdata->algs_info_size; i++) {
 | 
						|
		for (j = 0; j < hdev->pdata->algs_info[i].size; j++) {
 | 
						|
			err = crypto_register_ahash(
 | 
						|
				&hdev->pdata->algs_info[i].algs_list[j]);
 | 
						|
			if (err)
 | 
						|
				goto err_algs;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
err_algs:
 | 
						|
	dev_err(hdev->dev, "Algo %d : %d failed\n", i, j);
 | 
						|
	for (; i--; ) {
 | 
						|
		for (; j--;)
 | 
						|
			crypto_unregister_ahash(
 | 
						|
				&hdev->pdata->algs_info[i].algs_list[j]);
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_unregister_algs(struct stm32_hash_dev *hdev)
 | 
						|
{
 | 
						|
	unsigned int i, j;
 | 
						|
 | 
						|
	for (i = 0; i < hdev->pdata->algs_info_size; i++) {
 | 
						|
		for (j = 0; j < hdev->pdata->algs_info[i].size; j++)
 | 
						|
			crypto_unregister_ahash(
 | 
						|
				&hdev->pdata->algs_info[i].algs_list[j]);
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static struct stm32_hash_algs_info stm32_hash_algs_info_stm32f4[] = {
 | 
						|
	{
 | 
						|
		.algs_list	= algs_md5_sha1,
 | 
						|
		.size		= ARRAY_SIZE(algs_md5_sha1),
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
static const struct stm32_hash_pdata stm32_hash_pdata_stm32f4 = {
 | 
						|
	.algs_info	= stm32_hash_algs_info_stm32f4,
 | 
						|
	.algs_info_size	= ARRAY_SIZE(stm32_hash_algs_info_stm32f4),
 | 
						|
};
 | 
						|
 | 
						|
static struct stm32_hash_algs_info stm32_hash_algs_info_stm32f7[] = {
 | 
						|
	{
 | 
						|
		.algs_list	= algs_md5_sha1,
 | 
						|
		.size		= ARRAY_SIZE(algs_md5_sha1),
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.algs_list	= algs_sha224_sha256,
 | 
						|
		.size		= ARRAY_SIZE(algs_sha224_sha256),
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
static const struct stm32_hash_pdata stm32_hash_pdata_stm32f7 = {
 | 
						|
	.algs_info	= stm32_hash_algs_info_stm32f7,
 | 
						|
	.algs_info_size	= ARRAY_SIZE(stm32_hash_algs_info_stm32f7),
 | 
						|
};
 | 
						|
 | 
						|
static const struct of_device_id stm32_hash_of_match[] = {
 | 
						|
	{
 | 
						|
		.compatible = "st,stm32f456-hash",
 | 
						|
		.data = &stm32_hash_pdata_stm32f4,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.compatible = "st,stm32f756-hash",
 | 
						|
		.data = &stm32_hash_pdata_stm32f7,
 | 
						|
	},
 | 
						|
	{},
 | 
						|
};
 | 
						|
 | 
						|
MODULE_DEVICE_TABLE(of, stm32_hash_of_match);
 | 
						|
 | 
						|
static int stm32_hash_get_of_match(struct stm32_hash_dev *hdev,
 | 
						|
				   struct device *dev)
 | 
						|
{
 | 
						|
	hdev->pdata = of_device_get_match_data(dev);
 | 
						|
	if (!hdev->pdata) {
 | 
						|
		dev_err(dev, "no compatible OF match\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (of_property_read_u32(dev->of_node, "dma-maxburst",
 | 
						|
				 &hdev->dma_maxburst)) {
 | 
						|
		dev_info(dev, "dma-maxburst not specified, using 0\n");
 | 
						|
		hdev->dma_maxburst = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_probe(struct platform_device *pdev)
 | 
						|
{
 | 
						|
	struct stm32_hash_dev *hdev;
 | 
						|
	struct device *dev = &pdev->dev;
 | 
						|
	struct resource *res;
 | 
						|
	int ret, irq;
 | 
						|
 | 
						|
	hdev = devm_kzalloc(dev, sizeof(*hdev), GFP_KERNEL);
 | 
						|
	if (!hdev)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						|
	hdev->io_base = devm_ioremap_resource(dev, res);
 | 
						|
	if (IS_ERR(hdev->io_base))
 | 
						|
		return PTR_ERR(hdev->io_base);
 | 
						|
 | 
						|
	hdev->phys_base = res->start;
 | 
						|
 | 
						|
	ret = stm32_hash_get_of_match(hdev, dev);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	irq = platform_get_irq(pdev, 0);
 | 
						|
	if (irq < 0) {
 | 
						|
		dev_err(dev, "Cannot get IRQ resource\n");
 | 
						|
		return irq;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = devm_request_threaded_irq(dev, irq, stm32_hash_irq_handler,
 | 
						|
					stm32_hash_irq_thread, IRQF_ONESHOT,
 | 
						|
					dev_name(dev), hdev);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "Cannot grab IRQ\n");
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	hdev->clk = devm_clk_get(&pdev->dev, NULL);
 | 
						|
	if (IS_ERR(hdev->clk)) {
 | 
						|
		dev_err(dev, "failed to get clock for hash (%lu)\n",
 | 
						|
			PTR_ERR(hdev->clk));
 | 
						|
		return PTR_ERR(hdev->clk);
 | 
						|
	}
 | 
						|
 | 
						|
	ret = clk_prepare_enable(hdev->clk);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "failed to enable hash clock (%d)\n", ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	hdev->rst = devm_reset_control_get(&pdev->dev, NULL);
 | 
						|
	if (!IS_ERR(hdev->rst)) {
 | 
						|
		reset_control_assert(hdev->rst);
 | 
						|
		udelay(2);
 | 
						|
		reset_control_deassert(hdev->rst);
 | 
						|
	}
 | 
						|
 | 
						|
	hdev->dev = dev;
 | 
						|
 | 
						|
	platform_set_drvdata(pdev, hdev);
 | 
						|
 | 
						|
	ret = stm32_hash_dma_init(hdev);
 | 
						|
	if (ret)
 | 
						|
		dev_dbg(dev, "DMA mode not available\n");
 | 
						|
 | 
						|
	spin_lock(&stm32_hash.lock);
 | 
						|
	list_add_tail(&hdev->list, &stm32_hash.dev_list);
 | 
						|
	spin_unlock(&stm32_hash.lock);
 | 
						|
 | 
						|
	/* Initialize crypto engine */
 | 
						|
	hdev->engine = crypto_engine_alloc_init(dev, 1);
 | 
						|
	if (!hdev->engine) {
 | 
						|
		ret = -ENOMEM;
 | 
						|
		goto err_engine;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = crypto_engine_start(hdev->engine);
 | 
						|
	if (ret)
 | 
						|
		goto err_engine_start;
 | 
						|
 | 
						|
	hdev->dma_mode = stm32_hash_read(hdev, HASH_HWCFGR);
 | 
						|
 | 
						|
	/* Register algos */
 | 
						|
	ret = stm32_hash_register_algs(hdev);
 | 
						|
	if (ret)
 | 
						|
		goto err_algs;
 | 
						|
 | 
						|
	dev_info(dev, "Init HASH done HW ver %x DMA mode %u\n",
 | 
						|
		 stm32_hash_read(hdev, HASH_VER), hdev->dma_mode);
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
err_algs:
 | 
						|
err_engine_start:
 | 
						|
	crypto_engine_exit(hdev->engine);
 | 
						|
err_engine:
 | 
						|
	spin_lock(&stm32_hash.lock);
 | 
						|
	list_del(&hdev->list);
 | 
						|
	spin_unlock(&stm32_hash.lock);
 | 
						|
 | 
						|
	if (hdev->dma_lch)
 | 
						|
		dma_release_channel(hdev->dma_lch);
 | 
						|
 | 
						|
	clk_disable_unprepare(hdev->clk);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int stm32_hash_remove(struct platform_device *pdev)
 | 
						|
{
 | 
						|
	static struct stm32_hash_dev *hdev;
 | 
						|
 | 
						|
	hdev = platform_get_drvdata(pdev);
 | 
						|
	if (!hdev)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	stm32_hash_unregister_algs(hdev);
 | 
						|
 | 
						|
	crypto_engine_exit(hdev->engine);
 | 
						|
 | 
						|
	spin_lock(&stm32_hash.lock);
 | 
						|
	list_del(&hdev->list);
 | 
						|
	spin_unlock(&stm32_hash.lock);
 | 
						|
 | 
						|
	if (hdev->dma_lch)
 | 
						|
		dma_release_channel(hdev->dma_lch);
 | 
						|
 | 
						|
	clk_disable_unprepare(hdev->clk);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static struct platform_driver stm32_hash_driver = {
 | 
						|
	.probe		= stm32_hash_probe,
 | 
						|
	.remove		= stm32_hash_remove,
 | 
						|
	.driver		= {
 | 
						|
		.name	= "stm32-hash",
 | 
						|
		.of_match_table	= stm32_hash_of_match,
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
module_platform_driver(stm32_hash_driver);
 | 
						|
 | 
						|
MODULE_DESCRIPTION("STM32 SHA1/224/256 & MD5 (HMAC) hw accelerator driver");
 | 
						|
MODULE_AUTHOR("Lionel Debieve <lionel.debieve@st.com>");
 | 
						|
MODULE_LICENSE("GPL v2");
 |