This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM5728: Crypto AES driver questions

Part Number: AM5728


Tool/software: Linux

Hello,

I am working on the compulab board with the AM5728 sitara processor.

I have used TI sdk-linux-03.03.00 which corresponds to kernel 4.4.41.

I have taken as reference the omap_aes_driver.ko. I have compiled the module as loadable module.

This driver is composed of two files omap-aes.c and omap-aes-gcm.c

First of all : if I use the original version of this module, without ANY modifications when I remove the module and re-load I obtain an error :

user@cl-debian-armhf:~$ lsmod | grep omap_aes_driver
omap_aes_driver        19230  0
user@cl-debian-armhf:~$ sudo modprobe -r -v omap_aes_driver
rmmod omap_aes_driver
user@cl-debian-armhf:~$ lsmod | grep omap_aes_driver
user@cl-debian-armhf:~$ sudo modprobe  -v omap_aes_driver
insmod /lib/modules/4.4.41-cl-som-am57x-ti-3.2-patch-by-hand/kernel/drivers/crypto/omap-aes-driver.ko
user@cl-debian-armhf:~$
user@cl-debian-armhf:~$ lsmod | grep omap_aes_driver
omap_aes_driver        19230  0


user@cl-debian-armhf:~$ sudo tail -n 30 /var/log/syslog
May  5 10:21:42 cl-debian-armhf kernel: [  117.342501]  r9:ec886308 r8:13779d1c r7:00000000 r6:eea6d844 r5:bf2e23cc r4:eea6d810
May  5 10:21:42 cl-debian-armhf kernel: [  117.350321] [<c03f98cc>] (__driver_attach) from [<c03f7a60>] (bus_for_each_dev+0x70/0xa4)
May  5 10:21:42 cl-debian-armhf kernel: [  117.358531]  r7:00000000 r6:c03f98cc r5:bf2e23cc r4:00000000
May  5 10:21:42 cl-debian-armhf kernel: [  117.364249] [<c03f79f0>] (bus_for_each_dev) from [<c03f90c4>] (driver_attach+0x24/0x28)
May  5 10:21:42 cl-debian-armhf kernel: [  117.372284]  r6:c096aa70 r5:ed8e7a00 r4:bf2e23cc
May  5 10:21:42 cl-debian-armhf kernel: [  117.376949] [<c03f90a0>] (driver_attach) from [<c03f8d00>] (bus_add_driver+0x1a8/0x220)
May  5 10:21:42 cl-debian-armhf kernel: [  117.384991] [<c03f8b58>] (bus_add_driver) from [<c03fa100>] (driver_register+0x80/0x100)
May  5 10:21:42 cl-debian-armhf kernel: [  117.393112]  r7:ec886d80 r6:c0941760 r5:bf2e5000 r4:bf2e23cc
May  5 10:21:42 cl-debian-armhf kernel: [  117.398827] [<c03fa080>] (driver_register) from [<c03fb178>] (__platform_driver_register+0x48/0x50)
May  5 10:21:42 cl-debian-armhf kernel: [  117.407908]  r5:bf2e5000 r4:c096aa70
May  5 10:21:42 cl-debian-armhf kernel: [  117.411521] [<c03fb130>] (__platform_driver_register) from [<bf2e501c>] (omap_aes_driver_init+0x1c/0x24 [omap_aes_driver])
May  5 10:21:42 cl-debian-armhf kernel: [  117.422608]  r5:bf2e5000 r4:c0941760
May  5 10:21:42 cl-debian-armhf kernel: [  117.426222] [<bf2e5000>] (omap_aes_driver_init [omap_aes_driver]) from [<c000978c>] (do_one_initcall+0x98/0x1e4)
May  5 10:21:42 cl-debian-armhf kernel: [  117.436444] [<c00096f4>] (do_one_initcall) from [<c00d6914>] (do_init_module+0x68/0x38c)
May  5 10:21:42 cl-debian-armhf kernel: [  117.444566]  r10:bf2e2a40 r9:ec886308 r8:13779d1c r7:00000001 r6:ec8863c0 r5:00000001
May  5 10:21:42 cl-debian-armhf kernel: [  117.452467]  r4:bf2e2a40
May  5 10:21:42 cl-debian-armhf kernel: [  117.455021] [<c00d68ac>] (do_init_module) from [<c00a5e8c>] (load_module+0x1e28/0x20d8)
May  5 10:21:42 cl-debian-armhf kernel: [  117.463056]  r6:ec886300 r5:00000001 r4:eca59f44
May  5 10:21:42 cl-debian-armhf kernel: [  117.467718] [<c00a4064>] (load_module) from [<c00a6318>] (SyS_finit_module+0x88/0x98)
May  5 10:21:42 cl-debian-armhf kernel: [  117.475578]  r10:00000000 r9:eca58000 r8:c000fb44 r7:0000017b r6:7f5b4e80 r5:00000003
May  5 10:21:42 cl-debian-armhf kernel: [  117.483480]  r4:00000000
May  5 10:21:42 cl-debian-armhf kernel: [  117.486033] [<c00a6290>] (SyS_finit_module) from [<c000f9a0>] (ret_fast_syscall+0x0/0x34)
May  5 10:21:42 cl-debian-armhf kernel: [  117.494244]  r6:00000000 r5:00040000 r4:bed1e3d0
May  5 10:21:42 cl-debian-armhf kernel: [  117.498937] ---[ end trace 6a80443878e91c62 ]---
May  5 10:21:42 cl-debian-armhf kernel: [  117.503594] omap-aes 4b700000.aes: could not create sysfs device attrs
May  5 10:21:42 cl-debian-armhf kernel: [  117.510189] omap-aes 4b700000.aes: initialization failed.
May  5 10:21:42 cl-debian-armhf kernel: [  117.515656] omap-aes: probe of 4b700000.aes failed with error -17
May  5 10:23:36 cl-debian-armhf systemd-timesyncd[381]: interval/delta/delay/jitter/drift 256s/-0.001s/0.018s/0.001s/+0ppm
May  5 10:23:36 cl-debian-armhf rsyslogd-2007: action 'action 17' suspended, next retry is Fri May  5 10:24:06 2017 [try http://www.rsyslog.com/e/2007 ]
May  5 10:25:08 cl-debian-armhf rsyslogd-2007: action 'action 17' suspended, next retry is Fri May  5 10:25:38 2017 [try http://www.rsyslog.com/e/2007 ]


But this is not a big issue after module modification (copy in /lib/modules ...) because instead of using modprobe, I reboot.


Now the more important issue in my case is using the omap driver in context of FDE within dm-crypt module.

There was a bug in TI SDK version 4.1, even the cbc mode was not working, it has been fixed in 4.4.

Now my goal is to modify/create a new mode by adding operations on input (before encryption/dec) and output (after encryption/dec) buffers/scatterlists.


Instead of explaining all the details, I have created for *test* purpose a dummy mode : adding XOR before and after the ECB encryption with a constant buffer.

The advantage of this mode is that in both ways (enc/dec) we have the same operations (because the XOR cancels out each other).

Normally after an encryption and decryption of a plaintext we have to retrieve the original plaintext.


First try :
I have directly (in order to add less modification) changed the ecb mode.
My goal is not to modify the ecb mode this time but only force non alignment and observe buffers.

Two main modifications are necessary ; first in omap_sg_copy :

Here are the steps :
- we retrieve buf_in from in_sg (scatterlist) ( a question why buf_in and buf_out are not stored in ctx ?)
- we copy buf_in in  ctx->c_buf_in (only for check purpose)
- we store the checksum of buf_in in ctx->check_buf_before_ecb
- we continue the crypto operation

The second most important part is the done task function :

What I added :
- We compute again the checksum of buf_in (to see if it has been altered)
- if checksums are different I try to print sg and buffers to understand what is going wrong
- then we copy buf_out into dd->out_sg (as in the original code)

Luks tests
==========
I create a container with ecb mode just for test purpose :
1) creation of a file container with dd
2) luksFormat
3) luksOpen
4) Mkfs.ext4
5) mount
6) container opened

Here are the results :
First sometimes buf_in cheksum before and after the ecb encryption with co-pro is not the same.
This observation is confirmed when we compare the actual content of c_buf_in and buf_in after the encryption.

BUT I noticed that the container is opened and working.
So what is the problem?
The problem is that if buf_in temporarly does not contain the right plaintext how can I trust further operations on it ? (like a XOR for instance)

Other observation :
I have tried the *test* mode with XOR before and after encryption with two methods :
1) By forcing the copy from sg to buf and working on buffers instead of sg
->  this is not working, it's like if the plaintext was randomly altered.
2) By xoring sg with xor_sg function, I get the same result as 1)


I hope I have been clear enough to help you understand the problem.

I attach :

- the original code : omap-aes.c/h

- the patch for ecb mode modification : ecb_buf_comp_patch.txt 

- the assicated syslog : log_ecb_buf_comp.txt

- the log of module reload error : error_at_omap_driver_reload.txt

 log_ecb_buf_comp.txt

diff -u original/omap-aes.c ecb/omap-aes.c
--- original/omap-aes.c	2017-05-04 11:55:41.607498930 +0200
+++ ecb/omap-aes.c	2017-05-05 14:16:59.405794002 +0200
@@ -46,6 +46,144 @@
 static DEFINE_SPINLOCK(list_lock);
 
 static int aes_fallback_sz = 200;
+static unsigned long number_of_error =0;
+
+void omap_crypto_dump_sg(const char *name, struct scatterlist *sg, int len)
+{
+       void *buf;
+       struct scatterlist *tmp;
+       int i = 0;
+       int calc_len = 0;
+
+       if (len > 0)
+               buf = kzalloc(len, GFP_KERNEL);
+       else
+               buf = kzalloc(2048, GFP_KERNEL);
+       if (!buf)
+               return;
+
+       tmp = sg;
+
+       pr_info("%s: name=%s, sg=%p, len=%d\n", __func__, name, sg, len);
+
+       while (tmp) {
+               pr_info("%s: i=%d, page=%p, len=%d, offset=%d\n", __func__,
+                       i, sg_page(tmp), tmp->length, tmp->offset);
+               if (!len)
+                       calc_len += tmp->length;
+               tmp = sg_next(tmp);
+               i++;
+       }
+
+       if (!len)
+               len = calc_len;
+
+       scatterwalk_map_and_copy(buf, sg, 0, len, 0);
+       print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET, 16, 1,
+                      buf, len, false);
+
+       kfree(buf);
+}
+
+
+static void xor_buffer_32 (unsigned char * buf_io, unsigned char * buf_to_xor, unsigned len)
+{
+	uint32_t *ptr_io_32;
+	uint32_t *ptr_to_xor_32;
+	unsigned len_32 = len >> 2;
+	unsigned i;
+
+	ptr_io_32 = (uint32_t*) buf_io;
+	ptr_to_xor_32 = (uint32_t*) buf_to_xor;
+
+	for (i = 0; i < len_32; ++i, ++ptr_io_32, ++ptr_to_xor_32)
+	{
+		*ptr_io_32 ^= *ptr_to_xor_32;
+	}
+}
+
+
+static void xor_sg(struct scatterlist * sg_io, unsigned char * buf_to_xor, unsigned len)
+{
+	struct sg_mapping_iter miter;
+	unsigned int sg_flags = SG_MITER_ATOMIC | SG_MITER_FROM_SG;
+	unsigned int nents = sg_nents(sg_io);
+	unsigned long flags;
+	unsigned int actual_len;
+	unsigned int offset = 0;
+
+	sg_miter_start(&miter, sg_io, nents, sg_flags);
+
+	local_irq_save(flags);
+
+	while(sg_miter_next(&miter) && offset < len)
+	{
+		actual_len = min(miter.length, (unsigned int)buf_to_xor - offset);
+
+		xor_buffer_32(miter.addr, &buf_to_xor[offset], actual_len);
+
+		offset += actual_len;
+	}
+
+	sg_miter_stop(&miter);
+	local_irq_restore(flags);
+}
+
+
+unsigned long comp_checksum (unsigned char * buf, int len)
+{
+	u32 * ptr_32;
+	unsigned len_32 = len >> 2;
+	unsigned i;
+	u32 sum = 0; // because its an uint32_t, the modulo will be automatic
+
+	ptr_32 = (u32 *) buf;
+
+	for (i = 0; i < len_32; ++i, ++ptr_32)
+	{
+		sum += *ptr_32;
+	}
+
+	return sum;
+}
+
+unsigned char tweak_buf[512]={
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x05,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x07,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x09,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x05,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x07,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x09,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+			0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,
+	};
+
+
+
 
 #ifdef DEBUG
 #define omap_aes_read(dd, offset)				\
@@ -446,7 +584,10 @@
 static int omap_aes_copy_sgs(struct omap_aes_dev *dd)
 {
 	void *buf_in, *buf_out;
-	int pages, total;
+	int pages, total,j,i;
+	unsigned char ecb_soft[512];
+	struct omap_aes_ctx *ctx = crypto_ablkcipher_ctx(
+						crypto_ablkcipher_reqtfm(dd->req));
 
 	total = ALIGN(dd->total, AES_BLOCK_SIZE);
 	pages = get_order(total);
@@ -458,10 +599,21 @@
 		pr_err("Couldn't allocated pages for unaligned cases.\n");
 		return -1;
 	}
-
 	dd->orig_out = dd->out_sg;
 
 	sg_copy_buf(buf_in, dd->in_sg, 0, dd->total, 0);
+	if(dd->total !=512)
+		printk("dd->total = %d \n",dd->total);
+
+	/*
+	 * We compute the checksum of buf_in
+	 * */
+	ctx->check_buf_before_ecb	= comp_checksum(buf_in,dd->total);
+
+	/*
+	 * We store this  buf_in inside the ctx
+	 * */
+	memcpy(ctx->c_buf_in,buf_in,dd->total);
 
 	sg_init_table(dd->in_sgl, 1);
 	sg_set_buf(dd->in_sgl, buf_in, total);
@@ -511,25 +663,25 @@
 	dd->in_sg = req->src;
 	dd->out_sg = req->dst;
 
-	if (omap_aes_copy_needed(dd->in_sg, dd->total) ||
-	    omap_aes_copy_needed(dd->out_sg, dd->total)) {
-		if (omap_aes_copy_sgs(dd))
-			pr_err("Failed to copy SGs for unaligned cases\n");
-		dd->sgs_copied = 1;
-	} else {
-		dd->sgs_copied = 0;
-	}
 
-	len = ALIGN(dd->total, AES_BLOCK_SIZE);
-	dd->in_sg_len = scatterwalk_bytes_sglen(dd->in_sg, len);
-	dd->out_sg_len = scatterwalk_bytes_sglen(dd->out_sg, len);
-	BUG_ON(dd->in_sg_len < 0 || dd->out_sg_len < 0);
+
 
 	rctx = ablkcipher_request_ctx(req);
 	ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
 	rctx->mode &= FLAGS_MODE_MASK;
 	dd->flags = (dd->flags & ~FLAGS_MODE_MASK) | rctx->mode;
 
+
+	if (omap_aes_copy_sgs(dd))
+		pr_err("Failed to copy SGs for unaligned cases\n");
+	dd->sgs_copied = 1;
+
+	len = ALIGN(dd->total, AES_BLOCK_SIZE);
+	dd->in_sg_len = scatterwalk_bytes_sglen(dd->in_sg, len);
+	dd->out_sg_len = scatterwalk_bytes_sglen(dd->out_sg, len);
+	BUG_ON(dd->in_sg_len < 0 || dd->out_sg_len < 0);
+
+
 	dd->ctx = ctx;
 	rctx->dd = dd;
 
@@ -550,7 +702,7 @@
 {
 	struct omap_aes_dev *dd = (struct omap_aes_dev *)data;
 	void *buf_in, *buf_out;
-	int pages, len;
+	int pages, len,j;
 
 	pr_debug("enter done_task\n");
 
@@ -559,7 +711,7 @@
 				       DMA_FROM_DEVICE);
 		dma_unmap_sg(dd->dev, dd->in_sg, dd->in_sg_len, DMA_TO_DEVICE);
 		dma_unmap_sg(dd->dev, dd->out_sg, dd->out_sg_len,
-			     DMA_FROM_DEVICE);
+				DMA_FROM_DEVICE);
 		omap_aes_crypt_dma_stop(dd);
 	}
 
@@ -567,8 +719,27 @@
 		buf_in = sg_virt(dd->in_sgl);
 		buf_out = sg_virt(&dd->out_sgl);
 
-		sg_copy_buf(buf_out, dd->orig_out, 0, dd->total_save, 1);
+		if(number_of_error < 5){
+			/*
+			 * We compute the checksum of :
+			 * buf_in (to compare if it has changed)
+			 * */
+			dd->ctx->check_buf_after_ecb = comp_checksum(buf_in,dd->total);
+
+			if(dd->ctx->check_buf_before_ecb  != dd->ctx->check_buf_after_ecb ){
+				number_of_error++;
+				printk("buf_in before ecb with co-pro: %lu and  buf_in after : %lu \n",dd->ctx->check_buf_before_ecb,dd->ctx->check_buf_after_ecb );
+				print_hex_dump(KERN_CONT, "buf_in_after_ecb", DUMP_PREFIX_OFFSET, 16, 1,
+						buf_in, dd->total, false);
+				print_hex_dump(KERN_CONT, "buf_in_in_ctx", DUMP_PREFIX_OFFSET, 16, 1,
+						dd->ctx->c_buf_in, dd->total, false);
+				omap_crypto_dump_sg("sg_in_after_ecb",dd->in_sgl,dd->total);
+				print_hex_dump(KERN_CONT, "buf_out", DUMP_PREFIX_OFFSET, 16, 1,
+						buf_out, dd->total, false);
+			}
+		}
 
+		sg_copy_buf(buf_out, dd->orig_out, 0, dd->total_save, 1);
 		len = ALIGN(dd->total_save, AES_BLOCK_SIZE);
 		pages = get_order(len);
 		free_pages((unsigned long)buf_in, pages);
diff -u original/omap-aes.h ecb/omap-aes.h
--- original/omap-aes.h	2017-05-04 11:55:45.060446780 +0200
+++ ecb/omap-aes.h	2017-05-05 14:12:04.757987534 +0200
@@ -93,6 +93,9 @@
 	u32		key[AES_KEYSIZE_256 / sizeof(u32)];
 	u8		nonce[4];
 	unsigned long	flags;
+	unsigned char c_buf_in[512]; /* buf to check buf_in */
+	unsigned long check_buf_before_ecb;
+	unsigned long check_buf_after_ecb;
 	struct crypto_skcipher *ctr;
 	struct crypto_ablkcipher	*fallback;
 };
omap-aes.h
/*
 * Cryptographic API.
 *
 * Support for OMAP AES HW acceleration.
 *
 * Copyright (c) 2010 Nokia Corporation
 * Author: Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
 * Copyright (c) 2011 Texas Instruments Incorporated
 *
 * 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.
 *
 */

#define pr_fmt(fmt) "%20s: " fmt, __func__
#define prn(num) pr_debug(#num "=%d\n", num)
#define prx(num) pr_debug(#num "=%x\n", num)

#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.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 <crypto/skcipher.h>
#include <crypto/internal/aead.h>
#include "omap-aes.h"

#define DEFAULT_AUTOSUSPEND_DELAY	1000

/* keep registered devices data here */
static LIST_HEAD(dev_list);
static DEFINE_SPINLOCK(list_lock);

static int aes_fallback_sz = 200;

#ifdef DEBUG
#define omap_aes_read(dd, offset)				\
({								\
	int _read_ret;						\
	_read_ret = __raw_readl(dd->io_base + offset);		\
	pr_debug("omap_aes_read(" #offset "=%#x)= %#x\n",	\
		 offset, _read_ret);				\
	_read_ret;						\
})
#else
inline u32 omap_aes_read(struct omap_aes_dev *dd, u32 offset)
{
	return __raw_readl(dd->io_base + offset);
}
#endif

#ifdef DEBUG
#define omap_aes_write(dd, offset, value)				\
	do {								\
		pr_debug("omap_aes_write(" #offset "=%#x) value=%#x\n",	\
			 offset, value);				\
		__raw_writel(value, dd->io_base + offset);		\
	} while (0)
#else
inline void omap_aes_write(struct omap_aes_dev *dd, u32 offset,
				  u32 value)
{
	__raw_writel(value, dd->io_base + offset);
}
#endif

static inline void omap_aes_write_mask(struct omap_aes_dev *dd, u32 offset,
					u32 value, u32 mask)
{
	u32 val;

	val = omap_aes_read(dd, offset);
	val &= ~mask;
	val |= value;
	omap_aes_write(dd, offset, val);
}

static void omap_aes_write_n(struct omap_aes_dev *dd, u32 offset,
					u32 *value, int count)
{
	for (; count--; value++, offset += 4)
		omap_aes_write(dd, offset, *value);
}

static int omap_aes_hw_init(struct omap_aes_dev *dd)
{
	int err;

	if (!(dd->flags & FLAGS_INIT)) {
		dd->flags |= FLAGS_INIT;
		dd->err = 0;
	}

	err = pm_runtime_get_sync(dd->dev);
	if (err < 0) {
		dev_err(dd->dev, "failed to get sync: %d\n", err);
		return err;
	}

	return 0;
}

int omap_aes_write_ctrl(struct omap_aes_dev *dd)
{
	struct omap_aes_reqctx *rctx;
	unsigned int key32;
	int i, err;
	u32 val;

	err = omap_aes_hw_init(dd);
	if (err)
		return err;

	key32 = dd->ctx->keylen / sizeof(u32);

	/* RESET the key as previous HASH keys should not get affected*/
	if (dd->flags & FLAGS_GCM)
		for (i = 0; i < 0x40; i = i + 4)
			omap_aes_write(dd, i, 0x0);

	for (i = 0; i < key32; i++) {
		omap_aes_write(dd, AES_REG_KEY(dd, i),
			__le32_to_cpu(dd->ctx->key[i]));
	}

	if ((dd->flags & (FLAGS_CBC | FLAGS_CTR)) && dd->req->info)
		omap_aes_write_n(dd, AES_REG_IV(dd, 0), dd->req->info, 4);

	if ((dd->flags & (FLAGS_GCM)) && dd->aead_req->iv) {
		rctx = aead_request_ctx(dd->aead_req);
		omap_aes_write_n(dd, AES_REG_IV(dd, 0), (u32 *)rctx->iv, 4);
	}

	val = FLD_VAL(((dd->ctx->keylen >> 3) - 1), 4, 3);
	if (dd->flags & FLAGS_CBC)
		val |= AES_REG_CTRL_CBC;

	if (dd->flags & (FLAGS_CTR | FLAGS_GCM))
		val |= AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_128;

	if (dd->flags & FLAGS_GCM)
		val |= AES_REG_CTRL_GCM;

	if (dd->flags & FLAGS_ENCRYPT)
		val |= AES_REG_CTRL_DIRECTION;

	omap_aes_write_mask(dd, AES_REG_CTRL(dd), val, AES_REG_CTRL_MASK);

	return 0;
}

static void omap_aes_dma_trigger_omap2(struct omap_aes_dev *dd, int length)
{
	u32 mask, val;

	val = dd->pdata->dma_start;

	if (dd->dma_lch_out != NULL)
		val |= dd->pdata->dma_enable_out;
	if (dd->dma_lch_in != NULL)
		val |= dd->pdata->dma_enable_in;

	mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in |
	       dd->pdata->dma_start;

	omap_aes_write_mask(dd, AES_REG_MASK(dd), val, mask);

}

static void omap_aes_dma_trigger_omap4(struct omap_aes_dev *dd, int length)
{
	omap_aes_write(dd, AES_REG_LENGTH_N(0), length);
	omap_aes_write(dd, AES_REG_LENGTH_N(1), 0);
	if (dd->flags & FLAGS_GCM)
		omap_aes_write(dd, AES_REG_A_LEN, dd->assoc_len);

	omap_aes_dma_trigger_omap2(dd, length);
}

static void omap_aes_dma_stop(struct omap_aes_dev *dd)
{
	u32 mask;

	mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in |
	       dd->pdata->dma_start;

	omap_aes_write_mask(dd, AES_REG_MASK(dd), 0, mask);
}

struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_reqctx *rctx)
{
	struct omap_aes_dev *dd;

	spin_lock_bh(&list_lock);
	dd = list_first_entry(&dev_list, struct omap_aes_dev, list);
	list_move_tail(&dd->list, &dev_list);
	rctx->dd = dd;
	spin_unlock_bh(&list_lock);

	return dd;
}

static void omap_aes_dma_out_callback(void *data)
{
	struct omap_aes_dev *dd = data;

	/* dma_lch_out - completed */
	tasklet_schedule(&dd->done_task);
}

static int omap_aes_dma_init(struct omap_aes_dev *dd)
{
	int err;

	dd->dma_lch_out = NULL;
	dd->dma_lch_in = NULL;

	dd->dma_lch_in = dma_request_chan(dd->dev, "rx");
	if (IS_ERR(dd->dma_lch_in)) {
		dev_err(dd->dev, "Unable to request in DMA channel\n");
		return PTR_ERR(dd->dma_lch_in);
	}

	dd->dma_lch_out = dma_request_chan(dd->dev, "tx");
	if (IS_ERR(dd->dma_lch_out)) {
		dev_err(dd->dev, "Unable to request out DMA channel\n");
		err = PTR_ERR(dd->dma_lch_out);
		goto err_dma_out;
	}

	return 0;

err_dma_out:
	dma_release_channel(dd->dma_lch_in);

	return err;
}

static void omap_aes_dma_cleanup(struct omap_aes_dev *dd)
{
	if (dd->pio_only)
		return;

	dma_release_channel(dd->dma_lch_out);
	dma_release_channel(dd->dma_lch_in);
}

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 omap_aes_crypt_dma(struct omap_aes_dev *dd,
			      struct scatterlist *in_sg,
			      struct scatterlist *out_sg,
			      int in_sg_len, int out_sg_len)
{
	struct dma_async_tx_descriptor *tx_in, *tx_out;
	struct dma_slave_config cfg;
	int ret;

	if (dd->pio_only) {
		scatterwalk_start(&dd->in_walk, dd->in_sg);
		scatterwalk_start(&dd->out_walk, dd->out_sg);

		/* Enable DATAIN interrupt and let it take
		   care of the rest */
		omap_aes_write(dd, AES_REG_IRQ_ENABLE(dd), 0x2);
		return 0;
	}

	dma_sync_sg_for_device(dd->dev, dd->in_sg, in_sg_len, DMA_TO_DEVICE);

	memset(&cfg, 0, sizeof(cfg));

	cfg.src_addr = dd->phys_base + AES_REG_DATA_N(dd, 0);
	cfg.dst_addr = dd->phys_base + AES_REG_DATA_N(dd, 0);
	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
	cfg.src_maxburst = DST_MAXBURST;
	cfg.dst_maxburst = DST_MAXBURST;

	/* IN */
	ret = dmaengine_slave_config(dd->dma_lch_in, &cfg);
	if (ret) {
		dev_err(dd->dev, "can't configure IN dmaengine slave: %d\n",
			ret);
		return ret;
	}

	tx_in = dmaengine_prep_slave_sg(dd->dma_lch_in, in_sg, in_sg_len,
					DMA_MEM_TO_DEV,
					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
	if (!tx_in) {
		dev_err(dd->dev, "IN prep_slave_sg() failed\n");
		return -EINVAL;
	}

	/* No callback necessary */
	tx_in->callback_param = dd;

	/* OUT */
	ret = dmaengine_slave_config(dd->dma_lch_out, &cfg);
	if (ret) {
		dev_err(dd->dev, "can't configure OUT dmaengine slave: %d\n",
			ret);
		return ret;
	}

	tx_out = dmaengine_prep_slave_sg(dd->dma_lch_out, out_sg, out_sg_len,
					DMA_DEV_TO_MEM,
					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
	if (!tx_out) {
		dev_err(dd->dev, "OUT prep_slave_sg() failed\n");
		return -EINVAL;
	}

	if (dd->flags & FLAGS_GCM)
		tx_out->callback = omap_aes_gcm_dma_out_callback;
	else
		tx_out->callback = omap_aes_dma_out_callback;
	tx_out->callback_param = dd;

	dmaengine_submit(tx_in);
	dmaengine_submit(tx_out);

	dma_async_issue_pending(dd->dma_lch_in);
	dma_async_issue_pending(dd->dma_lch_out);

	/* start DMA */
	dd->pdata->trigger(dd, dd->total);

	return 0;
}

int omap_aes_crypt_dma_start(struct omap_aes_dev *dd)
{
	int err;

	pr_debug("total: %d\n", dd->total);

	if (!dd->pio_only) {
		err = dma_map_sg(dd->dev, dd->in_sg, dd->in_sg_len,
				 DMA_TO_DEVICE);
		if (!err) {
			dev_err(dd->dev, "dma_map_sg() error\n");
			return -EINVAL;
		}

		err = dma_map_sg(dd->dev, dd->out_sg, dd->out_sg_len,
				 DMA_FROM_DEVICE);
		if (!err) {
			dev_err(dd->dev, "dma_map_sg() error\n");
			return -EINVAL;
		}
	}

	err = omap_aes_crypt_dma(dd, dd->in_sg, dd->out_sg, dd->in_sg_len,
				 dd->out_sg_len);
	if (err && !dd->pio_only) {
		dma_unmap_sg(dd->dev, dd->in_sg, dd->in_sg_len, DMA_TO_DEVICE);
		dma_unmap_sg(dd->dev, dd->out_sg, dd->out_sg_len,
			     DMA_FROM_DEVICE);
	}

	return err;
}

static void omap_aes_finish_req(struct omap_aes_dev *dd, int err)
{
	struct ablkcipher_request *req = dd->req;

	pr_debug("err: %d\n", err);

	dd->flags &= ~FLAGS_BUSY;

	req->base.complete(&req->base, err);

	pm_runtime_mark_last_busy(dd->dev);
	pm_runtime_put_autosuspend(dd->dev);
}

int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd)
{
	pr_debug("total: %d\n", dd->total);

	omap_aes_dma_stop(dd);


	return 0;
}

bool omap_aes_copy_needed(struct scatterlist *sg, int total)
{
	int len = 0;

	if (!IS_ALIGNED(total, AES_BLOCK_SIZE))
		return true;

	while (sg) {
		if (!IS_ALIGNED(sg->offset, 4))
			return true;
		if (!IS_ALIGNED(sg->length, AES_BLOCK_SIZE))
			return true;
#ifdef CONFIG_ZONE_DMA
		if (page_zonenum(sg_page(sg)) != ZONE_DMA)
			return true;
#endif

		len += sg->length;
		sg = sg_next(sg);

		if (len >= total)
			break;
	}

	if (len != total)
		return true;

	return false;
}

static int omap_aes_copy_sgs(struct omap_aes_dev *dd)
{
	void *buf_in, *buf_out;
	int pages, total;

	total = ALIGN(dd->total, AES_BLOCK_SIZE);
	pages = get_order(total);

	buf_in = (void *)__get_free_pages(GFP_ATOMIC, pages);
	buf_out = (void *)__get_free_pages(GFP_ATOMIC, pages);

	if (!buf_in || !buf_out) {
		pr_err("Couldn't allocated pages for unaligned cases.\n");
		return -1;
	}

	dd->orig_out = dd->out_sg;

	sg_copy_buf(buf_in, dd->in_sg, 0, dd->total, 0);

	sg_init_table(dd->in_sgl, 1);
	sg_set_buf(dd->in_sgl, buf_in, total);
	dd->in_sg = dd->in_sgl;

	sg_init_table(&dd->out_sgl, 1);
	sg_set_buf(&dd->out_sgl, buf_out, total);
	dd->out_sg = &dd->out_sgl;

	return 0;
}

static int omap_aes_handle_queue(struct omap_aes_dev *dd,
			       struct ablkcipher_request *req)
{
	struct crypto_async_request *async_req, *backlog;
	struct omap_aes_ctx *ctx;
	struct omap_aes_reqctx *rctx;
	unsigned long flags;
	int err, ret = 0, len;

	spin_lock_irqsave(&dd->lock, flags);
	if (req)
		ret = ablkcipher_enqueue_request(&dd->queue, req);
	if (dd->flags & FLAGS_BUSY) {
		spin_unlock_irqrestore(&dd->lock, flags);
		return ret;
	}
	backlog = crypto_get_backlog(&dd->queue);
	async_req = crypto_dequeue_request(&dd->queue);
	if (async_req)
		dd->flags |= FLAGS_BUSY;
	spin_unlock_irqrestore(&dd->lock, flags);

	if (!async_req)
		return ret;

	if (backlog)
		backlog->complete(backlog, -EINPROGRESS);

	req = ablkcipher_request_cast(async_req);

	/* assign new request to device */
	dd->req = req;
	dd->total = req->nbytes;
	dd->total_save = req->nbytes;
	dd->in_sg = req->src;
	dd->out_sg = req->dst;

	if (omap_aes_copy_needed(dd->in_sg, dd->total) ||
	    omap_aes_copy_needed(dd->out_sg, dd->total)) {
		if (omap_aes_copy_sgs(dd))
			pr_err("Failed to copy SGs for unaligned cases\n");
		dd->sgs_copied = 1;
	} else {
		dd->sgs_copied = 0;
	}

	len = ALIGN(dd->total, AES_BLOCK_SIZE);
	dd->in_sg_len = scatterwalk_bytes_sglen(dd->in_sg, len);
	dd->out_sg_len = scatterwalk_bytes_sglen(dd->out_sg, len);
	BUG_ON(dd->in_sg_len < 0 || dd->out_sg_len < 0);

	rctx = ablkcipher_request_ctx(req);
	ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
	rctx->mode &= FLAGS_MODE_MASK;
	dd->flags = (dd->flags & ~FLAGS_MODE_MASK) | rctx->mode;

	dd->ctx = ctx;
	rctx->dd = dd;

	err = omap_aes_write_ctrl(dd);

	if (!err)
		err = omap_aes_crypt_dma_start(dd);
	if (err) {
		/* aes_task will not finish it, so do it here */
		omap_aes_finish_req(dd, err);
		tasklet_schedule(&dd->queue_task);
	}

	return ret; /* return ret, which is enqueue return value */
}

static void omap_aes_done_task(unsigned long data)
{
	struct omap_aes_dev *dd = (struct omap_aes_dev *)data;
	void *buf_in, *buf_out;
	int pages, len;

	pr_debug("enter done_task\n");

	if (!dd->pio_only) {
		dma_sync_sg_for_device(dd->dev, dd->out_sg, dd->out_sg_len,
				       DMA_FROM_DEVICE);
		dma_unmap_sg(dd->dev, dd->in_sg, dd->in_sg_len, DMA_TO_DEVICE);
		dma_unmap_sg(dd->dev, dd->out_sg, dd->out_sg_len,
			     DMA_FROM_DEVICE);
		omap_aes_crypt_dma_stop(dd);
	}

	if (dd->sgs_copied) {
		buf_in = sg_virt(dd->in_sgl);
		buf_out = sg_virt(&dd->out_sgl);

		sg_copy_buf(buf_out, dd->orig_out, 0, dd->total_save, 1);

		len = ALIGN(dd->total_save, AES_BLOCK_SIZE);
		pages = get_order(len);
		free_pages((unsigned long)buf_in, pages);
		free_pages((unsigned long)buf_out, pages);
	}

	omap_aes_finish_req(dd, 0);
	omap_aes_handle_queue(dd, NULL);

	pr_debug("exit\n");
}

static void omap_aes_queue_task(unsigned long data)
{
	struct omap_aes_dev *dd = (struct omap_aes_dev *)data;

	omap_aes_handle_queue(dd, NULL);
}

static int omap_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
{
	struct crypto_tfm *tfm =
		crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req));
	struct omap_aes_ctx *ctx = crypto_ablkcipher_ctx(
			crypto_ablkcipher_reqtfm(req));
	struct omap_aes_reqctx *rctx = ablkcipher_request_ctx(req);
	struct omap_aes_dev *dd;
	int ret;

	pr_debug("nbytes: %d, enc: %d, cbc: %d\n", req->nbytes,
		  !!(mode & FLAGS_ENCRYPT),
		  !!(mode & FLAGS_CBC));

	if (req->nbytes < aes_fallback_sz) {
		ablkcipher_request_set_tfm(req, ctx->fallback);

		if (mode & FLAGS_ENCRYPT)
			ret = crypto_ablkcipher_encrypt(req);
		else
			ret = crypto_ablkcipher_decrypt(req);
		ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm));
		return ret;
	}
	dd = omap_aes_find_dev(rctx);
	if (!dd)
		return -ENODEV;

	rctx->mode = mode;

	return omap_aes_handle_queue(dd, req);
}

/* ********************** ALG API ************************************ */

static int omap_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
			   unsigned int keylen)
{
	struct omap_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
	int ret;

	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;

	ctx->fallback->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
	ctx->fallback->base.crt_flags |=
		tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK;

	ret = crypto_ablkcipher_setkey(ctx->fallback, key, keylen);
	if (!ret)
		return 0;

	return 0;
}

static int omap_aes_ecb_encrypt(struct ablkcipher_request *req)
{
	return omap_aes_crypt(req, FLAGS_ENCRYPT);
}

static int omap_aes_ecb_decrypt(struct ablkcipher_request *req)
{
	return omap_aes_crypt(req, 0);
}

static int omap_aes_cbc_encrypt(struct ablkcipher_request *req)
{
	return omap_aes_crypt(req, FLAGS_ENCRYPT | FLAGS_CBC);
}

static int omap_aes_cbc_decrypt(struct ablkcipher_request *req)
{
	return omap_aes_crypt(req, FLAGS_CBC);
}

static int omap_aes_ctr_encrypt(struct ablkcipher_request *req)
{
	return omap_aes_crypt(req, FLAGS_ENCRYPT | FLAGS_CTR);
}

static int omap_aes_ctr_decrypt(struct ablkcipher_request *req)
{
	return omap_aes_crypt(req, FLAGS_CTR);
}

static int omap_aes_cra_init(struct crypto_tfm *tfm)
{
	const char *name = crypto_tfm_alg_name(tfm);
	const u32 flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK;
	struct omap_aes_ctx *ctx = crypto_tfm_ctx(tfm);
	struct crypto_ablkcipher *blk;

	blk = crypto_alloc_ablkcipher(name, 0, flags);
	if (IS_ERR(blk))
		return PTR_ERR(blk);

	ctx->fallback = blk;

	tfm->crt_ablkcipher.reqsize = sizeof(struct omap_aes_reqctx);

	return 0;
}

static int omap_aes_gcm_cra_init(struct crypto_aead *tfm)
{
	struct omap_aes_ctx *ctx = crypto_aead_ctx(tfm);

	tfm->reqsize = sizeof(struct omap_aes_reqctx);
	ctx->ctr = crypto_alloc_skcipher("ecb(aes)", 0, 0);
	if (IS_ERR(ctx->ctr)) {
		pr_warn("could not load aes driver for encrypting IV\n");
		return PTR_ERR(ctx->ctr);
	}

	return 0;
}

static void omap_aes_cra_exit(struct crypto_tfm *tfm)
{
	struct omap_aes_ctx *ctx = crypto_tfm_ctx(tfm);

	if (ctx->fallback)
		crypto_free_ablkcipher(ctx->fallback);

	ctx->fallback = NULL;
}

static void omap_aes_gcm_cra_exit(struct crypto_aead *tfm)
{
	struct omap_aes_ctx *ctx = crypto_aead_ctx(tfm);

	omap_aes_cra_exit(crypto_aead_tfm(tfm));

	if (ctx->ctr)
		crypto_free_skcipher(ctx->ctr);
}

/* ********************** ALGS ************************************ */

static struct crypto_alg algs_ecb_cbc[] = {
{
	.cra_name		= "ecb(aes)",
	.cra_driver_name	= "ecb-aes-omap",
	.cra_priority		= 300,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER |
				  CRYPTO_ALG_KERN_DRIVER_ONLY |
				  CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
	.cra_blocksize		= AES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct omap_aes_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= omap_aes_cra_init,
	.cra_exit		= omap_aes_cra_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= AES_MIN_KEY_SIZE,
		.max_keysize	= AES_MAX_KEY_SIZE,
		.setkey		= omap_aes_setkey,
		.encrypt	= omap_aes_ecb_encrypt,
		.decrypt	= omap_aes_ecb_decrypt,
	}
},
{
	.cra_name		= "cbc(aes)",
	.cra_driver_name	= "cbc-aes-omap",
	.cra_priority		= 300,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER |
				  CRYPTO_ALG_KERN_DRIVER_ONLY |
				  CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
	.cra_blocksize		= AES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct omap_aes_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= omap_aes_cra_init,
	.cra_exit		= omap_aes_cra_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= AES_MIN_KEY_SIZE,
		.max_keysize	= AES_MAX_KEY_SIZE,
		.ivsize		= AES_BLOCK_SIZE,
		.setkey		= omap_aes_setkey,
		.encrypt	= omap_aes_cbc_encrypt,
		.decrypt	= omap_aes_cbc_decrypt,
	}
}
};

static struct crypto_alg algs_ctr[] = {
{
	.cra_name		= "ctr(aes)",
	.cra_driver_name	= "ctr-aes-omap",
	.cra_priority		= 300,
	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER |
				  CRYPTO_ALG_KERN_DRIVER_ONLY |
				  CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
	.cra_blocksize		= AES_BLOCK_SIZE,
	.cra_ctxsize		= sizeof(struct omap_aes_ctx),
	.cra_alignmask		= 0,
	.cra_type		= &crypto_ablkcipher_type,
	.cra_module		= THIS_MODULE,
	.cra_init		= omap_aes_cra_init,
	.cra_exit		= omap_aes_cra_exit,
	.cra_u.ablkcipher = {
		.min_keysize	= AES_MIN_KEY_SIZE,
		.max_keysize	= AES_MAX_KEY_SIZE,
		.geniv		= "eseqiv",
		.ivsize		= AES_BLOCK_SIZE,
		.setkey		= omap_aes_setkey,
		.encrypt	= omap_aes_ctr_encrypt,
		.decrypt	= omap_aes_ctr_decrypt,
	}
} ,
};

static struct omap_aes_algs_info omap_aes_algs_info_ecb_cbc[] = {
	{
		.algs_list	= algs_ecb_cbc,
		.size		= ARRAY_SIZE(algs_ecb_cbc),
	},
};

static struct aead_alg algs_aead_gcm[] = {
{
	.base = {
		.cra_name		= "gcm(aes)",
		.cra_driver_name	= "gcm-aes-omap",
		.cra_priority		= 300,
		.cra_flags		= CRYPTO_ALG_ASYNC |
					  CRYPTO_ALG_KERN_DRIVER_ONLY,
		.cra_blocksize		= 1,
		.cra_ctxsize		= sizeof(struct omap_aes_ctx),
		.cra_alignmask		= 0xf,
		.cra_module		= THIS_MODULE,
	},
	.init		= omap_aes_gcm_cra_init,
	.exit		= omap_aes_gcm_cra_exit,
	.ivsize		= 12,
	.maxauthsize	= AES_BLOCK_SIZE,
	.setkey		= omap_aes_gcm_setkey,
	.encrypt	= omap_aes_gcm_encrypt,
	.decrypt	= omap_aes_gcm_decrypt,
},
{
	.base = {
		.cra_name		= "rfc4106(gcm(aes))",
		.cra_driver_name	= "rfc4106-gcm-aes-omap",
		.cra_priority		= 300,
		.cra_flags		= CRYPTO_ALG_ASYNC |
					  CRYPTO_ALG_KERN_DRIVER_ONLY,
		.cra_blocksize		= 1,
		.cra_ctxsize		= sizeof(struct omap_aes_ctx),
		.cra_alignmask		= 0xf,
		.cra_module		= THIS_MODULE,
	},
	.init		= omap_aes_gcm_cra_init,
	.exit		= omap_aes_gcm_cra_exit,
	.maxauthsize	= AES_BLOCK_SIZE,
	.ivsize		= 8,
	.setkey		= omap_aes_4106gcm_setkey,
	.encrypt	= omap_aes_4106gcm_encrypt,
	.decrypt	= omap_aes_4106gcm_decrypt,
},
};

static struct omap_aes_aead_algs omap_aes_aead_info = {
	.algs_list	=	algs_aead_gcm,
	.size		=	ARRAY_SIZE(algs_aead_gcm),
};

static const struct omap_aes_pdata omap_aes_pdata_omap2 = {
	.algs_info	= omap_aes_algs_info_ecb_cbc,
	.algs_info_size	= ARRAY_SIZE(omap_aes_algs_info_ecb_cbc),
	.trigger	= omap_aes_dma_trigger_omap2,
	.key_ofs	= 0x1c,
	.iv_ofs		= 0x20,
	.ctrl_ofs	= 0x30,
	.data_ofs	= 0x34,
	.rev_ofs	= 0x44,
	.mask_ofs	= 0x48,
	.dma_enable_in	= BIT(2),
	.dma_enable_out	= BIT(3),
	.dma_start	= BIT(5),
	.major_mask	= 0xf0,
	.major_shift	= 4,
	.minor_mask	= 0x0f,
	.minor_shift	= 0,
};

#ifdef CONFIG_OF
static struct omap_aes_algs_info omap_aes_algs_info_ecb_cbc_ctr[] = {
	{
		.algs_list	= algs_ecb_cbc,
		.size		= ARRAY_SIZE(algs_ecb_cbc),
	},
	{
		.algs_list	= algs_ctr,
		.size		= ARRAY_SIZE(algs_ctr),
	},
};

static const struct omap_aes_pdata omap_aes_pdata_omap3 = {
	.algs_info	= omap_aes_algs_info_ecb_cbc_ctr,
	.algs_info_size	= ARRAY_SIZE(omap_aes_algs_info_ecb_cbc_ctr),
	.trigger	= omap_aes_dma_trigger_omap2,
	.key_ofs	= 0x1c,
	.iv_ofs		= 0x20,
	.ctrl_ofs	= 0x30,
	.data_ofs	= 0x34,
	.rev_ofs	= 0x44,
	.mask_ofs	= 0x48,
	.dma_enable_in	= BIT(2),
	.dma_enable_out	= BIT(3),
	.dma_start	= BIT(5),
	.major_mask	= 0xf0,
	.major_shift	= 4,
	.minor_mask	= 0x0f,
	.minor_shift	= 0,
};

static const struct omap_aes_pdata omap_aes_pdata_omap4 = {
	.algs_info	= omap_aes_algs_info_ecb_cbc_ctr,
	.algs_info_size	= ARRAY_SIZE(omap_aes_algs_info_ecb_cbc_ctr),
	.aead_algs_info	= &omap_aes_aead_info,
	.trigger	= omap_aes_dma_trigger_omap4,
	.key_ofs	= 0x3c,
	.iv_ofs		= 0x40,
	.ctrl_ofs	= 0x50,
	.data_ofs	= 0x60,
	.rev_ofs	= 0x80,
	.mask_ofs	= 0x84,
	.irq_status_ofs = 0x8c,
	.irq_enable_ofs = 0x90,
	.dma_enable_in	= BIT(5),
	.dma_enable_out	= BIT(6),
	.major_mask	= 0x0700,
	.major_shift	= 8,
	.minor_mask	= 0x003f,
	.minor_shift	= 0,
};

static irqreturn_t omap_aes_irq(int irq, void *dev_id)
{
	struct omap_aes_dev *dd = dev_id;
	u32 status, i;
	u32 *src, *dst;

	status = omap_aes_read(dd, AES_REG_IRQ_STATUS(dd));
	if (status & AES_REG_IRQ_DATA_IN) {
		omap_aes_write(dd, AES_REG_IRQ_ENABLE(dd), 0x0);

		BUG_ON(!dd->in_sg);

		BUG_ON(_calc_walked(in) > dd->in_sg->length);

		src = sg_virt(dd->in_sg) + _calc_walked(in);

		for (i = 0; i < AES_BLOCK_WORDS; i++) {
			omap_aes_write(dd, AES_REG_DATA_N(dd, i), *src);

			scatterwalk_advance(&dd->in_walk, 4);
			if (dd->in_sg->length == _calc_walked(in)) {
				dd->in_sg = sg_next(dd->in_sg);
				if (dd->in_sg) {
					scatterwalk_start(&dd->in_walk,
							  dd->in_sg);
					src = sg_virt(dd->in_sg) +
					      _calc_walked(in);
				}
			} else {
				src++;
			}
		}

		/* Clear IRQ status */
		status &= ~AES_REG_IRQ_DATA_IN;
		omap_aes_write(dd, AES_REG_IRQ_STATUS(dd), status);

		/* Enable DATA_OUT interrupt */
		omap_aes_write(dd, AES_REG_IRQ_ENABLE(dd), 0x4);

	} else if (status & AES_REG_IRQ_DATA_OUT) {
		omap_aes_write(dd, AES_REG_IRQ_ENABLE(dd), 0x0);

		BUG_ON(!dd->out_sg);

		BUG_ON(_calc_walked(out) > dd->out_sg->length);

		dst = sg_virt(dd->out_sg) + _calc_walked(out);

		for (i = 0; i < AES_BLOCK_WORDS; i++) {
			*dst = omap_aes_read(dd, AES_REG_DATA_N(dd, i));
			scatterwalk_advance(&dd->out_walk, 4);
			if (dd->out_sg->length == _calc_walked(out)) {
				dd->out_sg = sg_next(dd->out_sg);
				if (dd->out_sg) {
					scatterwalk_start(&dd->out_walk,
							  dd->out_sg);
					dst = sg_virt(dd->out_sg) +
					      _calc_walked(out);
				}
			} else {
				dst++;
			}
		}

		dd->total -= min_t(size_t, AES_BLOCK_SIZE, dd->total);

		/* Clear IRQ status */
		status &= ~AES_REG_IRQ_DATA_OUT;
		omap_aes_write(dd, AES_REG_IRQ_STATUS(dd), status);

		if (!dd->total)
			/* All bytes read! */
			tasklet_schedule(&dd->done_task);
		else
			/* Enable DATA_IN interrupt for next block */
			omap_aes_write(dd, AES_REG_IRQ_ENABLE(dd), 0x2);
	}

	return IRQ_HANDLED;
}

static const struct of_device_id omap_aes_of_match[] = {
	{
		.compatible	= "ti,omap2-aes",
		.data		= &omap_aes_pdata_omap2,
	},
	{
		.compatible	= "ti,omap3-aes",
		.data		= &omap_aes_pdata_omap3,
	},
	{
		.compatible	= "ti,omap4-aes",
		.data		= &omap_aes_pdata_omap4,
	},
	{},
};
MODULE_DEVICE_TABLE(of, omap_aes_of_match);

static int omap_aes_get_res_of(struct omap_aes_dev *dd,
		struct device *dev, struct resource *res)
{
	struct device_node *node = dev->of_node;
	const struct of_device_id *match;
	int err = 0;

	match = of_match_device(of_match_ptr(omap_aes_of_match), dev);
	if (!match) {
		dev_err(dev, "no compatible OF match\n");
		err = -EINVAL;
		goto err;
	}

	err = of_address_to_resource(node, 0, res);
	if (err < 0) {
		dev_err(dev, "can't translate OF node address\n");
		err = -EINVAL;
		goto err;
	}

	dd->pdata = match->data;

err:
	return err;
}
#else
static const struct of_device_id omap_aes_of_match[] = {
	{},
};

static int omap_aes_get_res_of(struct omap_aes_dev *dd,
		struct device *dev, struct resource *res)
{
	return -EINVAL;
}
#endif

static int omap_aes_get_res_pdev(struct omap_aes_dev *dd,
		struct platform_device *pdev, struct resource *res)
{
	struct device *dev = &pdev->dev;
	struct resource *r;
	int err = 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;
	}
	memcpy(res, r, sizeof(*res));

	/* Only OMAP2/3 can be non-DT */
	dd->pdata = &omap_aes_pdata_omap2;

err:
	return err;
}

static ssize_t fallback_show(struct device *dev, struct device_attribute *attr,
			     char *buf)
{
	return sprintf(buf, "%d\n", aes_fallback_sz);
}

static ssize_t fallback_store(struct device *dev, struct device_attribute *attr,
			      const char *buf, size_t size)
{
	ssize_t status;
	long value;

	status = kstrtol(buf, 0, &value);
	if (status)
		return status;

	/* HW accelerator only works with buffers > 9 */
	if (value < 9) {
		dev_err(dev, "minimum fallback size 9\n");
		return -EINVAL;
	}

	aes_fallback_sz = value;

	return size;
}

static ssize_t queue_len_show(struct device *dev, struct device_attribute *attr,
			      char *buf)
{
	struct omap_aes_dev *dd = dev_get_drvdata(dev);

	return sprintf(buf, "%d\n", dd->queue.max_qlen);
}

static ssize_t queue_len_store(struct device *dev,
			       struct device_attribute *attr, const char *buf,
			       size_t size)
{
	struct omap_aes_dev *dd;
	ssize_t status;
	long value;
	unsigned long flags;

	status = kstrtol(buf, 0, &value);
	if (status)
		return status;

	if (value < 0)
		return -EINVAL;

	/*
	 * Changing the queue size in fly is safe, if size becomes smaller
	 * than current size, it will just not accept new entries until
	 * it has shrank enough.
	 */
	spin_lock_bh(&list_lock);
	list_for_each_entry(dd, &dev_list, list) {
		spin_lock_irqsave(&dd->lock, flags);
		dd->queue.max_qlen = value;
		dd->aead_queue.base.max_qlen = value;
		spin_unlock_irqrestore(&dd->lock, flags);
	}
	spin_unlock_bh(&list_lock);

	return size;
}

static DEVICE_ATTR_RW(queue_len);
static DEVICE_ATTR_RW(fallback);

static struct attribute *omap_aes_attrs[] = {
	&dev_attr_queue_len.attr,
	&dev_attr_fallback.attr,
	NULL,
};

static struct attribute_group omap_aes_attr_group = {
	.attrs = omap_aes_attrs,
};

static DEFINE_MUTEX(probe_lock);

static int omap_aes_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct omap_aes_dev *dd;
	struct crypto_alg *algp;
	struct aead_alg *aalg;
	struct resource res;
	int err = -ENOMEM, i, j, irq = -1;
	u32 reg;

	dd = devm_kzalloc(dev, sizeof(struct omap_aes_dev), GFP_KERNEL);
	if (dd == NULL) {
		dev_err(dev, "unable to alloc data struct.\n");
		goto err_data;
	}
	dd->dev = dev;
	platform_set_drvdata(pdev, dd);

	spin_lock_init(&dd->lock);
	crypto_init_queue(&dd->queue, OMAP_AES_QUEUE_LENGTH);
	aead_init_queue(&dd->aead_queue, OMAP_AES_QUEUE_LENGTH);

	err = (dev->of_node) ? omap_aes_get_res_of(dd, dev, &res) :
			       omap_aes_get_res_pdev(dd, pdev, &res);
	if (err)
		goto err_res;

	dd->io_base = devm_ioremap_resource(dev, &res);
	if (IS_ERR(dd->io_base)) {
		err = PTR_ERR(dd->io_base);
		goto err_res;
	}
	dd->phys_base = res.start;

	pm_runtime_use_autosuspend(dev);
	pm_runtime_set_autosuspend_delay(dev, DEFAULT_AUTOSUSPEND_DELAY);

	pm_runtime_enable(dev);
	err = pm_runtime_get_sync(dev);
	if (err < 0) {
		dev_err(dev, "%s: failed to get_sync(%d)\n",
			__func__, err);
		goto err_res;
	}

	omap_aes_dma_stop(dd);

	reg = omap_aes_read(dd, AES_REG_REV(dd));

	pm_runtime_put_sync(dev);

	dev_info(dev, "OMAP AES hw accel rev: %u.%u\n",
		 (reg & dd->pdata->major_mask) >> dd->pdata->major_shift,
		 (reg & dd->pdata->minor_mask) >> dd->pdata->minor_shift);

	tasklet_init(&dd->done_task, omap_aes_done_task, (unsigned long)dd);
	tasklet_init(&dd->queue_task, omap_aes_queue_task, (unsigned long)dd);

	err = omap_aes_dma_init(dd);
	if (err == -EPROBE_DEFER) {
		goto err_irq;
	} else if (err && AES_REG_IRQ_STATUS(dd) && AES_REG_IRQ_ENABLE(dd)) {
		dd->pio_only = 1;

		irq = platform_get_irq(pdev, 0);
		if (irq < 0) {
			dev_err(dev, "can't get IRQ resource\n");
			goto err_irq;
		}

		err = devm_request_irq(dev, irq, omap_aes_irq, 0,
				dev_name(dev), dd);
		if (err) {
			dev_err(dev, "Unable to grab omap-aes IRQ\n");
			goto err_irq;
		}
	}


	INIT_LIST_HEAD(&dd->list);
	spin_lock(&list_lock);
	list_add_tail(&dd->list, &dev_list);
	spin_unlock(&list_lock);

	mutex_lock(&probe_lock);

	for (i = 0; i < dd->pdata->algs_info_size; i++) {
		if (!dd->pdata->algs_info[i].registered) {
			for (j = 0; j < dd->pdata->algs_info[i].size; j++) {
				algp = &dd->pdata->algs_info[i].algs_list[j];

				pr_debug("reg alg: %s\n", algp->cra_name);
				INIT_LIST_HEAD(&algp->cra_list);

				err = crypto_register_alg(algp);
				if (err) {
					mutex_unlock(&probe_lock);
					goto err_algs;
				}

				dd->pdata->algs_info[i].registered++;
			}
		}
	}

	if (!dd->pdata->aead_algs_info->registered) {
		for (i = 0; i < dd->pdata->aead_algs_info->size; i++) {
			aalg = &dd->pdata->aead_algs_info->algs_list[i];
			algp = &aalg->base;

			pr_debug("reg alg: %s\n", algp->cra_name);
			INIT_LIST_HEAD(&algp->cra_list);

			err = crypto_register_aead(aalg);
			if (err) {
				mutex_unlock(&probe_lock);
				goto err_aead_algs;
			}

			dd->pdata->aead_algs_info->registered++;
		}
	}

	mutex_unlock(&probe_lock);

	err = sysfs_create_group(&dev->kobj, &omap_aes_attr_group);
	if (err) {
		dev_err(dev, "could not create sysfs device attrs\n");
		goto err_aead_algs;
	}

	return 0;
err_aead_algs:
	for (i = dd->pdata->aead_algs_info->registered - 1; i >= 0; i--) {
		aalg = &dd->pdata->aead_algs_info->algs_list[i];
		crypto_unregister_aead(aalg);
	}
err_algs:
	for (i = dd->pdata->algs_info_size - 1; i >= 0; i--)
		for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
			crypto_unregister_alg(
					&dd->pdata->algs_info[i].algs_list[j]);

	omap_aes_dma_cleanup(dd);
err_irq:
	tasklet_kill(&dd->done_task);
	tasklet_kill(&dd->queue_task);
	pm_runtime_disable(dev);
err_res:
	dd = NULL;
err_data:
	dev_err(dev, "initialization failed.\n");
	return err;
}

static int omap_aes_remove(struct platform_device *pdev)
{
	struct omap_aes_dev *dd = platform_get_drvdata(pdev);
	struct aead_alg *aalg;
	int i, j;

	if (!dd)
		return -ENODEV;

	spin_lock(&list_lock);
	list_del(&dd->list);
	spin_unlock(&list_lock);

	for (i = dd->pdata->algs_info_size - 1; i >= 0; i--)
		for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
			crypto_unregister_alg(
					&dd->pdata->algs_info[i].algs_list[j]);

	for (i = dd->pdata->aead_algs_info->size - 1; i >= 0; i--) {
		aalg = &dd->pdata->aead_algs_info->algs_list[i];
		crypto_unregister_aead(aalg);
	}

	tasklet_kill(&dd->done_task);
	tasklet_kill(&dd->queue_task);
	omap_aes_dma_cleanup(dd);
	pm_runtime_disable(dd->dev);
	dd = NULL;

	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int omap_aes_suspend(struct device *dev)
{
	pm_runtime_put_sync(dev);
	return 0;
}

static int omap_aes_resume(struct device *dev)
{
	pm_runtime_get_sync(dev);
	return 0;
}
#endif

static SIMPLE_DEV_PM_OPS(omap_aes_pm_ops, omap_aes_suspend, omap_aes_resume);

static struct platform_driver omap_aes_driver = {
	.probe	= omap_aes_probe,
	.remove	= omap_aes_remove,
	.driver	= {
		.name	= "omap-aes",
		.pm	= &omap_aes_pm_ops,
		.of_match_table	= omap_aes_of_match,
	},
};

module_platform_driver(omap_aes_driver);

MODULE_DESCRIPTION("OMAP AES hw acceleration support.");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Dmitry Kasatkin");

Linux 4.4.41-cl-som-am57x-ti-3.2-patch-by-hand #2 SMP PREEMPT Mon Apr 24 11:54:00 CEST 2017 armv7l
cl-debian-armhf login: [  116.631352] omap-aes 4b500000.aes: OMAP AES hw accel rev: 3.3
[  116.637755] ------------[ cut here ]------------
[  116.642406] WARNING: CPU: 1 PID: 1047 at fs/sysfs/dir.c:31 sysfs_warn_dup+0x6c/0x7c()
[  116.651091] sysfs: cannot create duplicate filename '/devices/platform/44000000.ocp/4b500000.aes/queue_len'
[  116.661042] Modules linked in: omap_aes_driver(O+) xhci_plat_hcd xhci_hcd usbcore joydev ads7846 dwc3 m25p80 userspace_consumer udc_core spi_nor evdev c_can_platform c_can us_consumer_wrapper can_dev leds_gpio snd_soc_omap_hdmi_audio omap_sham omap_wdt spi_ti_qspi phy_omap_usb2 ti_vpe ti_sc videobuf2_dma_contig ti_csc ti_vpdma v4l2_mem2mem videobuf2_memops dwc3_omap videobuf2_v4l2 videobuf2_core extcon rtc_omap omap_rng rng_core palmas_pwrbutton rtc_em3027 omap_remoteproc remoteproc virtio virtio_ring autofs4 [last unloaded: omap_aes_driver]
[  116.716316] CPU: 1 PID: 1047 Comm: modprobe Tainted: G           O    4.4.41-cl-som-am57x-ti-3.2-patch-by-hand #2
[  116.726621] Hardware name: Generic DRA74X (Flattened Device Tree)
[  116.732738] Backtrace: 
[  116.735213] [<c0013094>] (dump_backtrace) from [<c0013290>] (show_stack+0x18/0x1c)
[  116.742813]  r7:c0196ecc r6:60070013 r5:00000000 r4:c095d3d0
[  116.748538] [<c0013278>] (show_stack) from [<c02e1614>] (dump_stack+0x8c/0xa0)
[  116.755798] [<c02e1588>] (dump_stack) from [<c0034b30>] (warn_slowpath_common+0x88/0xb8)
[  116.763922]  r7:c0196ecc r6:0000001f r5:00000009 r4:eca59b78
[  116.769647] [<c0034aa8>] (warn_slowpath_common) from [<c0034b98>] (warn_slowpath_fmt+0x38/0x40)
[  116.778380]  r8:eea62500 r7:ffffffef r6:eea62500 r5:bf2e2268 r4:c0832304
[  116.785161] [<c0034b64>] (warn_slowpath_fmt) from [<c0196ecc>] (sysfs_warn_dup+0x6c/0x7c)
[  116.793371]  r3:edfd1fd4 r2:c0832304
[  116.796974]  r4:edfd1000
[  116.799530] [<c0196e60>] (sysfs_warn_dup) from [<c0196bac>] (sysfs_add_file_mode_ns+0x148/0x19c)
[  116.808350]  r6:eea62500 r5:bf2e2430 r4:00000000
[  116.813016] [<c0196a64>] (sysfs_add_file_mode_ns) from [<c01975d0>] (internal_create_group+0x104/0x30c)
[  116.822448]  r7:bf2e23a4 r6:00000000 r5:000001a4 r4:bf2e2414
[  116.828167] [<c01974cc>] (internal_create_group) from [<c01977f0>] (sysfs_create_group+0x18/0x1c)
[  116.837075]  r10:00000018 r9:00000002 r8:00000280 r7:00000000 r6:eea6d610 r5:eea6d600
[  116.844980]  r4:edb9f410
[  116.847545] [<c01977d8>] (sysfs_create_group) from [<bf2dfd2c>] (omap_aes_probe+0x5f8/0x6a4 [omap_aes_driver])
[  116.857602] [<bf2df734>] (omap_aes_probe [omap_aes_driver]) from [<c03fb250>] (platform_drv_probe+0x54/0xb8)
[  116.867469]  r10:00000000 r9:ec886308 r8:00000043 r7:fffffdfb r6:bf2e23cc r5:eea6d610
[  116.875375]  r4:c09c1e7c
[  116.877928] [<c03fb1fc>] (platform_drv_probe) from [<c03f97d8>] (driver_probe_device+0x204/0x2f8)
[  116.886836]  r7:bf2e23cc r6:00000000 r5:eea6d610 r4:c09c1e7c
[  116.892556] [<c03f95d4>] (driver_probe_device) from [<c03f9960>] (__driver_attach+0x94/0x98)
[  116.901027]  r9:ec886308 r8:13779d1c r7:00000000 r6:eea6d644 r5:bf2e23cc r4:eea6d610
[  116.908853] [<c03f98cc>] (__driver_attach) from [<c03f7a60>] (bus_for_each_dev+0x70/0xa4)
[  116.917063]  r7:00000000 r6:c03f98cc r5:bf2e23cc r4:00000000
[  116.922785] [<c03f79f0>] (bus_for_each_dev) from [<c03f90c4>] (driver_attach+0x24/0x28)
[  116.930821]  r6:c096aa70 r5:ed8e7a00 r4:bf2e23cc
[  116.935488] [<c03f90a0>] (driver_attach) from [<c03f8d00>] (bus_add_driver+0x1a8/0x220)
[  116.943528] [<c03f8b58>] (bus_add_driver) from [<c03fa100>] (driver_register+0x80/0x100)
[  116.951651]  r7:ec886d80 r6:c0941760 r5:bf2e5000 r4:bf2e23cc
[  116.957371] [<c03fa080>] (driver_register) from [<c03fb178>] (__platform_driver_register+0x48/0x50)
[  116.966453]  r5:bf2e5000 r4:c096aa70
[  116.970069] [<c03fb130>] (__platform_driver_register) from [<bf2e501c>] (omap_aes_driver_init+0x1c/0x24 [omap_aes_driver])
[  116.981157]  r5:bf2e5000 r4:c0941760
[  116.984772] [<bf2e5000>] (omap_aes_driver_init [omap_aes_driver]) from [<c000978c>] (do_one_initcall+0x98/0x1e4)
[  116.994996] [<c00096f4>] (do_one_initcall) from [<c00d6914>] (do_init_module+0x68/0x38c)
[  117.003118]  r10:bf2e2a40 r9:ec886308 r8:13779d1c r7:00000001 r6:ec8863c0 r5:00000001
[  117.011024]  r4:bf2e2a40
[  117.013582] [<c00d68ac>] (do_init_module) from [<c00a5e8c>] (load_module+0x1e28/0x20d8)
[  117.021618]  r6:ec886300 r5:00000001 r4:eca59f44
[  117.026284] [<c00a4064>] (load_module) from [<c00a6318>] (SyS_finit_module+0x88/0x98)
[  117.034145]  r10:00000000 r9:eca58000 r8:c000fb44 r7:0000017b r6:7f5b4e80 r5:00000003
[  117.042050]  r4:00000000
[  117.044605] [<c00a6290>] (SyS_finit_module) from [<c000f9a0>] (ret_fast_syscall+0x0/0x34)
[  117.052814]  r6:00000000 r5:00040000 r4:bed1e3d0
[  117.058006] ---[ end trace 6a80443878e91c61 ]---
[  117.062663] omap-aes 4b500000.aes: could not create sysfs device attrs
[  117.069267] omap-aes 4b500000.aes: initialization failed.
[  117.086893] ------------[ cut here ]------------
[  117.091539] WARNING: CPU: 1 PID: 1047 at fs/sysfs/dir.c:31 sysfs_warn_dup+0x6c/0x7c()
[  117.099428] sysfs: cannot create duplicate filename '/devices/platform/44000000.ocp/4b700000.aes/queue_len'
[  117.109231] Modules linked in: omap_aes_driver(O+) xhci_plat_hcd xhci_hcd usbcore joydev ads7846 dwc3 m25p80 userspace_consumer udc_core spi_nor evdev c_can_platform c_can us_consumer_wrapper can_dev leds_gpio snd_soc_omap_hdmi_audio omap_sham omap_wdt spi_ti_qspi phy_omap_usb2 ti_vpe ti_sc videobuf2_dma_contig ti_csc ti_vpdma v4l2_mem2mem videobuf2_memops dwc3_omap videobuf2_v4l2 videobuf2_core extcon rtc_omap omap_rng rng_core palmas_pwrbutton rtc_em3027 omap_remoteproc remoteproc virtio virtio_ring autofs4 [last unloaded: omap_aes_driver]
[  117.157836] CPU: 1 PID: 1047 Comm: modprobe Tainted: G        W  O    4.4.41-cl-som-am57x-ti-3.2-patch-by-hand #2
[  117.168140] Hardware name: Generic DRA74X (Flattened Device Tree)
[  117.174255] Backtrace: 
[  117.176727] [<c0013094>] (dump_backtrace) from [<c0013290>] (show_stack+0x18/0x1c)
[  117.184327]  r7:c0196ecc r6:60070013 r5:00000000 r4:c095d3d0
[  117.190048] [<c0013278>] (show_stack) from [<c02e1614>] (dump_stack+0x8c/0xa0)
[  117.197307] [<c02e1588>] (dump_stack) from [<c0034b30>] (warn_slowpath_common+0x88/0xb8)
[  117.205429]  r7:c0196ecc r6:0000001f r5:00000009 r4:eca59b78
[  117.211149] [<c0034aa8>] (warn_slowpath_common) from [<c0034b98>] (warn_slowpath_fmt+0x38/0x40)
[  117.219881]  r8:eea62910 r7:ffffffef r6:eea62910 r5:bf2e2268 r4:c0832304
[  117.226656] [<c0034b64>] (warn_slowpath_fmt) from [<c0196ecc>] (sysfs_warn_dup+0x6c/0x7c)
[  117.234865]  r3:edfd1fd4 r2:c0832304
[  117.238468]  r4:edfd1000
[  117.241024] [<c0196e60>] (sysfs_warn_dup) from [<c0196bac>] (sysfs_add_file_mode_ns+0x148/0x19c)
[  117.249845]  r6:eea62910 r5:bf2e2430 r4:00000000
[  117.254510] [<c0196a64>] (sysfs_add_file_mode_ns) from [<c01975d0>] (internal_create_group+0x104/0x30c)
[  117.263941]  r7:bf2e23a4 r6:00000000 r5:000001a4 r4:bf2e2414
[  117.269658] [<c01974cc>] (internal_create_group) from [<c01977f0>] (sysfs_create_group+0x18/0x1c)
[  117.278565]  r10:00000018 r9:00000001 r8:00000002 r7:bf2e2380 r6:eea6d810 r5:eea6d800
[  117.286467]  r4:edfb7010
[  117.289029] [<c01977d8>] (sysfs_create_group) from [<bf2dfd2c>] (omap_aes_probe+0x5f8/0x6a4 [omap_aes_driver])
[  117.299083] [<bf2df734>] (omap_aes_probe [omap_aes_driver]) from [<c03fb250>] (platform_drv_probe+0x54/0xb8)
[  117.308948]  r10:00000000 r9:ec886308 r8:00000043 r7:fffffdfb r6:bf2e23cc r5:eea6d810
[  117.316852]  r4:c09c1e7c
[  117.319405] [<c03fb1fc>] (platform_drv_probe) from [<c03f97d8>] (driver_probe_device+0x204/0x2f8)
[  117.328313]  r7:bf2e23cc r6:00000000 r5:eea6d810 r4:c09c1e7c
[  117.334030] [<c03f95d4>] (driver_probe_device) from [<c03f9960>] (__driver_attach+0x94/0x98)
[  117.342501]  r9:ec886308 r8:13779d1c r7:00000000 r6:eea6d844 r5:bf2e23cc r4:eea6d810
[  117.350321] [<c03f98cc>] (__driver_attach) from [<c03f7a60>] (bus_for_each_dev+0x70/0xa4)
[  117.358531]  r7:00000000 r6:c03f98cc r5:bf2e23cc r4:00000000
[  117.364249] [<c03f79f0>] (bus_for_each_dev) from [<c03f90c4>] (driver_attach+0x24/0x28)
[  117.372284]  r6:c096aa70 r5:ed8e7a00 r4:bf2e23cc
[  117.376949] [<c03f90a0>] (driver_attach) from [<c03f8d00>] (bus_add_driver+0x1a8/0x220)
[  117.384991] [<c03f8b58>] (bus_add_driver) from [<c03fa100>] (driver_register+0x80/0x100)
[  117.393112]  r7:ec886d80 r6:c0941760 r5:bf2e5000 r4:bf2e23cc
[  117.398827] [<c03fa080>] (driver_register) from [<c03fb178>] (__platform_driver_register+0x48/0x50)
[  117.407908]  r5:bf2e5000 r4:c096aa70
[  117.411521] [<c03fb130>] (__platform_driver_register) from [<bf2e501c>] (omap_aes_driver_init+0x1c/0x24 [omap_aes_driver])
[  117.422608]  r5:bf2e5000 r4:c0941760
[  117.426222] [<bf2e5000>] (omap_aes_driver_init [omap_aes_driver]) from [<c000978c>] (do_one_initcall+0x98/0x1e4)
[  117.436444] [<c00096f4>] (do_one_initcall) from [<c00d6914>] (do_init_module+0x68/0x38c)
[  117.444566]  r10:bf2e2a40 r9:ec886308 r8:13779d1c r7:00000001 r6:ec8863c0 r5:00000001
[  117.452467]  r4:bf2e2a40
[  117.455021] [<c00d68ac>] (do_init_module) from [<c00a5e8c>] (load_module+0x1e28/0x20d8)
[  117.463056]  r6:ec886300 r5:00000001 r4:eca59f44
[  117.467718] [<c00a4064>] (load_module) from [<c00a6318>] (SyS_finit_module+0x88/0x98)
[  117.475578]  r10:00000000 r9:eca58000 r8:c000fb44 r7:0000017b r6:7f5b4e80 r5:00000003
[  117.483480]  r4:00000000
[  117.486033] [<c00a6290>] (SyS_finit_module) from [<c000f9a0>] (ret_fast_syscall+0x0/0x34)
[  117.494244]  r6:00000000 r5:00040000 r4:bed1e3d0
[  117.498937] ---[ end trace 6a80443878e91c62 ]---
[  117.503594] omap-aes 4b700000.aes: could not create sysfs device attrs
[  117.510189] omap-aes 4b700000.aes: initialization failed.
[  117.515656] omap-aes: probe of 4b700000.aes failed with error -17