diff -drupN a/drivers/crypto/ingenic-aes.c b/drivers/crypto/ingenic-aes.c --- a/drivers/crypto/ingenic-aes.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/crypto/ingenic-aes.c 2022-06-09 05:02:28.000000000 +0300 @@ -0,0 +1,891 @@ +/* + * Cryptographic API. + * + * Support for Ingenic AES HW acceleration. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ingenic-aes.h" + +/* keep registered devices data here */ +static LIST_HEAD(dev_list); +static DEFINE_SPINLOCK(list_lock); + + +static inline unsigned long aes_read(struct ingenic_aes_dev *aes, unsigned long offset) +{ + return readl(aes->io_base + offset); +} + +static inline void aes_write(struct ingenic_aes_dev *aes, unsigned long offset, + unsigned long value) +{ + writel(value, aes->io_base + offset); +} + +void dump_aes_reg(struct ingenic_aes_dev *aes){ + dev_info(aes->dev,"AES_ASCR: 0x%08lx\n",aes_read(aes,AES_ASCR)); + dev_info(aes->dev,"AES_ASSR: 0x%08lx\n",aes_read(aes,AES_ASSR)); + dev_info(aes->dev,"AES_ASINTM: 0x%08lx\n",aes_read(aes,AES_ASINTM)); + dev_info(aes->dev,"AES_ASSA: 0x%08lx\n",aes_read(aes,AES_ASSA)); + dev_info(aes->dev,"AES_ASDA: 0x%08lx\n",aes_read(aes,AES_ASDA)); + dev_info(aes->dev,"AES_ASTC: 0x%08lx\n",aes_read(aes,AES_ASTC)); +} + +static inline int ingenic_aes_hw_init(struct ingenic_aes_dev *aes) +{ + /* + * clocks are enabled when request starts and disabled when finished. + * It may be long delays between requests. + * Device might go to off mode to save power. + */ + pm_runtime_get_sync(aes->dev); + + if (!(aes->flags & FLAGS_INIT)) { + aes->flags |= FLAGS_INIT; + aes->err = 0; + } + + return 0; +} + +static inline int check_keydone(struct ingenic_aes_dev *aes) +{ + unsigned long timeout = 1000; + while(!(aes_read(aes,AES_ASSR) & (1 << 0)) && --timeout); + if(timeout == 0){ + dev_err(aes->dev,"Write keys is timeout.\n"); + return -1; + } + return 0; +} + +static inline void ingenic_aes_stop(struct ingenic_aes_dev *aes) +{ + unsigned long d; + d = aes_read(aes, AES_ASCR); + d &= ~1; + aes_write(aes, AES_ASCR,d); + aes->flags = 0; + pm_runtime_put_sync(aes->dev); + pr_debug("ingenic aes stop. finished!\n"); +} +static inline int ingenic_aes_write_ctrl(struct ingenic_aes_dev *aes) +{ + unsigned int key32; + int i; + unsigned long val,init_data,mode,alg; + int err; + err = ingenic_aes_hw_init(aes); + if (err){ + dev_err(aes->dev,"aes hw init failed.\n"); + return err; + } + // 1. Mask interrupt and clear interrupt. + aes_write(aes,AES_ASINTM,0); + aes_read(aes,AES_ASSR); + + // 2. AES enable and clear data for new process. + init_data = ASCR_PS_DEF | ASCR_CLR | ASCR_EN; + aes_write(aes,AES_ASCR,init_data); + + // 3. configure AES + + key32 = aes->ctx->keylen / sizeof(unsigned long); + switch(aes->ctx->keylen){ + case AES_KEYSIZE_128: + val = 0; + break; + case AES_KEYSIZE_192: + val = 1; + break; + case AES_KEYSIZE_256: + val = 2; + break; + default: + dev_err(aes->dev,"len error\n"); + return -EINVAL; + } + alg = (aes->flags & FLAGS_CBC) == FLAGS_CBC ? CBC_ALG:ECB_ALG; + mode = (aes->flags & FLAGS_DECRYPT) == FLAGS_DECRYPT ? DECRYPTO_MODE : ENCRYPTO_MODE; + + init_data = ASCR_PS_DEF | ASCR_KEYL(val) | ASCR_ALGS(alg) | ASCR_DECE(mode) | ASCR_EN; + aes_write(aes,AES_ASCR,init_data); + + if (alg == CBC_ALG){ + // Initialize IV of cbc alg + unsigned int *dat = aes->req->info; + if(dat == NULL){ + dev_err(aes->dev,"no set iv data in cbc(aes)\n"); + } + for (i = 0; i < 4; i++) { + aes_write(aes, AES_ASIV,__be32_to_cpu(dat[i])); + /* printk("======== iv:0x%08lx 0x%08x\n",dat[i],__be32_to_cpu(dat[i])); */ + } + init_data = aes_read(aes,AES_ASCR); + aes_write(aes,AES_ASCR,init_data | ASCR_INIT_IV); + } + // Initialize Key. + for (i = 0; i < key32; i++) { + aes_write(aes, AES_ASKY, + __be32_to_cpu(aes->ctx->key[i])); + /* printk("======== 0x%08lx 0x%08x\n",aes->ctx->key[i],__be32_to_cpu(aes->ctx->key[i])); */ + } + + init_data = aes_read(aes,AES_ASCR); + aes_write(aes,AES_ASCR,init_data | ASCR_KEYS); // check key done in dma process. + return 0; +} + +static struct ingenic_aes_dev *ingenic_aes_find_dev(struct ingenic_aes_ctx *ctx) +{ + struct ingenic_aes_dev *aes = NULL, *tmp; + + spin_lock(&list_lock); + if (!ctx->aes) { + list_for_each_entry(tmp, &dev_list, list) { + /* FIXME: take fist available aes core */ + aes = tmp; + break; + } + ctx->aes = aes; + } else { + /* already found before */ + aes = ctx->aes; + } + spin_unlock(&list_lock); + + return aes; +} + +static int ingenic_aes_dma_init(struct ingenic_aes_dev *aes) +{ + int err = -ENOMEM; + aes->buf_in = (void *)__get_free_pages(GFP_KERNEL, INGENIC_AES_CACHE_PAGE_SHIFT); + aes->buf_out = (void *)__get_free_pages(GFP_KERNEL, INGENIC_AES_CACHE_PAGE_SHIFT); + aes->buflen = PAGE_SIZE << INGENIC_AES_CACHE_PAGE_SHIFT; + aes->buflen &= ~(AES_BLOCK_SIZE - 1); + + if (!aes->buf_in || !aes->buf_out) { + dev_err(aes->dev, "unable to alloc pages.\n"); + goto err_alloc; + } + aes->dma_addr_in = dma_map_single(aes->dev, aes->buf_in, aes->buflen, + DMA_TO_DEVICE); + if (dma_mapping_error(aes->dev, aes->dma_addr_in)) { + dev_err(aes->dev, "dma %d bytes error\n", aes->buflen); + err = -EINVAL; + goto err_map_in; + } + + aes->dma_addr_out = dma_map_single(aes->dev, aes->buf_out, aes->buflen, + DMA_FROM_DEVICE); + if (dma_mapping_error(aes->dev, aes->dma_addr_out)) { + dev_err(aes->dev, "dma %d bytes error\n", aes->buflen); + err = -EINVAL; + goto err_map_out; + } + return 0; +err_map_out: + dma_unmap_single(aes->dev, aes->dma_addr_in, aes->buflen, DMA_TO_DEVICE); +err_map_in: + free_pages((unsigned long)aes->buf_out, INGENIC_AES_CACHE_PAGE_SHIFT); + free_pages((unsigned long)aes->buf_in, INGENIC_AES_CACHE_PAGE_SHIFT); +err_alloc: + if (err) + pr_err("error: %d\n", err); + return err; +} + +static void ingenic_aes_dma_cleanup(struct ingenic_aes_dev *aes) +{ + free_pages((unsigned long)aes->buf_out, INGENIC_AES_CACHE_PAGE_SHIFT); + free_pages((unsigned long)aes->buf_in, INGENIC_AES_CACHE_PAGE_SHIFT); +} + +static void sg_copy_buf(void *buf, struct scatterlist *sg, + unsigned int start, unsigned int nbytes, int out) +{ + struct scatter_walk walk; + + if (!nbytes) + return; + + scatterwalk_start(&walk, sg); + scatterwalk_advance(&walk, start); + scatterwalk_copychunks(buf, &walk, nbytes, out); + scatterwalk_done(&walk, out, 0); +} + +static int sg_copy(struct scatterlist **sg, size_t *offset, void *buf, + size_t buflen, size_t total, int out) +{ + unsigned int count, off = 0; + + while (buflen && total) { + count = min((*sg)->length - *offset, total); + count = min(count, buflen); + + if (!count) + return off; + + /* + * buflen and total are AES_BLOCK_SIZE size aligned, + * so count should be also aligned + */ + + sg_copy_buf(buf + off, *sg, *offset, count, out); + + off += count; + buflen -= count; + *offset += count; + total -= count; + + if (*offset == (*sg)->length) { + *sg = sg_next(*sg); + if (*sg) + *offset = 0; + else + total = 0; + } + } + + return off; +} + +void dump_sgdata(struct scatterlist *sg) +{ + int i; + unsigned int *d = sg_virt(sg); + for(i = 0;i < sg->length;i += 4) + { + printk("%5d:0x%08x\n",i,d[i/4]); + } +} + +void prep_sgdata(struct scatterlist *sg) +{ + int i; + unsigned int *d = sg_virt(sg); + unsigned int dat; + for(i = 0;i < sg->length;i += 4) + { + dat = d[i/4]; + d[i/4] = __be32_to_cpu(dat); + } +} + + +static int ingenic_aes_crypt_dma(struct crypto_tfm *tfm, + struct scatterlist *in_sg, struct scatterlist *out_sg) +{ + struct ingenic_aes_ctx *ctx = crypto_tfm_ctx(tfm); + struct ingenic_aes_dev *aes = ctx->aes; + + dma_addr_t dma_addr_in = sg_dma_address(in_sg); + dma_addr_t dma_addr_out = sg_dma_address(out_sg); + int length = sg_dma_len(in_sg); + int blen = 0; + + unsigned int config; +// dev_info(aes->dev,"len: %d\n", length); + if(check_keydone(aes) != 0){ + dev_err(aes->dev,"check key done failed!\n"); + return -EINPROGRESS; + } + + config = aes_read(aes,AES_ASCR); + + aes->dma_size = length; + aes_write(aes,AES_ASSA,dma_addr_in); + aes_write(aes,AES_ASDA,dma_addr_out); + + blen = length / 8; // perblock is 128bit. + + if(length % 8) + return -EINVAL; + + aes_write(aes,AES_ASTC,blen); + aes_write(aes,AES_ASINTM,4); + config |= (ASCR_DMAE | ASCR_DMAS); + aes_write(aes,AES_ASCR,config); + return 0; +} +static int ingenic_aes_crypt_dma_start(struct ingenic_aes_dev *aes) +{ + struct crypto_tfm *tfm = crypto_ablkcipher_tfm( + crypto_ablkcipher_reqtfm(aes->req)); + int err, fast = 0, in, out; + size_t count; + dma_addr_t addr_in, addr_out; + struct scatterlist *in_sg, *out_sg; + int len32; + + dev_dbg(aes->dev,"total: %d\n", aes->total); + + if (sg_is_last(aes->in_sg) && sg_is_last(aes->out_sg)) { + /* check for alignment */ + in = IS_ALIGNED((unsigned long)aes->in_sg->offset, sizeof(unsigned long)); + out = IS_ALIGNED((unsigned long)aes->out_sg->offset, sizeof(unsigned long)); + + fast = in && out; + } + if (fast) { + count = min(aes->total, sg_dma_len(aes->in_sg)); + count = min(count, sg_dma_len(aes->out_sg)); + + if (count != aes->total) { + pr_err("request length != buffer length\n"); + return -EINVAL; + } + + prep_sgdata(aes->in_sg); + //dump_sgdata(aes->in_sg); + + pr_debug("fast\n"); + err = dma_map_sg(aes->dev, aes->in_sg, 1, DMA_TO_DEVICE); + + if (!err) { + dev_err(aes->dev, "dma_map_sg() error\n"); + return -EINVAL; + } + + err = dma_map_sg(aes->dev, aes->out_sg, 1, DMA_FROM_DEVICE); + if (!err) { + dev_err(aes->dev, "dma_map_sg() error\n"); + dma_unmap_sg(aes->dev, aes->in_sg, 1, DMA_TO_DEVICE); + return -EINVAL; + } + + addr_in = sg_dma_address(aes->in_sg); + addr_out = sg_dma_address(aes->out_sg); + + in_sg = aes->in_sg; + out_sg = aes->out_sg; + + aes->flags |= FLAGS_FAST; + + } else { + /* use cache buffers */ + count = sg_copy(&aes->in_sg, &aes->in_offset, aes->buf_in, + aes->buflen, aes->total, 0); + + len32 = DIV_ROUND_UP(count, DMA_MIN) * DMA_MIN; + + /* + * The data going into the AES module has been copied + * to a local buffer and the data coming out will go + * into a local buffer so set up local SG entries for + * both. + */ + + sg_init_one(&aes->in_sgl,aes->buf_in,len32); + sg_dma_len(&aes->in_sgl) = len32; + sg_dma_address(&aes->in_sgl) = aes->dma_addr_in; + sg_init_one(&aes->out_sgl,aes->buf_out,len32); + sg_dma_len(&aes->out_sgl) = len32; + sg_dma_address(&aes->out_sgl) = aes->dma_addr_out; + + in_sg = &aes->in_sgl; + out_sg = &aes->out_sgl; + + addr_in = aes->dma_addr_in; + addr_out = aes->dma_addr_out; + + prep_sgdata(in_sg); + //dump_sgdata(in_sg); + + aes->flags &= ~FLAGS_FAST; + dma_cache_sync(aes->dev, sg_virt(in_sg), len32,DMA_TO_DEVICE); + dma_cache_sync(aes->dev, sg_virt(out_sg),len32,DMA_FROM_DEVICE); + } + + aes->total -= count; + err = ingenic_aes_crypt_dma(tfm, in_sg, out_sg); + if (err) { + dma_unmap_sg(aes->dev, aes->in_sg, 1, DMA_TO_DEVICE); + dma_unmap_sg(aes->dev, aes->out_sg, 1, DMA_TO_DEVICE); + } + + return err; +} + +static void ingenic_aes_finish_req(struct ingenic_aes_dev *aes, int err) +{ + struct ablkcipher_request *req = aes->req; + + pr_debug("err: %d\n", err); + + aes->flags &= ~FLAGS_BUSY; + req->base.complete(&req->base, err); +} + +static int ingenic_aes_crypt_dma_stop(struct ingenic_aes_dev *aes) +{ + int err = 0; + size_t count; + /* unsigned int val; */ + pr_debug("total: %d\n", aes->total); + + /* val = aes_read(aes,AES_ASCR); */ + /* val &= ~(3 << 8); */ + /* aes_write(aes,AES_ASCR,val); */ + + if (aes->flags & FLAGS_FAST) + { + dma_unmap_sg(aes->dev, aes->out_sg, 1, DMA_FROM_DEVICE); + prep_sgdata(aes->out_sg); + //dump_sgdata(aes->out_sg); + dma_unmap_sg(aes->dev, aes->in_sg, 1, DMA_TO_DEVICE); + } else { + dma_sync_single_for_device(aes->dev, aes->dma_addr_out, + aes->dma_size, DMA_FROM_DEVICE); + + prep_sgdata(&aes->out_sgl); + //dump_sgdata(&aes->out_sgl); + + /* copy data */ + count = sg_copy(&aes->out_sg, &aes->out_offset, aes->buf_out, + aes->buflen, aes->dma_size, 1); + + if (count != aes->dma_size) { + err = -EINVAL; + pr_err("not all data converted: %u\n", count); + } + } + return err; +} + +static int ingenic_aes_start(struct ingenic_aes_dev *aes, + struct ablkcipher_request *req) +{ + struct crypto_async_request *async_req, *backlog; + struct ingenic_aes_ctx *ctx; + struct ingenic_aes_reqctx *rctx; + unsigned long flags; + int err, ret = 0; + spin_lock_irqsave(&aes->lock, flags); + if (req) + ret = ablkcipher_enqueue_request(&aes->queue, req); + if (aes->flags & FLAGS_BUSY) { + spin_unlock_irqrestore(&aes->lock, flags); + return ret; + } + backlog = crypto_get_backlog(&aes->queue); + async_req = crypto_dequeue_request(&aes->queue); + if (async_req) + aes->flags |= FLAGS_BUSY; + spin_unlock_irqrestore(&aes->lock, flags); + + if (!async_req){ + ingenic_aes_stop(aes); + return ret; + } + if (backlog) + backlog->complete(backlog, -EINPROGRESS); + req = ablkcipher_request_cast(async_req); + + /* assign new request to device */ + aes->req = req; + aes->total = req->nbytes; + aes->in_offset = 0; + aes->in_sg = req->src; + aes->out_offset = 0; + aes->out_sg = req->dst; + + rctx = ablkcipher_request_ctx(req); + ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); + rctx->mode &= FLAGS_MODE_MASK; + aes->flags = (aes->flags & ~FLAGS_MODE_MASK) | rctx->mode; + + aes->ctx = ctx; + ctx->aes = aes; + + err = ingenic_aes_write_ctrl(aes); + if (!err) + err = ingenic_aes_crypt_dma_start(aes); + + if (err) { + + dev_err(aes->dev,"dma start failed.!\n"); + ingenic_aes_stop(aes); + /* aes_task will not finish it, so do it here */ + ingenic_aes_finish_req(aes, err); + } + return ret; /* return ret, which is enqueue return value */ +} + +static int ingenic_aes_crypt(struct ablkcipher_request *req, unsigned long mode) +{ + struct ingenic_aes_ctx *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + struct ingenic_aes_reqctx *rctx = ablkcipher_request_ctx(req); + struct ingenic_aes_dev *aes; + pr_info("nbytes: %d, enc: %d, cbc: %d\n", req->nbytes, + !(mode & FLAGS_DECRYPT), + !!(mode & FLAGS_CBC)); + + if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE)) { + pr_err("request size is not exact amount of AES blocks\n"); + return -EINVAL; + } + aes = ingenic_aes_find_dev(ctx); + if (!aes) + return -ENODEV; + rctx->mode = mode; + return ingenic_aes_start(aes, req); +} +static irqreturn_t ingenic_aec_irqthread(int irq, void *data) +{ + struct ingenic_aes_dev *aes = (struct ingenic_aes_dev *)data; + unsigned long val; + unsigned long mask; + + val = aes_read(aes,AES_ASSR); + mask = aes_read(aes,AES_ASINTM); + val = val & mask; + + if(val & 4){ + int err; + err = ingenic_aes_crypt_dma_stop(aes); + aes_write(aes,AES_ASSR,4); + err = aes->err ? : err; + if (aes->total && !err) { + err = ingenic_aes_crypt_dma_start(aes); + if (!err) + return IRQ_HANDLED; /* DMA started. Not fininishing. */ + } + ingenic_aes_finish_req(aes, err); + ingenic_aes_start(aes,NULL); + }else if(val & 2){ + aes_write(aes,AES_ASSR,2); + }else if(val & 1){ + aes_write(aes,AES_ASSR,1); + }else{ + dev_err(aes->dev,"unknown irq!!!\n"); + dump_aes_reg(aes); + } + + return IRQ_HANDLED; +} + +/* ********************** ALG API ************************************ */ + +static int ingenic_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct ingenic_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm); + + if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 && + keylen != AES_KEYSIZE_256) + return -EINVAL; + + pr_debug("enter, keylen: %d\n", keylen); + + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + + return 0; +} + +static int ingenic_aes_ecb_encrypt(struct ablkcipher_request *req) +{ + return ingenic_aes_crypt(req, 0); +} + +static int ingenic_aes_ecb_decrypt(struct ablkcipher_request *req) +{ + return ingenic_aes_crypt(req, FLAGS_DECRYPT); +} + +static int ingenic_aes_cbc_encrypt(struct ablkcipher_request *req) +{ + return ingenic_aes_crypt(req, FLAGS_CBC); +} + +static int ingenic_aes_cbc_decrypt(struct ablkcipher_request *req) +{ + return ingenic_aes_crypt(req, FLAGS_DECRYPT | FLAGS_CBC); +} +static int ingenic_aes_cra_init(struct crypto_tfm *tfm) +{ + pr_debug("enter\n"); + tfm->crt_ablkcipher.reqsize = sizeof(struct ingenic_aes_reqctx); + return 0; +} + +static void ingenic_aes_cra_exit(struct crypto_tfm *tfm) +{ + pr_debug("exit\n"); +} + + +/* ********************** ALGS ************************************ */ + +static struct crypto_alg algs_ecb_cbc[] = { + { + .cra_name = "ecb(aes)", + .cra_driver_name = "ecb-aes-ingenic", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct ingenic_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ingenic_aes_cra_init, + .cra_exit = ingenic_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = ingenic_aes_setkey, + .encrypt = ingenic_aes_ecb_encrypt, + .decrypt = ingenic_aes_ecb_decrypt, + } + }, + { + .cra_name = "cbc(aes)", + .cra_driver_name = "cbc-aes-ingenic", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct ingenic_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ingenic_aes_cra_init, + .cra_exit = ingenic_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ingenic_aes_setkey, + .encrypt = ingenic_aes_cbc_encrypt, + .decrypt = ingenic_aes_cbc_decrypt, + } + }, +}; + +static struct ingenic_aes_pdata ingenic_aes_pdata = { + .algs_list = algs_ecb_cbc, + .size = ARRAY_SIZE(algs_ecb_cbc), +}; +static int ingenic_aes_get_res_pdev(struct ingenic_aes_dev *aes, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *r; + int err; + int ret = 0; + /* Get the base address */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(dev, "no MEM resource info\n"); + err = -ENODEV; + goto err; + } + + aes->io_base = devm_ioremap(aes->dev, r->start, resource_size(r)); + if(IS_ERR_OR_NULL(aes->io_base)) { + dev_err(&pdev->dev, "Failed to remap io resources!\n"); + ret = -ENOMEM; + goto err; + } + + aes->irq = platform_get_irq(pdev,0); + if(aes->irq < 0){ + dev_err(dev,"Failed to request irq resource info\n"); + ret = -EBUSY; + goto err; + } + + err = request_threaded_irq(aes->irq,NULL,ingenic_aec_irqthread, + IRQF_ONESHOT,dev_name(dev),aes); + + aes->pdata = &ingenic_aes_pdata; + +err: + return ret; +} +static noinline void pdma_wait(void) +{ + __asm__ volatile ( + " .set push \n\t" + " .set noreorder \n\t" + " .set mips32 \n\t" + " li $26, 0 \n\t" + " mtc0 $26, $12 \n\t" + " nop \n\t" + "1: \n\t" + " wait \n\t" + " b 1b \n\t" + " nop \n\t" + " .set reorder \n\t" + " .set pop \n\t" + ); +} + +static int ingenic_aes_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ingenic_aes_dev *aes; + struct crypto_alg *algp; + int err = -ENOMEM, i; + + aes = devm_kzalloc(dev,sizeof(struct ingenic_aes_dev), GFP_KERNEL); + if (aes == NULL) { + dev_err(dev, "unable to alloc data struct.\n"); + goto err_data; + } + aes->dev = dev; + aes->clk_gate = devm_clk_get(&pdev->dev, "gate_aes"); + if (IS_ERR(aes->clk_gate)) { + dev_err(&pdev->dev, "cannot find RTC clock\n"); + err = PTR_ERR(aes->clk_gate); + goto err_data; + } + + clk_prepare_enable(aes->clk_gate); + aes->clk_gate = devm_clk_get(&pdev->dev, "gate_pdma"); + if (IS_ERR(aes->clk_gate)) { + dev_err(&pdev->dev, "cannot find aes clock\n"); + err = PTR_ERR(aes->clk_gate); + goto err_data; + } + + clk_prepare_enable(aes->clk_gate); + printk("gate = %x\n", *(unsigned long*)0xb0000020); + + platform_set_drvdata(pdev, aes); + + reset_mcu(); + memcpy((void*)MCU_BOOT,pdma_wait,64); + boot_up_mcu(); + + mdelay(100); + /* *(unsigned long*)0xb3421030 = 1; */ + + spin_lock_init(&aes->lock); + crypto_init_queue(&aes->queue, INGENIC_AES_QUEUE_LENGTH); + + err = ingenic_aes_get_res_pdev(aes, pdev); + if (err) + goto err_data; + + err = ingenic_aes_dma_init(aes); + if (err) + goto err_data; + + INIT_LIST_HEAD(&aes->list); + spin_lock(&list_lock); + list_add_tail(&aes->list, &dev_list); + spin_unlock(&list_lock); + for (i = 0; i < aes->pdata->size; i++) { + algp = &aes->pdata->algs_list[i]; + pr_info("reg alg: %s\n", algp->cra_name); + err = crypto_register_alg(algp); + if (err) + goto err_algs; + aes->pdata->registered++; + } + printk("ingenic aes[%d] register ok.",aes->pdata->registered); + return 0; +err_algs: + for (i = aes->pdata->registered - 1; i >= 0; i--) + crypto_unregister_alg( + &aes->pdata->algs_list[i]); + ingenic_aes_dma_cleanup(aes); +err_data: + dev_err(dev, "initialization failed.\n"); + return err; +} + +static int ingenic_aes_remove(struct platform_device *pdev) +{ + struct ingenic_aes_dev *aes = platform_get_drvdata(pdev); + int i; + + if (!aes) + return -ENODEV; + + spin_lock(&list_lock); + list_del(&aes->list); + spin_unlock(&list_lock); + for (i = aes->pdata->registered - 1; i >= 0; i--) + crypto_unregister_alg( + &aes->pdata->algs_list[i]); + aes->pdata->registered = 0; + ingenic_aes_dma_cleanup(aes); + pm_runtime_disable(aes->dev); + kfree(aes); + aes = NULL; + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ingenic_aes_suspend(struct device *dev) +{ + pm_runtime_put_sync(dev); + return 0; +} + +static int ingenic_aes_resume(struct device *dev) +{ + pm_runtime_get_sync(dev); + return 0; +} +#endif + +static const struct dev_pm_ops ingenic_aes_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ingenic_aes_suspend, ingenic_aes_resume) +}; + +static const struct of_device_id ingenic_aes_dt_match[] = { + { .compatible = "ingenic,aes", .data = NULL }, + {}, +}; +MODULE_DEVICE_TABLE(of, aic_dt_match); + + +static struct platform_driver ingenic_aes_driver = { + .probe = ingenic_aes_probe, + .remove = ingenic_aes_remove, + .driver = { + .name = "aes", + .owner = THIS_MODULE, + .pm = &ingenic_aes_pm_ops, + .of_match_table = of_match_ptr(ingenic_aes_dt_match), + }, +}; + +module_platform_driver(ingenic_aes_driver); + +MODULE_DESCRIPTION("Ingenic AES hw acceleration support."); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Stephon.Qiu");