|  | // SPDX-License-Identifier: GPL-2.0+ | 
|  | /* | 
|  | * Copyright 2021 Aspeed Technology Inc. | 
|  | */ | 
|  | #include <crypto/engine.h> | 
|  | #include <crypto/internal/akcipher.h> | 
|  | #include <crypto/internal/rsa.h> | 
|  | #include <crypto/scatterwalk.h> | 
|  | #include <linux/clk.h> | 
|  | #include <linux/count_zeros.h> | 
|  | #include <linux/dma-mapping.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/mfd/syscon.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/regmap.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/string.h> | 
|  |  | 
|  | #ifdef CONFIG_CRYPTO_DEV_ASPEED_DEBUG | 
|  | #define ACRY_DBG(d, fmt, ...)	\ | 
|  | dev_info((d)->dev, "%s() " fmt, __func__, ##__VA_ARGS__) | 
|  | #else | 
|  | #define ACRY_DBG(d, fmt, ...)	\ | 
|  | dev_dbg((d)->dev, "%s() " fmt, __func__, ##__VA_ARGS__) | 
|  | #endif | 
|  |  | 
|  | /***************************** | 
|  | *                           * | 
|  | * ACRY register definitions * | 
|  | *                           * | 
|  | * ***************************/ | 
|  | #define ASPEED_ACRY_TRIGGER		0x000	/* ACRY Engine Control: trigger */ | 
|  | #define ASPEED_ACRY_DMA_CMD		0x048	/* ACRY Engine Control: Command */ | 
|  | #define ASPEED_ACRY_DMA_SRC_BASE	0x04C	/* ACRY DRAM base address for DMA */ | 
|  | #define ASPEED_ACRY_DMA_LEN		0x050	/* ACRY Data Length of DMA */ | 
|  | #define ASPEED_ACRY_RSA_KEY_LEN		0x058	/* ACRY RSA Exp/Mod Key Length (Bits) */ | 
|  | #define ASPEED_ACRY_INT_MASK		0x3F8	/* ACRY Interrupt Mask */ | 
|  | #define ASPEED_ACRY_STATUS		0x3FC	/* ACRY Interrupt Status */ | 
|  |  | 
|  | /* rsa trigger */ | 
|  | #define  ACRY_CMD_RSA_TRIGGER		BIT(0) | 
|  | #define  ACRY_CMD_DMA_RSA_TRIGGER	BIT(1) | 
|  |  | 
|  | /* rsa dma cmd */ | 
|  | #define  ACRY_CMD_DMA_SRAM_MODE_RSA	(0x3 << 4) | 
|  | #define  ACRY_CMD_DMEM_AHB		BIT(8) | 
|  | #define  ACRY_CMD_DMA_SRAM_AHB_ENGINE	0 | 
|  |  | 
|  | /* rsa key len */ | 
|  | #define  RSA_E_BITS_LEN(x)		((x) << 16) | 
|  | #define  RSA_M_BITS_LEN(x)		(x) | 
|  |  | 
|  | /* acry isr */ | 
|  | #define  ACRY_RSA_ISR			BIT(1) | 
|  |  | 
|  | #define ASPEED_ACRY_BUFF_SIZE		0x1800	/* DMA buffer size */ | 
|  | #define ASPEED_ACRY_SRAM_MAX_LEN	2048	/* ACRY SRAM maximum length (Bytes) */ | 
|  | #define ASPEED_ACRY_RSA_MAX_KEY_LEN	512	/* ACRY RSA maximum key length (Bytes) */ | 
|  |  | 
|  | #define CRYPTO_FLAGS_BUSY		BIT(1) | 
|  | #define BYTES_PER_DWORD			4 | 
|  |  | 
|  | /***************************** | 
|  | *                           * | 
|  | * AHBC register definitions * | 
|  | *                           * | 
|  | * ***************************/ | 
|  | #define AHBC_REGION_PROT		0x240 | 
|  | #define REGION_ACRYM			BIT(23) | 
|  |  | 
|  | #define ast_acry_write(acry, val, offset)	\ | 
|  | writel((val), (acry)->regs + (offset)) | 
|  |  | 
|  | #define ast_acry_read(acry, offset)		\ | 
|  | readl((acry)->regs + (offset)) | 
|  |  | 
|  | struct aspeed_acry_dev; | 
|  |  | 
|  | typedef int (*aspeed_acry_fn_t)(struct aspeed_acry_dev *); | 
|  |  | 
|  | struct aspeed_acry_dev { | 
|  | void __iomem			*regs; | 
|  | struct device			*dev; | 
|  | int				irq; | 
|  | struct clk			*clk; | 
|  | struct regmap			*ahbc; | 
|  |  | 
|  | struct akcipher_request		*req; | 
|  | struct tasklet_struct		done_task; | 
|  | aspeed_acry_fn_t		resume; | 
|  | unsigned long			flags; | 
|  |  | 
|  | /* ACRY output SRAM buffer */ | 
|  | void __iomem			*acry_sram; | 
|  |  | 
|  | /* ACRY input DMA buffer */ | 
|  | void				*buf_addr; | 
|  | dma_addr_t			buf_dma_addr; | 
|  |  | 
|  | struct crypto_engine		*crypt_engine_rsa; | 
|  |  | 
|  | /* ACRY SRAM memory mapped */ | 
|  | int				exp_dw_mapping[ASPEED_ACRY_RSA_MAX_KEY_LEN]; | 
|  | int				mod_dw_mapping[ASPEED_ACRY_RSA_MAX_KEY_LEN]; | 
|  | int				data_byte_mapping[ASPEED_ACRY_SRAM_MAX_LEN]; | 
|  | }; | 
|  |  | 
|  | struct aspeed_acry_ctx { | 
|  | struct aspeed_acry_dev		*acry_dev; | 
|  |  | 
|  | struct rsa_key			key; | 
|  | int				enc; | 
|  | u8				*n; | 
|  | u8				*e; | 
|  | u8				*d; | 
|  | size_t				n_sz; | 
|  | size_t				e_sz; | 
|  | size_t				d_sz; | 
|  |  | 
|  | aspeed_acry_fn_t		trigger; | 
|  |  | 
|  | struct crypto_akcipher          *fallback_tfm; | 
|  | }; | 
|  |  | 
|  | struct aspeed_acry_alg { | 
|  | struct aspeed_acry_dev		*acry_dev; | 
|  | struct akcipher_engine_alg	akcipher; | 
|  | }; | 
|  |  | 
|  | enum aspeed_rsa_key_mode { | 
|  | ASPEED_RSA_EXP_MODE = 0, | 
|  | ASPEED_RSA_MOD_MODE, | 
|  | ASPEED_RSA_DATA_MODE, | 
|  | }; | 
|  |  | 
|  | static inline struct akcipher_request * | 
|  | akcipher_request_cast(struct crypto_async_request *req) | 
|  | { | 
|  | return container_of(req, struct akcipher_request, base); | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_do_fallback(struct akcipher_request *req) | 
|  | { | 
|  | struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req); | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher); | 
|  | int err; | 
|  |  | 
|  | akcipher_request_set_tfm(req, ctx->fallback_tfm); | 
|  |  | 
|  | if (ctx->enc) | 
|  | err = crypto_akcipher_encrypt(req); | 
|  | else | 
|  | err = crypto_akcipher_decrypt(req); | 
|  |  | 
|  | akcipher_request_set_tfm(req, cipher); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static bool aspeed_acry_need_fallback(struct akcipher_request *req) | 
|  | { | 
|  | struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req); | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher); | 
|  |  | 
|  | return ctx->key.n_sz > ASPEED_ACRY_RSA_MAX_KEY_LEN; | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_handle_queue(struct aspeed_acry_dev *acry_dev, | 
|  | struct akcipher_request *req) | 
|  | { | 
|  | if (aspeed_acry_need_fallback(req)) { | 
|  | ACRY_DBG(acry_dev, "SW fallback\n"); | 
|  | return aspeed_acry_do_fallback(req); | 
|  | } | 
|  |  | 
|  | return crypto_transfer_akcipher_request_to_engine(acry_dev->crypt_engine_rsa, req); | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_do_request(struct crypto_engine *engine, void *areq) | 
|  | { | 
|  | struct akcipher_request *req = akcipher_request_cast(areq); | 
|  | struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req); | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher); | 
|  | struct aspeed_acry_dev *acry_dev = ctx->acry_dev; | 
|  |  | 
|  | acry_dev->req = req; | 
|  | acry_dev->flags |= CRYPTO_FLAGS_BUSY; | 
|  |  | 
|  | return ctx->trigger(acry_dev); | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_complete(struct aspeed_acry_dev *acry_dev, int err) | 
|  | { | 
|  | struct akcipher_request *req = acry_dev->req; | 
|  |  | 
|  | acry_dev->flags &= ~CRYPTO_FLAGS_BUSY; | 
|  |  | 
|  | crypto_finalize_akcipher_request(acry_dev->crypt_engine_rsa, req, err); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Copy Data to DMA buffer for engine used. | 
|  | */ | 
|  | static void aspeed_acry_rsa_sg_copy_to_buffer(struct aspeed_acry_dev *acry_dev, | 
|  | u8 *buf, struct scatterlist *src, | 
|  | size_t nbytes) | 
|  | { | 
|  | static u8 dram_buffer[ASPEED_ACRY_SRAM_MAX_LEN]; | 
|  | int i = 0, j; | 
|  | int data_idx; | 
|  |  | 
|  | ACRY_DBG(acry_dev, "\n"); | 
|  |  | 
|  | scatterwalk_map_and_copy(dram_buffer, src, 0, nbytes, 0); | 
|  |  | 
|  | for (j = nbytes - 1; j >= 0; j--) { | 
|  | data_idx = acry_dev->data_byte_mapping[i]; | 
|  | buf[data_idx] =  dram_buffer[j]; | 
|  | i++; | 
|  | } | 
|  |  | 
|  | for (; i < ASPEED_ACRY_SRAM_MAX_LEN; i++) { | 
|  | data_idx = acry_dev->data_byte_mapping[i]; | 
|  | buf[data_idx] = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Copy Exp/Mod to DMA buffer for engine used. | 
|  | * | 
|  | * Params: | 
|  | * - mode 0 : Exponential | 
|  | * - mode 1 : Modulus | 
|  | * | 
|  | * Example: | 
|  | * - DRAM memory layout: | 
|  | *	D[0], D[4], D[8], D[12] | 
|  | * - ACRY SRAM memory layout should reverse the order of source data: | 
|  | *	D[12], D[8], D[4], D[0] | 
|  | */ | 
|  | static int aspeed_acry_rsa_ctx_copy(struct aspeed_acry_dev *acry_dev, void *buf, | 
|  | const void *xbuf, size_t nbytes, | 
|  | enum aspeed_rsa_key_mode mode) | 
|  | { | 
|  | const u8 *src = xbuf; | 
|  | __le32 *dw_buf = buf; | 
|  | int nbits, ndw; | 
|  | int i, j, idx; | 
|  | u32 data = 0; | 
|  |  | 
|  | ACRY_DBG(acry_dev, "nbytes:%zu, mode:%d\n", nbytes, mode); | 
|  |  | 
|  | if (nbytes > ASPEED_ACRY_RSA_MAX_KEY_LEN) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Remove the leading zeros */ | 
|  | while (nbytes > 0 && src[0] == 0) { | 
|  | src++; | 
|  | nbytes--; | 
|  | } | 
|  |  | 
|  | nbits = nbytes * 8; | 
|  | if (nbytes > 0) | 
|  | nbits -= count_leading_zeros(src[0]) - (BITS_PER_LONG - 8); | 
|  |  | 
|  | /* double-world alignment */ | 
|  | ndw = DIV_ROUND_UP(nbytes, BYTES_PER_DWORD); | 
|  |  | 
|  | if (nbytes > 0) { | 
|  | i = BYTES_PER_DWORD - nbytes % BYTES_PER_DWORD; | 
|  | i %= BYTES_PER_DWORD; | 
|  |  | 
|  | for (j = ndw; j > 0; j--) { | 
|  | for (; i < BYTES_PER_DWORD; i++) { | 
|  | data <<= 8; | 
|  | data |= *src++; | 
|  | } | 
|  |  | 
|  | i = 0; | 
|  |  | 
|  | if (mode == ASPEED_RSA_EXP_MODE) | 
|  | idx = acry_dev->exp_dw_mapping[j - 1]; | 
|  | else /* mode == ASPEED_RSA_MOD_MODE */ | 
|  | idx = acry_dev->mod_dw_mapping[j - 1]; | 
|  |  | 
|  | dw_buf[idx] = cpu_to_le32(data); | 
|  | } | 
|  | } | 
|  |  | 
|  | return nbits; | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_rsa_transfer(struct aspeed_acry_dev *acry_dev) | 
|  | { | 
|  | struct akcipher_request *req = acry_dev->req; | 
|  | u8 __iomem *sram_buffer = acry_dev->acry_sram; | 
|  | struct scatterlist *out_sg = req->dst; | 
|  | static u8 dram_buffer[ASPEED_ACRY_SRAM_MAX_LEN]; | 
|  | int leading_zero = 1; | 
|  | int result_nbytes; | 
|  | int i = 0, j; | 
|  | int data_idx; | 
|  |  | 
|  | /* Set Data Memory to AHB(CPU) Access Mode */ | 
|  | ast_acry_write(acry_dev, ACRY_CMD_DMEM_AHB, ASPEED_ACRY_DMA_CMD); | 
|  |  | 
|  | /* Disable ACRY SRAM protection */ | 
|  | regmap_update_bits(acry_dev->ahbc, AHBC_REGION_PROT, | 
|  | REGION_ACRYM, 0); | 
|  |  | 
|  | result_nbytes = ASPEED_ACRY_SRAM_MAX_LEN; | 
|  |  | 
|  | for (j = ASPEED_ACRY_SRAM_MAX_LEN - 1; j >= 0; j--) { | 
|  | data_idx = acry_dev->data_byte_mapping[j]; | 
|  | if (readb(sram_buffer + data_idx) == 0 && leading_zero) { | 
|  | result_nbytes--; | 
|  | } else { | 
|  | leading_zero = 0; | 
|  | dram_buffer[i] = readb(sram_buffer + data_idx); | 
|  | i++; | 
|  | } | 
|  | } | 
|  |  | 
|  | ACRY_DBG(acry_dev, "result_nbytes:%d, req->dst_len:%d\n", | 
|  | result_nbytes, req->dst_len); | 
|  |  | 
|  | if (result_nbytes <= req->dst_len) { | 
|  | scatterwalk_map_and_copy(dram_buffer, out_sg, 0, result_nbytes, | 
|  | 1); | 
|  | req->dst_len = result_nbytes; | 
|  |  | 
|  | } else { | 
|  | dev_err(acry_dev->dev, "RSA engine error!\n"); | 
|  | } | 
|  |  | 
|  | memzero_explicit(acry_dev->buf_addr, ASPEED_ACRY_BUFF_SIZE); | 
|  |  | 
|  | return aspeed_acry_complete(acry_dev, 0); | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_rsa_trigger(struct aspeed_acry_dev *acry_dev) | 
|  | { | 
|  | struct akcipher_request *req = acry_dev->req; | 
|  | struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req); | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher); | 
|  | int ne, nm; | 
|  |  | 
|  | if (!ctx->n || !ctx->n_sz) { | 
|  | dev_err(acry_dev->dev, "%s: key n is not set\n", __func__); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | memzero_explicit(acry_dev->buf_addr, ASPEED_ACRY_BUFF_SIZE); | 
|  |  | 
|  | /* Copy source data to DMA buffer */ | 
|  | aspeed_acry_rsa_sg_copy_to_buffer(acry_dev, acry_dev->buf_addr, | 
|  | req->src, req->src_len); | 
|  |  | 
|  | nm = aspeed_acry_rsa_ctx_copy(acry_dev, acry_dev->buf_addr, ctx->n, | 
|  | ctx->n_sz, ASPEED_RSA_MOD_MODE); | 
|  | if (ctx->enc) { | 
|  | if (!ctx->e || !ctx->e_sz) { | 
|  | dev_err(acry_dev->dev, "%s: key e is not set\n", | 
|  | __func__); | 
|  | return -EINVAL; | 
|  | } | 
|  | /* Copy key e to DMA buffer */ | 
|  | ne = aspeed_acry_rsa_ctx_copy(acry_dev, acry_dev->buf_addr, | 
|  | ctx->e, ctx->e_sz, | 
|  | ASPEED_RSA_EXP_MODE); | 
|  | } else { | 
|  | if (!ctx->d || !ctx->d_sz) { | 
|  | dev_err(acry_dev->dev, "%s: key d is not set\n", | 
|  | __func__); | 
|  | return -EINVAL; | 
|  | } | 
|  | /* Copy key d to DMA buffer */ | 
|  | ne = aspeed_acry_rsa_ctx_copy(acry_dev, acry_dev->buf_addr, | 
|  | ctx->key.d, ctx->key.d_sz, | 
|  | ASPEED_RSA_EXP_MODE); | 
|  | } | 
|  |  | 
|  | ast_acry_write(acry_dev, acry_dev->buf_dma_addr, | 
|  | ASPEED_ACRY_DMA_SRC_BASE); | 
|  | ast_acry_write(acry_dev, (ne << 16) + nm, | 
|  | ASPEED_ACRY_RSA_KEY_LEN); | 
|  | ast_acry_write(acry_dev, ASPEED_ACRY_BUFF_SIZE, | 
|  | ASPEED_ACRY_DMA_LEN); | 
|  |  | 
|  | acry_dev->resume = aspeed_acry_rsa_transfer; | 
|  |  | 
|  | /* Enable ACRY SRAM protection */ | 
|  | regmap_update_bits(acry_dev->ahbc, AHBC_REGION_PROT, | 
|  | REGION_ACRYM, REGION_ACRYM); | 
|  |  | 
|  | ast_acry_write(acry_dev, ACRY_RSA_ISR, ASPEED_ACRY_INT_MASK); | 
|  | ast_acry_write(acry_dev, ACRY_CMD_DMA_SRAM_MODE_RSA | | 
|  | ACRY_CMD_DMA_SRAM_AHB_ENGINE, ASPEED_ACRY_DMA_CMD); | 
|  |  | 
|  | /* Trigger RSA engines */ | 
|  | ast_acry_write(acry_dev, ACRY_CMD_RSA_TRIGGER | | 
|  | ACRY_CMD_DMA_RSA_TRIGGER, ASPEED_ACRY_TRIGGER); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_rsa_enc(struct akcipher_request *req) | 
|  | { | 
|  | struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req); | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher); | 
|  | struct aspeed_acry_dev *acry_dev = ctx->acry_dev; | 
|  |  | 
|  | ctx->trigger = aspeed_acry_rsa_trigger; | 
|  | ctx->enc = 1; | 
|  |  | 
|  | return aspeed_acry_handle_queue(acry_dev, req); | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_rsa_dec(struct akcipher_request *req) | 
|  | { | 
|  | struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req); | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher); | 
|  | struct aspeed_acry_dev *acry_dev = ctx->acry_dev; | 
|  |  | 
|  | ctx->trigger = aspeed_acry_rsa_trigger; | 
|  | ctx->enc = 0; | 
|  |  | 
|  | return aspeed_acry_handle_queue(acry_dev, req); | 
|  | } | 
|  |  | 
|  | static u8 *aspeed_rsa_key_copy(u8 *src, size_t len) | 
|  | { | 
|  | return kmemdup(src, len, GFP_KERNEL); | 
|  | } | 
|  |  | 
|  | static int aspeed_rsa_set_n(struct aspeed_acry_ctx *ctx, u8 *value, | 
|  | size_t len) | 
|  | { | 
|  | ctx->n_sz = len; | 
|  | ctx->n = aspeed_rsa_key_copy(value, len); | 
|  | if (!ctx->n) | 
|  | return -ENOMEM; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int aspeed_rsa_set_e(struct aspeed_acry_ctx *ctx, u8 *value, | 
|  | size_t len) | 
|  | { | 
|  | ctx->e_sz = len; | 
|  | ctx->e = aspeed_rsa_key_copy(value, len); | 
|  | if (!ctx->e) | 
|  | return -ENOMEM; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int aspeed_rsa_set_d(struct aspeed_acry_ctx *ctx, u8 *value, | 
|  | size_t len) | 
|  | { | 
|  | ctx->d_sz = len; | 
|  | ctx->d = aspeed_rsa_key_copy(value, len); | 
|  | if (!ctx->d) | 
|  | return -ENOMEM; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void aspeed_rsa_key_free(struct aspeed_acry_ctx *ctx) | 
|  | { | 
|  | kfree_sensitive(ctx->n); | 
|  | kfree_sensitive(ctx->e); | 
|  | kfree_sensitive(ctx->d); | 
|  | ctx->n_sz = 0; | 
|  | ctx->e_sz = 0; | 
|  | ctx->d_sz = 0; | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_rsa_setkey(struct crypto_akcipher *tfm, const void *key, | 
|  | unsigned int keylen, int priv) | 
|  | { | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm); | 
|  | struct aspeed_acry_dev *acry_dev = ctx->acry_dev; | 
|  | int ret; | 
|  |  | 
|  | if (priv) | 
|  | ret = rsa_parse_priv_key(&ctx->key, key, keylen); | 
|  | else | 
|  | ret = rsa_parse_pub_key(&ctx->key, key, keylen); | 
|  |  | 
|  | if (ret) { | 
|  | dev_err(acry_dev->dev, "rsa parse key failed, ret:0x%x\n", | 
|  | ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Aspeed engine supports up to 4096 bits, | 
|  | * Use software fallback instead. | 
|  | */ | 
|  | if (ctx->key.n_sz > ASPEED_ACRY_RSA_MAX_KEY_LEN) | 
|  | return 0; | 
|  |  | 
|  | ret = aspeed_rsa_set_n(ctx, (u8 *)ctx->key.n, ctx->key.n_sz); | 
|  | if (ret) | 
|  | goto err; | 
|  |  | 
|  | ret = aspeed_rsa_set_e(ctx, (u8 *)ctx->key.e, ctx->key.e_sz); | 
|  | if (ret) | 
|  | goto err; | 
|  |  | 
|  | if (priv) { | 
|  | ret = aspeed_rsa_set_d(ctx, (u8 *)ctx->key.d, ctx->key.d_sz); | 
|  | if (ret) | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err: | 
|  | dev_err(acry_dev->dev, "rsa set key failed\n"); | 
|  | aspeed_rsa_key_free(ctx); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_rsa_set_pub_key(struct crypto_akcipher *tfm, | 
|  | const void *key, | 
|  | unsigned int keylen) | 
|  | { | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm); | 
|  | int ret; | 
|  |  | 
|  | ret = crypto_akcipher_set_pub_key(ctx->fallback_tfm, key, keylen); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | return aspeed_acry_rsa_setkey(tfm, key, keylen, 0); | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_rsa_set_priv_key(struct crypto_akcipher *tfm, | 
|  | const void *key, | 
|  | unsigned int keylen) | 
|  | { | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm); | 
|  | int ret; | 
|  |  | 
|  | ret = crypto_akcipher_set_priv_key(ctx->fallback_tfm, key, keylen); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | return aspeed_acry_rsa_setkey(tfm, key, keylen, 1); | 
|  | } | 
|  |  | 
|  | static unsigned int aspeed_acry_rsa_max_size(struct crypto_akcipher *tfm) | 
|  | { | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm); | 
|  |  | 
|  | if (ctx->key.n_sz > ASPEED_ACRY_RSA_MAX_KEY_LEN) | 
|  | return crypto_akcipher_maxsize(ctx->fallback_tfm); | 
|  |  | 
|  | return ctx->n_sz; | 
|  | } | 
|  |  | 
|  | static int aspeed_acry_rsa_init_tfm(struct crypto_akcipher *tfm) | 
|  | { | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm); | 
|  | struct akcipher_alg *alg = crypto_akcipher_alg(tfm); | 
|  | const char *name = crypto_tfm_alg_name(&tfm->base); | 
|  | struct aspeed_acry_alg *acry_alg; | 
|  |  | 
|  | acry_alg = container_of(alg, struct aspeed_acry_alg, akcipher.base); | 
|  |  | 
|  | ctx->acry_dev = acry_alg->acry_dev; | 
|  |  | 
|  | ctx->fallback_tfm = crypto_alloc_akcipher(name, 0, CRYPTO_ALG_ASYNC | | 
|  | CRYPTO_ALG_NEED_FALLBACK); | 
|  | if (IS_ERR(ctx->fallback_tfm)) { | 
|  | dev_err(ctx->acry_dev->dev, "ERROR: Cannot allocate fallback for %s %ld\n", | 
|  | name, PTR_ERR(ctx->fallback_tfm)); | 
|  | return PTR_ERR(ctx->fallback_tfm); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void aspeed_acry_rsa_exit_tfm(struct crypto_akcipher *tfm) | 
|  | { | 
|  | struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm); | 
|  |  | 
|  | crypto_free_akcipher(ctx->fallback_tfm); | 
|  | } | 
|  |  | 
|  | static struct aspeed_acry_alg aspeed_acry_akcipher_algs[] = { | 
|  | { | 
|  | .akcipher.base = { | 
|  | .encrypt = aspeed_acry_rsa_enc, | 
|  | .decrypt = aspeed_acry_rsa_dec, | 
|  | .sign = aspeed_acry_rsa_dec, | 
|  | .verify = aspeed_acry_rsa_enc, | 
|  | .set_pub_key = aspeed_acry_rsa_set_pub_key, | 
|  | .set_priv_key = aspeed_acry_rsa_set_priv_key, | 
|  | .max_size = aspeed_acry_rsa_max_size, | 
|  | .init = aspeed_acry_rsa_init_tfm, | 
|  | .exit = aspeed_acry_rsa_exit_tfm, | 
|  | .base = { | 
|  | .cra_name = "rsa", | 
|  | .cra_driver_name = "aspeed-rsa", | 
|  | .cra_priority = 300, | 
|  | .cra_flags = CRYPTO_ALG_TYPE_AKCIPHER | | 
|  | CRYPTO_ALG_ASYNC | | 
|  | CRYPTO_ALG_KERN_DRIVER_ONLY | | 
|  | CRYPTO_ALG_NEED_FALLBACK, | 
|  | .cra_module = THIS_MODULE, | 
|  | .cra_ctxsize = sizeof(struct aspeed_acry_ctx), | 
|  | }, | 
|  | }, | 
|  | .akcipher.op = { | 
|  | .do_one_request = aspeed_acry_do_request, | 
|  | }, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static void aspeed_acry_register(struct aspeed_acry_dev *acry_dev) | 
|  | { | 
|  | int i, rc; | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE(aspeed_acry_akcipher_algs); i++) { | 
|  | aspeed_acry_akcipher_algs[i].acry_dev = acry_dev; | 
|  | rc = crypto_engine_register_akcipher(&aspeed_acry_akcipher_algs[i].akcipher); | 
|  | if (rc) { | 
|  | ACRY_DBG(acry_dev, "Failed to register %s\n", | 
|  | aspeed_acry_akcipher_algs[i].akcipher.base.base.cra_name); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void aspeed_acry_unregister(struct aspeed_acry_dev *acry_dev) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE(aspeed_acry_akcipher_algs); i++) | 
|  | crypto_engine_unregister_akcipher(&aspeed_acry_akcipher_algs[i].akcipher); | 
|  | } | 
|  |  | 
|  | /* ACRY interrupt service routine. */ | 
|  | static irqreturn_t aspeed_acry_irq(int irq, void *dev) | 
|  | { | 
|  | struct aspeed_acry_dev *acry_dev = (struct aspeed_acry_dev *)dev; | 
|  | u32 sts; | 
|  |  | 
|  | sts = ast_acry_read(acry_dev, ASPEED_ACRY_STATUS); | 
|  | ast_acry_write(acry_dev, sts, ASPEED_ACRY_STATUS); | 
|  |  | 
|  | ACRY_DBG(acry_dev, "irq sts:0x%x\n", sts); | 
|  |  | 
|  | if (sts & ACRY_RSA_ISR) { | 
|  | /* Stop RSA engine */ | 
|  | ast_acry_write(acry_dev, 0, ASPEED_ACRY_TRIGGER); | 
|  |  | 
|  | if (acry_dev->flags & CRYPTO_FLAGS_BUSY) | 
|  | tasklet_schedule(&acry_dev->done_task); | 
|  | else | 
|  | dev_err(acry_dev->dev, "RSA no active requests.\n"); | 
|  | } | 
|  |  | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * ACRY SRAM has its own memory layout. | 
|  | * Set the DRAM to SRAM indexing for future used. | 
|  | */ | 
|  | static void aspeed_acry_sram_mapping(struct aspeed_acry_dev *acry_dev) | 
|  | { | 
|  | int i, j = 0; | 
|  |  | 
|  | for (i = 0; i < (ASPEED_ACRY_SRAM_MAX_LEN / BYTES_PER_DWORD); i++) { | 
|  | acry_dev->exp_dw_mapping[i] = j; | 
|  | acry_dev->mod_dw_mapping[i] = j + 4; | 
|  | acry_dev->data_byte_mapping[(i * 4)] = (j + 8) * 4; | 
|  | acry_dev->data_byte_mapping[(i * 4) + 1] = (j + 8) * 4 + 1; | 
|  | acry_dev->data_byte_mapping[(i * 4) + 2] = (j + 8) * 4 + 2; | 
|  | acry_dev->data_byte_mapping[(i * 4) + 3] = (j + 8) * 4 + 3; | 
|  | j++; | 
|  | j = j % 4 ? j : j + 8; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void aspeed_acry_done_task(unsigned long data) | 
|  | { | 
|  | struct aspeed_acry_dev *acry_dev = (struct aspeed_acry_dev *)data; | 
|  |  | 
|  | (void)acry_dev->resume(acry_dev); | 
|  | } | 
|  |  | 
|  | static const struct of_device_id aspeed_acry_of_matches[] = { | 
|  | { .compatible = "aspeed,ast2600-acry", }, | 
|  | {}, | 
|  | }; | 
|  |  | 
|  | static int aspeed_acry_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct aspeed_acry_dev *acry_dev; | 
|  | struct device *dev = &pdev->dev; | 
|  | int rc; | 
|  |  | 
|  | acry_dev = devm_kzalloc(dev, sizeof(struct aspeed_acry_dev), | 
|  | GFP_KERNEL); | 
|  | if (!acry_dev) | 
|  | return -ENOMEM; | 
|  |  | 
|  | acry_dev->dev = dev; | 
|  |  | 
|  | platform_set_drvdata(pdev, acry_dev); | 
|  |  | 
|  | acry_dev->regs = devm_platform_ioremap_resource(pdev, 0); | 
|  | if (IS_ERR(acry_dev->regs)) | 
|  | return PTR_ERR(acry_dev->regs); | 
|  |  | 
|  | acry_dev->acry_sram = devm_platform_ioremap_resource(pdev, 1); | 
|  | if (IS_ERR(acry_dev->acry_sram)) | 
|  | return PTR_ERR(acry_dev->acry_sram); | 
|  |  | 
|  | /* Get irq number and register it */ | 
|  | acry_dev->irq = platform_get_irq(pdev, 0); | 
|  | if (acry_dev->irq < 0) | 
|  | return -ENXIO; | 
|  |  | 
|  | rc = devm_request_irq(dev, acry_dev->irq, aspeed_acry_irq, 0, | 
|  | dev_name(dev), acry_dev); | 
|  | if (rc) { | 
|  | dev_err(dev, "Failed to request irq.\n"); | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | acry_dev->clk = devm_clk_get_enabled(dev, NULL); | 
|  | if (IS_ERR(acry_dev->clk)) { | 
|  | dev_err(dev, "Failed to get acry clk\n"); | 
|  | return PTR_ERR(acry_dev->clk); | 
|  | } | 
|  |  | 
|  | acry_dev->ahbc = syscon_regmap_lookup_by_phandle(dev->of_node, | 
|  | "aspeed,ahbc"); | 
|  | if (IS_ERR(acry_dev->ahbc)) { | 
|  | dev_err(dev, "Failed to get AHBC regmap\n"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | /* Initialize crypto hardware engine structure for RSA */ | 
|  | acry_dev->crypt_engine_rsa = crypto_engine_alloc_init(dev, true); | 
|  | if (!acry_dev->crypt_engine_rsa) { | 
|  | rc = -ENOMEM; | 
|  | goto clk_exit; | 
|  | } | 
|  |  | 
|  | rc = crypto_engine_start(acry_dev->crypt_engine_rsa); | 
|  | if (rc) | 
|  | goto err_engine_rsa_start; | 
|  |  | 
|  | tasklet_init(&acry_dev->done_task, aspeed_acry_done_task, | 
|  | (unsigned long)acry_dev); | 
|  |  | 
|  | /* Set Data Memory to AHB(CPU) Access Mode */ | 
|  | ast_acry_write(acry_dev, ACRY_CMD_DMEM_AHB, ASPEED_ACRY_DMA_CMD); | 
|  |  | 
|  | /* Initialize ACRY SRAM index */ | 
|  | aspeed_acry_sram_mapping(acry_dev); | 
|  |  | 
|  | acry_dev->buf_addr = dmam_alloc_coherent(dev, ASPEED_ACRY_BUFF_SIZE, | 
|  | &acry_dev->buf_dma_addr, | 
|  | GFP_KERNEL); | 
|  | if (!acry_dev->buf_addr) { | 
|  | rc = -ENOMEM; | 
|  | goto err_engine_rsa_start; | 
|  | } | 
|  |  | 
|  | aspeed_acry_register(acry_dev); | 
|  |  | 
|  | dev_info(dev, "Aspeed ACRY Accelerator successfully registered\n"); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err_engine_rsa_start: | 
|  | crypto_engine_exit(acry_dev->crypt_engine_rsa); | 
|  | clk_exit: | 
|  | clk_disable_unprepare(acry_dev->clk); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | static void aspeed_acry_remove(struct platform_device *pdev) | 
|  | { | 
|  | struct aspeed_acry_dev *acry_dev = platform_get_drvdata(pdev); | 
|  |  | 
|  | aspeed_acry_unregister(acry_dev); | 
|  | crypto_engine_exit(acry_dev->crypt_engine_rsa); | 
|  | tasklet_kill(&acry_dev->done_task); | 
|  | clk_disable_unprepare(acry_dev->clk); | 
|  | } | 
|  |  | 
|  | MODULE_DEVICE_TABLE(of, aspeed_acry_of_matches); | 
|  |  | 
|  | static struct platform_driver aspeed_acry_driver = { | 
|  | .probe		= aspeed_acry_probe, | 
|  | .remove_new	= aspeed_acry_remove, | 
|  | .driver		= { | 
|  | .name   = KBUILD_MODNAME, | 
|  | .of_match_table = aspeed_acry_of_matches, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | module_platform_driver(aspeed_acry_driver); | 
|  |  | 
|  | MODULE_AUTHOR("Neal Liu <neal_liu@aspeedtech.com>"); | 
|  | MODULE_DESCRIPTION("ASPEED ACRY driver for hardware RSA Engine"); | 
|  | MODULE_LICENSE("GPL"); |