firmware/br-ext-chip-ingenic/board/t40/kernel/patches/00000-drivers_crypto_ingeni...

896 lines
23 KiB
Diff

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 <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/crypto.h>
+#include <linux/interrupt.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/aes.h>
+#include <linux/delay.h>
+#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");