| // 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"); |