diff --git a/MAINTAINERS b/MAINTAINERS index c58a0890ecf1..16aa14fa2206 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13443,7 +13443,7 @@ AMLOGIC meson64_xxx_defconfigs M: Jianxin Pan F: scripts/amlogic/configs/ -AMLOGIC CRYPTO DMA +AMLOGIC CRYPTO DMA BLKMV M: Matthew Shyu F: drivers/amlogic/crypto/* diff --git a/arch/arm/boot/dts/amlogic/meson8b.dtsi b/arch/arm/boot/dts/amlogic/meson8b.dtsi index d2535edb4965..10cb3305f4c2 100644 --- a/arch/arm/boot/dts/amlogic/meson8b.dtsi +++ b/arch/arm/boot/dts/amlogic/meson8b.dtsi @@ -280,5 +280,23 @@ dwc2_b { "usb0"; }; + aml_tdes { + compatible = "amlogic,des,tdes"; + dev_name = "aml_tdes_blkmv"; + status = "okay"; + interrupts = <0 68 1>; + clocks = <&clkc CLKID_BLKMV>; + clock-names = "blkmv"; + }; + + aml_aes { + compatible = "amlogic,aes"; + dev_name = "aml_aes_blkmv"; + status = "okay"; + interrupts = <0 68 1>; + clocks = <&clkc CLKID_BLKMV>; + clock-names = "blkmv"; + }; + }; }; /* end of / */ diff --git a/arch/arm/configs/meson32_defconfig b/arch/arm/configs/meson32_defconfig index a5890e651f76..86d08d48b4ed 100644 --- a/arch/arm/configs/meson32_defconfig +++ b/arch/arm/configs/meson32_defconfig @@ -38,6 +38,7 @@ CONFIG_ARM_CPUIDLE=y CONFIG_VFP=y CONFIG_NEON=y CONFIG_KERNEL_MODE_NEON=y +CONFIG_NET=y CONFIG_AMLOGIC_DRIVER=y CONFIG_AMLOGIC_UART=y CONFIG_AMLOGIC_SERIAL_MESON_CONSOLE=y @@ -49,6 +50,8 @@ CONFIG_AMLOGIC_USBPHY=y CONFIG_AMLOGIC_CLK=y # CONFIG_AMLOGIC_RESET is not set CONFIG_AMLOGIC_M8B_CLK=y +CONFIG_AMLOGIC_CRYPTO=y +CONFIG_AMLOGIC_CRYPTO_BLKMV=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_DMA_CMA=y @@ -133,8 +136,10 @@ CONFIG_KEYS=y CONFIG_CRYPTO_GF128MUL=m CONFIG_CRYPTO_SEQIV=y # CONFIG_CRYPTO_ECHAINIV is not set +CONFIG_CRYPTO_DES=y CONFIG_CRYPTO_DEFLATE=y CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y # CONFIG_CRYPTO_HW is not set CONFIG_CRC_CCITT=m CONFIG_CRC_ITU_T=m diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index d8e9cf0c55fc..d82afc7ff21f 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -183,6 +183,7 @@ CONFIG_AMLOGIC_CLK=y CONFIG_AMLOGIC_COMMON_CLK_SCPI=y CONFIG_AMLOGIC_GX_CLK=y CONFIG_AMLOGIC_CRYPTO=y +CONFIG_AMLOGIC_CRYPTO_DMA=y CONFIG_AMLOGIC_INPUT=y CONFIG_AMLOGIC_INPUT_KEYBOARD=y CONFIG_AMLOGIC_ADC_KEYPADS=y diff --git a/drivers/amlogic/crypto/Kconfig b/drivers/amlogic/crypto/Kconfig index 84e5ae184ad3..cb6a4fdf1ce4 100644 --- a/drivers/amlogic/crypto/Kconfig +++ b/drivers/amlogic/crypto/Kconfig @@ -7,4 +7,24 @@ config AMLOGIC_CRYPTO select CRYPTO_BLKCIPHER select CRYPTO_HASH default n + +config AMLOGIC_CRYPTO_DMA + bool "amlogic HW CRYPTO module based on DMA" + depends on AMLOGIC_CRYPTO + default n + help + New Crypto DMA engine starting from S905-X + Current supported Socs are S905-X, S905-D, S912 + Drivers using new dma engine are in file name + such aml-xxx-dma.c + +config AMLOGIC_CRYPTO_BLKMV + bool "amlogic HW CRYPTO module based on BLKMV" + depends on AMLOGIC_CRYPTO + default n + help + Legacy Crypto engine based on module BLKMV + Drivers using new dma engine are in file name + such aml-xxx-blkmv.c + endmenu diff --git a/drivers/amlogic/crypto/Makefile b/drivers/amlogic/crypto/Makefile index 5fafcc0c3de7..0271f403f35b 100644 --- a/drivers/amlogic/crypto/Makefile +++ b/drivers/amlogic/crypto/Makefile @@ -1,4 +1,7 @@ -obj-$(CONFIG_AMLOGIC_CRYPTO) += aml-aes-dma.o -obj-$(CONFIG_AMLOGIC_CRYPTO) += aml-tdes-dma.o -obj-$(CONFIG_AMLOGIC_CRYPTO) += aml-crypto-dma.o -obj-$(CONFIG_AMLOGIC_CRYPTO) += aml-sha-dma.o +obj-$(CONFIG_AMLOGIC_CRYPTO_DMA) += aml-aes-dma.o +obj-$(CONFIG_AMLOGIC_CRYPTO_DMA) += aml-tdes-dma.o +obj-$(CONFIG_AMLOGIC_CRYPTO_DMA) += aml-crypto-dma.o +obj-$(CONFIG_AMLOGIC_CRYPTO_DMA) += aml-sha-dma.o +obj-$(CONFIG_AMLOGIC_CRYPTO_BLKMV) += aml-aes-blkmv.o +obj-$(CONFIG_AMLOGIC_CRYPTO_BLKMV) += aml-tdes-blkmv.o +obj-$(CONFIG_AMLOGIC_CRYPTO_BLKMV) += aml-crypto-blkmv.o diff --git a/drivers/amlogic/crypto/aml-aes-blkmv.c b/drivers/amlogic/crypto/aml-aes-blkmv.c new file mode 100644 index 000000000000..217391fc3f8c --- /dev/null +++ b/drivers/amlogic/crypto/aml-aes-blkmv.c @@ -0,0 +1,885 @@ +/* + * drivers/amlogic/crypto/aml-aes-blkmv.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aml-crypto-blkmv.h" + +/* AES flags */ +#define AES_FLAGS_MODE_MASK 0x07 +#define AES_FLAGS_ENCRYPT BIT(0) +#define AES_FLAGS_CBC BIT(1) +#define AES_FLAGS_CTR BIT(2) + +#define AES_FLAGS_INIT BIT(8) +#define AES_FLAGS_DMA BIT(9) +#define AES_FLAGS_BUSY BIT(10) + +#define AML_AES_QUEUE_LENGTH 50 +#define AML_AES_DMA_THRESHOLD 16 + +unsigned char ndma_aes_table_block[NDMA_TABLE_SIZE + 32]; +#define NDMA_AES_TABLE_START (((unsigned long)ndma_aes_table_block+0x1f)\ + &(~0x1f)) +unsigned long ndma_aes_table_ptr; + +struct aml_aes_dev; + +struct aml_aes_ctx { + struct aml_aes_dev *dd; + + int keylen; + u32 key[AES_KEYSIZE_256 / sizeof(u32)]; + + u16 block_size; +}; + +struct aml_aes_reqctx { + unsigned long mode; +}; + +struct aml_aes_dev { + struct list_head list; + + struct aml_aes_ctx *ctx; + struct device *dev; + struct clk *clk; + int irq; + + unsigned long flags; + int err; + + spinlock_t lock; + struct crypto_queue queue; + + struct tasklet_struct done_task; + struct tasklet_struct queue_task; + + struct ablkcipher_request *req; + size_t total; + + struct scatterlist *in_sg; + size_t in_offset; + struct scatterlist *out_sg; + size_t out_offset; + + size_t buflen; + size_t dma_size; + + void *buf_in; + dma_addr_t dma_addr_in; + + void *buf_out; + dma_addr_t dma_addr_out; + dma_addr_t dma_descript_tab; +}; + +struct aml_aes_drv { + struct list_head dev_list; + spinlock_t lock; +}; + +static struct aml_aes_drv aml_aes = { + .dev_list = LIST_HEAD_INIT(aml_aes.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(aml_aes.lock), +}; + +static void set_aes_key_normal(u32 *key, unsigned int keylen) +{ + cbus_wr(NDMA_AES_REG0, ((cbus_rd(NDMA_AES_REG0) & ~(0x3 << 8)) + | (AES_THREAD_INDEX << 8))); + cbus_wr(NDMA_AES_KEY_0, *key); + cbus_wr(NDMA_AES_KEY_1, *(key+1)); + cbus_wr(NDMA_AES_KEY_2, *(key+2)); + cbus_wr(NDMA_AES_KEY_3, *(key+3)); + if (keylen > AES_KEYSIZE_128) { + cbus_wr(NDMA_AES_KEY_4, *(key+4)); + cbus_wr(NDMA_AES_KEY_5, *(key+5)); + } + if (keylen > AES_KEYSIZE_192) { + cbus_wr(NDMA_AES_KEY_6, *(key+6)); + cbus_wr(NDMA_AES_KEY_7, *(key+7)); + } +} + +static void set_aes_key(u32 *key, unsigned int keylen) +{ + set_aes_key_normal(key, keylen); +} + +static void set_aes_iv_normal(unsigned long *iv) +{ + cbus_wr(NDMA_AES_REG0, ((cbus_rd(NDMA_AES_REG0) + & ~(0x3 << 8)) + | (AES_THREAD_INDEX << 8))); + cbus_wr(NDMA_AES_IV_0, *iv); + cbus_wr(NDMA_AES_IV_1, *(iv+1)); + cbus_wr(NDMA_AES_IV_2, *(iv+2)); + cbus_wr(NDMA_AES_IV_3, *(iv+3)); +} + +static void set_aes_iv_big_endian_normal(unsigned long *iv) +{ + cbus_wr(NDMA_AES_REG0, ((cbus_rd(NDMA_AES_REG0) + & ~(0x3 << 8)) + | (AES_THREAD_INDEX << 8))); + cbus_wr(NDMA_AES_IV_3, swap_ulong32(*iv)); + cbus_wr(NDMA_AES_IV_2, swap_ulong32(*(iv+1))); + cbus_wr(NDMA_AES_IV_1, swap_ulong32(*(iv+2))); + cbus_wr(NDMA_AES_IV_0, swap_ulong32(*(iv+3))); +} + +static void set_aes_iv(unsigned long *iv) +{ + set_aes_iv_normal(iv); +} + +static void set_aes_iv_big_endian(unsigned long *iv) +{ + set_aes_iv_big_endian_normal(iv); +} + +static int aml_aes_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; + + scatterwalk_map_and_copy(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; +} + +static struct aml_aes_dev *aml_aes_find_dev(struct aml_aes_ctx *ctx) +{ + struct aml_aes_dev *aes_dd = NULL; + struct aml_aes_dev *tmp; + + spin_lock_bh(&aml_aes.lock); + if (!ctx->dd) { + list_for_each_entry(tmp, &aml_aes.dev_list, list) { + aes_dd = tmp; + break; + } + ctx->dd = aes_dd; + } else { + aes_dd = ctx->dd; + } + + spin_unlock_bh(&aml_aes.lock); + + return aes_dd; +} + +static int aml_aes_hw_init(struct aml_aes_dev *dd) +{ + cbus_wr(AM_RING_OSC_REG0, 0x1); + cbus_wr(NDMA_CNTL_REG1, cbus_rd(NDMA_CNTL_REG1) & 0xfffffffe); + /* enable module clk */ + if (dd->clk) + clk_prepare_enable(dd->clk); + + if (!(dd->flags & AES_FLAGS_INIT)) { + dd->flags |= AES_FLAGS_INIT; + dd->err = 0; + } + + return 0; +} + +static void aml_aes_finish_req(struct aml_aes_dev *dd, int err) +{ + struct ablkcipher_request *req = dd->req; + + dd->flags &= ~AES_FLAGS_BUSY; + req->base.complete(&req->base, err); +} + + +static int aml_aes_crypt_dma(struct aml_aes_dev *dd, + dma_addr_t dma_addr_in, dma_addr_t dma_addr_out, + int length, unsigned long restart) +{ + unsigned long mode; + unsigned long type; +#if AML_CRYPTO_DEBUG + int i = 0; +#endif + dd->dma_size = length; + + dma_sync_single_for_device(dd->dev, dma_addr_in, length, + DMA_TO_DEVICE); + + dd->flags |= AES_FLAGS_DMA; + + mode = MODE_ECB; + + if (dd->flags & AES_FLAGS_CBC) + mode = MODE_CBC; + else if (dd->flags & AES_FLAGS_CTR) + mode = MODE_CTR; + + type = ((mode & 0x3)<<3)| + (((dd->flags & AES_FLAGS_ENCRYPT) ? 1 : 0) << 2)| + ((dd->ctx->keylen == AES_KEYSIZE_128) ? 0 : + ((dd->ctx->keylen == AES_KEYSIZE_192) ? 1 : 2)); + ndma_add_descriptor_1d( + ndma_aes_table_ptr, /* table location */ + 1, /* irq */ + restart, /* restart */ + 0, /* pre_endian*/ + 0, /* post_endian */ + type, /* keytype + cryptodir */ + dd->dma_size, /* bytes_to_move */ + INLINE_TYPE_AES, /* inlinetype */ + dma_addr_in, /* src_addr */ + dma_addr_out); /* dest_addr */ + + dma_sync_single_for_device(dd->dev, dd->dma_descript_tab, + NDMA_TABLE_SIZE, DMA_TO_DEVICE); + ndma_set_table_position(AES_THREAD_INDEX, + dd->dma_descript_tab, NDMA_TABLE_SIZE); + ndma_set_table_count(AES_THREAD_INDEX, 1); + +#if AML_CRYPTO_DEBUG + pr_err("%s: %d\n", __func__, __LINE__); + for (i = 0x2270; i < 0x229d; i++) + pr_err("reg(%4x) = 0x%8x\n", i, cbus_rd(i)); + + for (i = 0; i < 8; i++) + pr_err("table addr(%d) = 0x%8lx\n", i, + *(unsigned long *)(ndma_aes_table_ptr + i * 4)); +#endif + + ndma_start(AES_THREAD_INDEX); + + return 0; +} + +static int aml_aes_crypt_dma_start(struct aml_aes_dev *dd, + unsigned long restart) +{ + int err = 0; + size_t count; + dma_addr_t addr_in, addr_out; + + /* use cache buffers */ + count = aml_aes_sg_copy(&dd->in_sg, &dd->in_offset, + dd->buf_in, dd->buflen, dd->total, 0); + addr_in = dd->dma_addr_in; + addr_out = dd->dma_addr_out; + dd->total -= count; + + err = aml_aes_crypt_dma(dd, addr_in, addr_out, count, restart); + + return err; +} + +static int aml_aes_write_ctrl(struct aml_aes_dev *dd) +{ + int err = 0; + + err = aml_aes_hw_init(dd); + if (err) + return err; + + set_aes_key(dd->ctx->key, dd->ctx->keylen); + + if (dd->flags & AES_FLAGS_CBC) + set_aes_iv(dd->req->info); + else if (dd->flags & AES_FLAGS_CTR) + set_aes_iv_big_endian(dd->req->info); + + return err; +} + +static int aml_aes_handle_queue(struct aml_aes_dev *dd, + struct ablkcipher_request *req) +{ + struct crypto_async_request *async_req, *backlog; + struct aml_aes_ctx *ctx; + struct aml_aes_reqctx *rctx; + unsigned long flags; + int err, ret = 0; + + spin_lock_irqsave(&dd->lock, flags); + if (req) + ret = ablkcipher_enqueue_request(&dd->queue, req); + + if (dd->flags & AES_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 |= AES_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->in_offset = 0; + dd->in_sg = req->src; + dd->out_offset = 0; + dd->out_sg = req->dst; + + rctx = ablkcipher_request_ctx(req); + ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); + rctx->mode &= AES_FLAGS_MODE_MASK; + dd->flags = (dd->flags & ~AES_FLAGS_MODE_MASK) | rctx->mode; + dd->ctx = ctx; + ctx->dd = dd; + + err = aml_aes_write_ctrl(dd); + if (!err) { + if (dd->total % AML_AES_DMA_THRESHOLD == 0) + err = aml_aes_crypt_dma_start(dd, 1); + else { + pr_err("size %d is not multiple of %d", + dd->total, AML_AES_DMA_THRESHOLD); + err = -EINVAL; + } + } + if (err) { + /* aes_task will not finish it, so do it here */ + aml_aes_finish_req(dd, err); + tasklet_schedule(&dd->queue_task); + } + + return ret; +} + +static int aml_aes_crypt_dma_stop(struct aml_aes_dev *dd) +{ + int err = -EINVAL; + size_t count; + + if (dd->flags & AES_FLAGS_DMA) { + err = 0; + dma_sync_single_for_cpu(dd->dev, dd->dma_descript_tab, + NDMA_TABLE_SIZE, DMA_TO_DEVICE); + dma_sync_single_for_device(dd->dev, dd->dma_addr_out, + dd->dma_size, DMA_FROM_DEVICE); + + /* copy data */ + count = aml_aes_sg_copy(&dd->out_sg, &dd->out_offset, + dd->buf_out, dd->buflen, dd->dma_size, 1); + if (count != dd->dma_size) { + err = -EINVAL; + pr_err("not all data converted: %u\n", count); + } + dd->flags &= ~AES_FLAGS_DMA; + } + + return err; +} + + +static int aml_aes_buff_init(struct aml_aes_dev *dd) +{ + int err = -ENOMEM; + + dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, 0); + dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, 0); + dd->buflen = PAGE_SIZE; + dd->buflen &= ~(AES_BLOCK_SIZE - 1); + + if (!dd->buf_in || !dd->buf_out) { + dev_err(dd->dev, "unable to alloc pages.\n"); + goto err_alloc; + } + + /* MAP here */ + dd->dma_addr_in = dma_map_single(dd->dev, dd->buf_in, + dd->buflen, DMA_TO_DEVICE); + if (dma_mapping_error(dd->dev, dd->dma_addr_in)) { + dev_err(dd->dev, "dma %d bytes error\n", dd->buflen); + err = -EINVAL; + goto err_map_in; + } + + dd->dma_addr_out = dma_map_single(dd->dev, dd->buf_out, + dd->buflen, DMA_FROM_DEVICE); + if (dma_mapping_error(dd->dev, dd->dma_addr_out)) { + dev_err(dd->dev, "dma %d bytes error\n", dd->buflen); + err = -EINVAL; + goto err_map_out; + } + + dd->dma_descript_tab = dma_map_single(dd->dev, + (void *)ndma_aes_table_ptr, + NDMA_TABLE_SIZE, DMA_TO_DEVICE); + + if (dma_mapping_error(dd->dev, dd->dma_descript_tab)) { + dev_err(dd->dev, "dma descriptor error\n"); + err = -EINVAL; + goto err_map_descriptor; + } + + return 0; + +err_map_descriptor: + dma_unmap_single(dd->dev, dd->dma_descript_tab, NDMA_TABLE_SIZE, + DMA_TO_DEVICE); + +err_map_out: + dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, + DMA_TO_DEVICE); +err_map_in: + free_page((unsigned long)dd->buf_out); + free_page((unsigned long)dd->buf_in); +err_alloc: + if (err) + pr_err("error: %d\n", err); + return err; +} + +static void aml_aes_buff_cleanup(struct aml_aes_dev *dd) +{ + dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, + DMA_FROM_DEVICE); + dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, + DMA_TO_DEVICE); + free_page((unsigned long)dd->buf_out); + free_page((unsigned long)dd->buf_in); +} + +static int aml_aes_crypt(struct ablkcipher_request *req, unsigned long mode) +{ + struct aml_aes_ctx *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + struct aml_aes_reqctx *rctx = ablkcipher_request_ctx(req); + struct aml_aes_dev *dd; + + if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE)) { + pr_err("request size is not exact amount of AES blocks\n"); + return -EINVAL; + } + ctx->block_size = AES_BLOCK_SIZE; + + dd = aml_aes_find_dev(ctx); + if (!dd) + return -ENODEV; + + rctx->mode = mode; + + return aml_aes_handle_queue(dd, req); +} + +static int aml_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct aml_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm); + + if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 && + keylen != AES_KEYSIZE_256) { + crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + + return 0; +} + +static int aml_aes_ecb_encrypt(struct ablkcipher_request *req) +{ + return aml_aes_crypt(req, + AES_FLAGS_ENCRYPT); +} + +static int aml_aes_ecb_decrypt(struct ablkcipher_request *req) +{ + return aml_aes_crypt(req, + 0); +} + +static int aml_aes_cbc_encrypt(struct ablkcipher_request *req) +{ + return aml_aes_crypt(req, + AES_FLAGS_ENCRYPT | AES_FLAGS_CBC); +} + +static int aml_aes_cbc_decrypt(struct ablkcipher_request *req) +{ + return aml_aes_crypt(req, + AES_FLAGS_CBC); +} + +static int aml_aes_ctr_encrypt(struct ablkcipher_request *req) +{ + return aml_aes_crypt(req, + AES_FLAGS_ENCRYPT | AES_FLAGS_CTR); +} + +static int aml_aes_ctr_decrypt(struct ablkcipher_request *req) +{ + /* XXX: use encrypt to replace for decrypt */ + return aml_aes_crypt(req, + AES_FLAGS_ENCRYPT | AES_FLAGS_CTR); +} + +static int aml_aes_cra_init(struct crypto_tfm *tfm) +{ + tfm->crt_ablkcipher.reqsize = sizeof(struct aml_aes_reqctx); + + return 0; +} + +static void aml_aes_cra_exit(struct crypto_tfm *tfm) +{ +} + +static struct crypto_alg aes_algs[] = { + { + .cra_name = "ecb(aes)", + .cra_driver_name = "ecb-aes-aml", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct aml_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = aml_aes_cra_init, + .cra_exit = aml_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = aml_aes_setkey, + .encrypt = aml_aes_ecb_encrypt, + .decrypt = aml_aes_ecb_decrypt, + } + }, + { + .cra_name = "cbc(aes)", + .cra_driver_name = "cbc-aes-aml", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct aml_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = aml_aes_cra_init, + .cra_exit = aml_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = aml_aes_setkey, + .encrypt = aml_aes_cbc_encrypt, + .decrypt = aml_aes_cbc_decrypt, + } + }, + { + .cra_name = "ctr(aes)", + .cra_driver_name = "ctr-aes-aml", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct aml_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = aml_aes_cra_init, + .cra_exit = aml_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = aml_aes_setkey, + .encrypt = aml_aes_ctr_encrypt, + .decrypt = aml_aes_ctr_decrypt, + } + } +}; + +static void aml_aes_queue_task(unsigned long data) +{ + struct aml_aes_dev *dd = (struct aml_aes_dev *)data; + + aml_aes_handle_queue(dd, NULL); +} + +static void aml_aes_done_task(unsigned long data) +{ + struct aml_aes_dev *dd = (struct aml_aes_dev *) data; + int err; +#if AML_CRYPTO_DEBUG + int i = 0; +#endif + + err = aml_aes_crypt_dma_stop(dd); + +#if AML_CRYPTO_DEBUG + pr_err("%s: %d\n", __func__, __LINE__); + for (i = 0x2270; i < 0x228D; i++) + pr_err("reg(%4x) = 0x%8x\n", i, cbus_rd(i)); + + for (i = 0; i < 8; i++) { + pr_err("table addr(%d) = 0x%8lx\n", i, + *(unsigned long *)(ndma_aes_table_ptr + i * 4)); + } +#endif + err = dd->err ? : err; + + if (dd->total && !err) { + if (!err) + err = aml_aes_crypt_dma_start(dd, 0); + if (!err) + return; /* DMA started. Not fininishing. */ + } + + aml_aes_finish_req(dd, err); + aml_aes_handle_queue(dd, NULL); +} + +static irqreturn_t aml_aes_irq(int irq, void *dev_id) +{ + struct aml_aes_dev *aes_dd = dev_id; + + if (!(cbus_rd(NDMA_TABLE_ADD_REG) & (0xF << (AES_THREAD_INDEX * 8))) + && (cbus_rd(NDMA_THREAD_REG) & (1 << (AES_THREAD_INDEX + 8)))) { + if (AES_FLAGS_BUSY & aes_dd->flags) { + ndma_stop(AES_THREAD_INDEX); + tasklet_schedule(&aes_dd->done_task); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } + } + + return IRQ_NONE; +} + +static void aml_aes_unregister_algs(struct aml_aes_dev *dd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aes_algs); i++) + crypto_unregister_alg(&aes_algs[i]); +} + +static int aml_aes_register_algs(struct aml_aes_dev *dd) +{ + int err, i, j; + + for (i = 0; i < ARRAY_SIZE(aes_algs); i++) { + err = crypto_register_alg(&aes_algs[i]); + if (err) + goto err_aes_algs; + } + + return 0; + +err_aes_algs: + for (j = 0; j < i; j++) + crypto_unregister_alg(&aes_algs[j]); + + return err; +} + +static int aml_aes_probe(struct platform_device *pdev) +{ + struct aml_aes_dev *aes_dd; + struct device *dev = &pdev->dev; + struct resource *res_irq = 0; + int err; + + ndma_aes_table_ptr = NDMA_AES_TABLE_START; + + aes_dd = kzalloc(sizeof(struct aml_aes_dev), GFP_KERNEL); + if (aes_dd == NULL) { + err = -ENOMEM; + goto aes_dd_err; + } + + aes_dd->dev = dev; + + platform_set_drvdata(pdev, aes_dd); + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + aes_dd->clk = devm_clk_get(&pdev->dev, "blkmv"); + + INIT_LIST_HEAD(&aes_dd->list); + + tasklet_init(&aes_dd->done_task, aml_aes_done_task, + (unsigned long)aes_dd); + tasklet_init(&aes_dd->queue_task, aml_aes_queue_task, + (unsigned long)aes_dd); + + crypto_init_queue(&aes_dd->queue, AML_AES_QUEUE_LENGTH); + + aes_dd->irq = res_irq->start; + + err = request_irq(aes_dd->irq, aml_aes_irq, IRQF_SHARED, "aml-aes", + aes_dd); + if (err) { + dev_err(dev, "unable to request aes irq.\n"); + goto aes_irq_err; + } + + err = aml_aes_hw_init(aes_dd); + if (err) + goto err_aes_buff; + + err = aml_aes_buff_init(aes_dd); + if (err) + goto err_aes_buff; + + spin_lock(&aml_aes.lock); + list_add_tail(&aes_dd->list, &aml_aes.dev_list); + spin_unlock(&aml_aes.lock); + + err = aml_aes_register_algs(aes_dd); + if (err) + goto err_algs; + + dev_info(dev, "Aml AES\n"); + + return 0; + +err_algs: + spin_lock(&aml_aes.lock); + list_del(&aes_dd->list); + spin_unlock(&aml_aes.lock); + aml_aes_buff_cleanup(aes_dd); +err_aes_buff: + free_irq(aes_dd->irq, aes_dd); +aes_irq_err: + tasklet_kill(&aes_dd->done_task); + tasklet_kill(&aes_dd->queue_task); + if (aes_dd->clk) { + clk_disable_unprepare(aes_dd->clk); + devm_clk_put(&pdev->dev, aes_dd->clk); + } + kfree(aes_dd); + aes_dd = NULL; +aes_dd_err: + dev_err(dev, "initialization failed.\n"); + + return err; +} + +static int aml_aes_remove(struct platform_device *pdev) +{ + static struct aml_aes_dev *aes_dd; + + aes_dd = platform_get_drvdata(pdev); + if (!aes_dd) + return -ENODEV; + spin_lock(&aml_aes.lock); + list_del(&aes_dd->list); + spin_unlock(&aml_aes.lock); + + aml_aes_unregister_algs(aes_dd); + + tasklet_kill(&aes_dd->done_task); + tasklet_kill(&aes_dd->queue_task); + + if (aes_dd->irq > 0) + free_irq(aes_dd->irq, aes_dd); + + if (aes_dd->clk) { + clk_disable_unprepare(aes_dd->clk); + devm_clk_put(&pdev->dev, aes_dd->clk); + } + kfree(aes_dd); + aes_dd = NULL; + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id aml_aes_dt_match[] = { + { .compatible = "amlogic,aes", + }, + {}, +}; +#else +#define aml_aes_dt_match NULL +#endif + +static struct platform_driver aml_aes_driver = { + .probe = aml_aes_probe, + .remove = aml_aes_remove, + .driver = { + .name = "aml_aes_blkmv", + .owner = THIS_MODULE, + .of_match_table = aml_aes_dt_match, + }, +}; + +module_platform_driver(aml_aes_driver); + +MODULE_DESCRIPTION("Aml AES hw acceleration support."); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("matthew.shyu "); diff --git a/drivers/amlogic/crypto/aml-crypto-blkmv.c b/drivers/amlogic/crypto/aml-crypto-blkmv.c new file mode 100644 index 000000000000..b4e65fdd196b --- /dev/null +++ b/drivers/amlogic/crypto/aml-crypto-blkmv.c @@ -0,0 +1,213 @@ +/* + * drivers/amlogic/crypto/aml-crypto-blkmv.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aml-crypto-blkmv.h" + +// --------------------------------------------------------------------- +// DMA descriptor table support +// --------------------------------------------------------------------- +unsigned long swap_ulong32(unsigned long val) +{ + unsigned char *p = (unsigned char *)&val; + + return ((*p)<<24)+((*(p+1))<<16)+((*(p+2))<<8)+((*(p+3))<<0); +} + +// -------------------------------------------- +// NDMA_set_table_position +// -------------------------------------------- +void ndma_set_table_position(unsigned long thread_num, + unsigned long table_start, unsigned long size) +{ + unsigned long table_end; + + table_end = table_start + size; + switch (thread_num) { + case 3: + cbus_wr(NDMA_THREAD_TABLE_START3, table_start); + cbus_wr(NDMA_THREAD_TABLE_END3, table_end); + /* Pulse thread init to register the new start/end locations */ + cbus_wr(NDMA_THREAD_REG, + cbus_rd(NDMA_THREAD_REG) | (1 << 27)); + break; + case 2: + cbus_wr(NDMA_THREAD_TABLE_START2, table_start); + cbus_wr(NDMA_THREAD_TABLE_END2, table_end); + /* Pulse thread init to register the new start/end locations */ + cbus_wr(NDMA_THREAD_REG, + cbus_rd(NDMA_THREAD_REG) | (1 << 26)); + break; + case 1: + cbus_wr(NDMA_THREAD_TABLE_START1, table_start); + cbus_wr(NDMA_THREAD_TABLE_END1, table_end); + /* Pulse thread init to register the new start/end locations */ + cbus_wr(NDMA_THREAD_REG, + cbus_rd(NDMA_THREAD_REG) | (1 << 25)); + break; + case 0: + cbus_wr(NDMA_THREAD_TABLE_START0, table_start); + cbus_wr(NDMA_THREAD_TABLE_END0, table_end); + /* Pulse thread init to register the new start/end locations */ + cbus_wr(NDMA_THREAD_REG, + cbus_rd(NDMA_THREAD_REG) | (1 << 24)); + break; + } +} + +/* -------------------------------------------- + * ndma_add_descriptor_1d + * ------------------------------------------- + */ +void ndma_add_descriptor_1d( + unsigned long table, + unsigned long irq, + unsigned long restart, + unsigned long pre_endian, + unsigned long post_endian, + unsigned long type, + unsigned long bytes_to_move, + unsigned long inlinetype, + unsigned long src_addr, + unsigned long dest_addr) +{ + unsigned long *p = (unsigned long *) table; + unsigned long curr_key; + unsigned long keytype; + unsigned long cryptodir; + unsigned long mode; + + (*p++) = (0x01 << 30) | /* owned by CPU */ + (pre_endian << 27) | + (0 << 26) | + (0 << 25) | + (inlinetype << 22) | /* TDES in-place processing */ + (irq << 21) | + (0 << 0); /* thread slice */ + + (*p++) = src_addr; + + (*p++) = dest_addr; + + (*p++) = bytes_to_move & 0x01FFFFFF; + + (*p++) = 0x00000000; /* 2D entry */ + + (*p++) = 0x00000000; /* 2D entry */ + + /* Prepare the pointer for the next descriptor boundary + * inline processing + bytes to move extension + */ + switch (inlinetype) { + case INLINE_TYPE_TDES: + curr_key = (type & 0x3); + (*p++) = (restart << 6) | + (curr_key << 3) | + (post_endian << 0); + break; + case INLINE_TYPE_DIVX: + (*p++) = post_endian & 0x7; + break; + case INLINE_TYPE_CRC: + (*p++) = post_endian & 0x7; + break; + case INLINE_TYPE_AES: + keytype = (type & 0x3); + cryptodir = ((type >> 2) & 1); + mode = ((type >> 3) & 3); + (*p++) = (pre_endian << 0) | + (post_endian << 4) | + (keytype << 8) | + (cryptodir << 10)| + (((restart) ? 1 : 0) << 11)| + (mode << 12) | + (0 << 14) | /* 128 bit ctr */ + (0xf << 16); /* big endian */ + break; + default: + *p++ = 0; + break; + } + *p = 0; + +} + +void ndma_set_table_count(unsigned long thread_num, unsigned int cnt) +{ + cbus_wr(NDMA_TABLE_ADD_REG, (thread_num << 8) | (cnt << 0)); +} + +/* -------------------------------------------- + * ndma_start() + * -------------------------------------------- + * Start the block move procedure + */ +void ndma_start(unsigned long thread_num) +{ + /* Enable */ + cbus_wr(NDMA_CNTL_REG0, + (cbus_rd(NDMA_CNTL_REG0) | (1 << NDMA_ENABLE))); + cbus_wr(NDMA_THREAD_REG, + (cbus_rd(NDMA_THREAD_REG) + | (1 << (thread_num + 8)))); +} + +void ndma_stop(unsigned long thread_num) +{ + cbus_wr(NDMA_THREAD_REG, + cbus_rd(NDMA_THREAD_REG) + & ~(1 << (thread_num + 8))); + /* If no threads enabled, then shut down the DMA engine completely */ + if (!(cbus_rd(NDMA_THREAD_REG) & (0xF << 8))) + cbus_wr(NDMA_CNTL_REG0, + cbus_rd(NDMA_CNTL_REG0) + & ~(1 << NDMA_ENABLE)); +} + +/* -------------------------------------------- + * ndma_wait_for_completion() + * -------------------------------------------- + * Wait for all block moves to complete + */ + +void ndma_wait_for_completion(unsigned long thread_num) +{ + while ((cbus_rd(NDMA_TABLE_ADD_REG) & (0xF << (thread_num*8)))) + ; +} + diff --git a/drivers/amlogic/crypto/aml-crypto-blkmv.h b/drivers/amlogic/crypto/aml-crypto-blkmv.h new file mode 100644 index 000000000000..1d22bd89858d --- /dev/null +++ b/drivers/amlogic/crypto/aml-crypto-blkmv.h @@ -0,0 +1,113 @@ +/* + * drivers/amlogic/crypto/aml-crypto-blkmv.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _AML_CRYPTO_H_ +#define _AML_CRYPTO_H_ + +#include +#define AML_CRYPTO_DEBUG 0 + +#define AM_RING_OSC_REG0 0x207f + +#define cbus_wr(addr, data) \ + aml_write_cbus(addr, data) +#define cbus_rd(addr) \ + aml_read_cbus(addr) + +#define NDMA_TABLE_SIZE (32) +/* driver logic flags */ +#define TDES_KEY_LENGTH 32 +#define TDES_MIN_BLOCK_SIZE 8 + +#define MODE_ECB 0 +#define MODE_CBC 1 +#define MODE_CTR 2 + +#define DIR_ENCRYPT 0 +#define DIR_DECRYPT 1 + +#define INLINE_TYPE_NORMAL 0 +#define INLINE_TYPE_TDES 1 +#define INLINE_TYPE_DIVX 2 +#define INLINE_TYPE_CRC 3 +#define INLINE_TYPE_AES 4 + +#define NDMA_CNTL_REG0 0x2270 + #define NDMA_AES_STATUS 12 + #define NDMA_ENABLE 14 + #define NDMA_STATUS 26 +#define NDMA_TABLE_ADD_REG 0x2272 +#define NDMA_TDES_KEY_LO 0x2273 +#define NDMA_TDES_KEY_HI 0x2274 +#define NDMA_TDES_CONTROL 0x2275 +#define NDMA_AES_CONTROL 0x2276 +#define NDMA_AES_RK_FIFO 0x2277 +#define NDMA_CRC_OUT 0x2278 +#define NDMA_THREAD_REG 0x2279 +#define NDMA_THREAD_TABLE_START0 0x2280 +#define NDMA_THREAD_TABLE_CURR0 0x2281 +#define NDMA_THREAD_TABLE_END0 0x2282 +#define NDMA_THREAD_TABLE_START1 0x2283 +#define NDMA_THREAD_TABLE_CURR1 0x2284 +#define NDMA_THREAD_TABLE_END1 0x2285 +#define NDMA_THREAD_TABLE_START2 0x2286 +#define NDMA_THREAD_TABLE_CURR2 0x2287 +#define NDMA_THREAD_TABLE_END2 0x2288 +#define NDMA_THREAD_TABLE_START3 0x2289 +#define NDMA_THREAD_TABLE_CURR3 0x228a +#define NDMA_THREAD_TABLE_END3 0x228b +#define NDMA_CNTL_REG1 0x228c +#define NDMA_AES_KEY_0 0x2290 +#define NDMA_AES_KEY_1 0x2291 +#define NDMA_AES_KEY_2 0x2292 +#define NDMA_AES_KEY_3 0x2293 +#define NDMA_AES_KEY_4 0x2294 +#define NDMA_AES_KEY_5 0x2295 +#define NDMA_AES_KEY_6 0x2296 +#define NDMA_AES_KEY_7 0x2297 +#define NDMA_AES_IV_0 0x2298 +#define NDMA_AES_IV_1 0x2299 +#define NDMA_AES_IV_2 0x229a +#define NDMA_AES_IV_3 0x229b +#define NDMA_AES_REG0 0x229c + +#define TDES_THREAD_INDEX 2 +#define AES_THREAD_INDEX 3 +#define SEC_ALLOWED_MASK 0xc /* thread 0 and 1 are secure*/ + +unsigned long swap_ulong32(unsigned long val); +void ndma_set_table_position(unsigned long thread_num, + unsigned long table_start, unsigned long size); +void ndma_set_table_position_secure(unsigned long thread_num, + unsigned long table_start, unsigned long size); +void ndma_add_descriptor_1d( + unsigned long table, + unsigned long irq, + unsigned long restart, + unsigned long pre_endian, + unsigned long post_endian, + unsigned long type, + unsigned long bytes_to_move, + unsigned long inlinetype, + unsigned long src_addr, + unsigned long dest_addr); + +void ndma_set_table_count(unsigned long thread_num, unsigned int cnt); +void ndma_start(unsigned long thread_num); +void ndma_stop(unsigned long thread_num); +void ndma_wait_for_completion(unsigned long thread_num); +#endif diff --git a/drivers/amlogic/crypto/aml-tdes-blkmv.c b/drivers/amlogic/crypto/aml-tdes-blkmv.c new file mode 100644 index 000000000000..76e55b57adb6 --- /dev/null +++ b/drivers/amlogic/crypto/aml-tdes-blkmv.c @@ -0,0 +1,937 @@ +/* + * drivers/amlogic/crypto/aml-tdes-blkmv.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aml-crypto-blkmv.h" + +/* TDES flags */ +#define TDES_FLAGS_MODE_MASK 0x03 +#define TDES_FLAGS_ENCRYPT BIT(0) +#define TDES_FLAGS_CBC BIT(1) + +#define TDES_FLAGS_INIT BIT(8) +#define TDES_FLAGS_DMA BIT(9) +#define TDES_FLAGS_BUSY BIT(10) + +#define AML_TDES_QUEUE_LENGTH 50 +#define TDES_KEY_STARTING_IDX 0 + +unsigned char ndma_tdes_table_block[NDMA_TABLE_SIZE + 32]; +#define NDMA_TDES_TABLE_START (((unsigned long)ndma_tdes_table_block + 0x1f)\ + & (~0x1f)) +unsigned long ndma_tdes_table_ptr; + +struct aml_tdes_dev; + +struct aml_tdes_ctx { + struct aml_tdes_dev *dd; + + int keylen; + u32 key[3*DES_KEY_SIZE / sizeof(u32)]; + + u16 block_size; + u32 key_idx; +}; + +struct aml_tdes_reqctx { + unsigned long mode; +}; + +struct aml_tdes_dev { + struct list_head list; + + struct aml_tdes_ctx *ctx; + struct device *dev; + struct clk *clk; + int irq; + u32 idx; + + unsigned long flags; + int err; + + spinlock_t lock; + struct crypto_queue queue; + + struct tasklet_struct done_task; + struct tasklet_struct queue_task; + + struct ablkcipher_request *req; + size_t total; + + struct scatterlist *in_sg; + size_t in_offset; + struct scatterlist *out_sg; + size_t out_offset; + + size_t buflen; + size_t dma_size; + + void *buf_in; + dma_addr_t dma_addr_in; + + void *buf_out; + dma_addr_t dma_addr_out; + dma_addr_t dma_descript_tab; +}; + +struct aml_tdes_drv { + struct list_head dev_list; + spinlock_t lock; +}; + +static struct aml_tdes_drv aml_tdes = { + .dev_list = LIST_HEAD_INIT(aml_tdes.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(aml_tdes.lock), +}; + +static void write_key_iv(unsigned long addr, unsigned long data_hi, + unsigned long data_lo) +{ + unsigned long temp; + + temp = swap_ulong32(data_lo); + cbus_wr(NDMA_TDES_KEY_LO, temp); + temp = swap_ulong32(data_hi); + cbus_wr(NDMA_TDES_KEY_HI, temp); + + /* write the key */ + cbus_wr(NDMA_TDES_CONTROL, ((1 << 31) | (addr << 0))); +} + +static void write_modes(unsigned long idx, unsigned long cbc_en, + unsigned long decrypt, unsigned long des_modes) +{ + /* Write the key */ + cbus_wr(NDMA_TDES_CONTROL, ((1 << 30) + | (des_modes << 6) + | (cbc_en << 5) + | (decrypt << 4) + | idx * 4 << 0)); +} + + +static void set_tdes_key(u32 *key, u32 keylen, u32 idx, u32 dir) +{ + idx = (idx * 4); + if (dir) { + if (keylen == DES_KEY_SIZE * 2) { + write_key_iv(idx + 0, *(key), *(key + 1)); + write_key_iv(idx + 1, *(key + 1 * 2), + *(key + 1 * 2 + 1)); + write_key_iv(idx + 2, *(key), *(key + 1)); + } else { + write_key_iv(idx + 0, *(key + 2 * 2), + *(key + 2 * 2 + 1)); + write_key_iv(idx + 1, *(key + 1 * 2), + *(key + 1 * 2 + 1)); + write_key_iv(idx + 2, *(key), *(key + 1)); + } + } else { + if (keylen == DES_KEY_SIZE * 2) { + write_key_iv(idx + 0, *(key), *(key + 1)); + write_key_iv(idx + 1, *(key + 1 * 2), + *(key + 1 * 2 + 1)); + write_key_iv(idx + 2, *(key), *(key + 1)); + } else { + write_key_iv(idx + 0, *(key), *(key+1)); + write_key_iv(idx + 1, *(key + 1 * 2), + *(key + 1 * 2 + 1)); + write_key_iv(idx + 2, *(key + 2 * 2), + *(key + 2 * 2 + 1)); + } + } +} + +static void set_tdes_iv(u32 *iv, u32 idx) +{ + idx = (idx * 4); + if (!iv) + write_key_iv(idx + 3, 0x00000000, 0x00000000); + else + write_key_iv(idx + 3, *(iv), *(iv + 1)); +} + +static size_t aml_tdes_sg_copy(struct scatterlist **sg, size_t *offset, + void *buf, size_t buflen, size_t total, int out) +{ + size_t count = 0, off = 0; + + while (buflen && total) { + count = min((*sg)->length - *offset, total); + count = min(count, buflen); + + if (!count) + return off; + + scatterwalk_map_and_copy(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; +} + +static struct aml_tdes_dev *aml_tdes_find_dev(struct aml_tdes_ctx *ctx) +{ + struct aml_tdes_dev *tdes_dd = NULL; + struct aml_tdes_dev *tmp; + + spin_lock_bh(&aml_tdes.lock); + if (!ctx->dd) { + list_for_each_entry(tmp, &aml_tdes.dev_list, list) { + tdes_dd = tmp; + break; + } + ctx->dd = tdes_dd; + } else { + tdes_dd = ctx->dd; + } + + spin_unlock_bh(&aml_tdes.lock); + + return tdes_dd; +} + +static int aml_tdes_hw_init(struct aml_tdes_dev *dd) +{ + cbus_wr(AM_RING_OSC_REG0, 0x1); + cbus_wr(NDMA_CNTL_REG1, cbus_rd(NDMA_CNTL_REG1) & 0xfffffffe); + /* enable module clk */ + if (dd->clk) + clk_prepare_enable(dd->clk); + + if (!(dd->flags & TDES_FLAGS_INIT)) { + dd->flags |= TDES_FLAGS_INIT; + dd->err = 0; + } + + return 0; +} + +static void aml_tdes_finish_req(struct aml_tdes_dev *dd, int err) +{ + struct ablkcipher_request *req = dd->req; + + dd->flags &= ~TDES_FLAGS_BUSY; + if (req->base.complete) + req->base.complete(&req->base, err); +} + + +static int aml_tdes_crypt_dma(struct aml_tdes_dev *dd, + dma_addr_t dma_addr_in, dma_addr_t dma_addr_out, + int length, unsigned long restart) +{ + unsigned long mode; +#if AML_CRYPTO_DEBUG + int i = 0; +#endif + dd->dma_size = length; + + dma_sync_single_for_device(dd->dev, dma_addr_in, length, + DMA_TO_DEVICE); + + dd->flags |= TDES_FLAGS_DMA; + + mode = MODE_ECB; + + if (dd->flags & TDES_FLAGS_CBC) + mode = MODE_CBC; + + ndma_add_descriptor_1d( + ndma_tdes_table_ptr, /* table location */ + 1, /* irq */ + restart, /* restart */ + 7, /* pre_endian */ + 7, /* post_endian */ + dd->ctx->key_idx,/* keytype + cryptodir */ + dd->dma_size, /* bytes_to_move */ + INLINE_TYPE_TDES, /* inlinetype */ + dma_addr_in, /* src_addr */ + dma_addr_out); /* dest_addr */ + + dma_sync_single_for_device(dd->dev, dd->dma_descript_tab, + NDMA_TABLE_SIZE, DMA_TO_DEVICE); + ndma_set_table_position(TDES_THREAD_INDEX, + dd->dma_descript_tab, NDMA_TABLE_SIZE); + ndma_set_table_count(TDES_THREAD_INDEX, 1); + +#if AML_CRYPTO_DEBUG + for (i = 0x2270; i < 0x229d; i++) + pr_err("reg(%4x) = 0x%8x\n", i, cbus_rd(i)); + + for (i = 0; i < 8; i++) { + pr_err("table addr(%d) = 0x%8lx\n", i, + *(unsigned long *)(ndma_tdes_table_ptr + i * 4)); + } +#endif + + ndma_start(TDES_THREAD_INDEX); + + return 0; +} + +static int aml_tdes_crypt_dma_start(struct aml_tdes_dev *dd, + unsigned long restart) +{ + int err = 0; + size_t count; + dma_addr_t addr_in, addr_out; + + /* use cache buffers */ + count = aml_tdes_sg_copy(&dd->in_sg, &dd->in_offset, + dd->buf_in, dd->buflen, dd->total, 0); + addr_in = dd->dma_addr_in; + addr_out = dd->dma_addr_out; + dd->total -= count; + + err = aml_tdes_crypt_dma(dd, addr_in, addr_out, count, restart); + + return err; +} + +static int aml_tdes_write_ctrl(struct aml_tdes_dev *dd) +{ + int err = 0; + unsigned int tdes_mode = 0; + unsigned int crypt = 0; + + err = aml_tdes_hw_init(dd); + if (err) + return err; + + if (dd->flags & TDES_FLAGS_CBC) + tdes_mode = 1; + else + tdes_mode = 0; + + if (dd->flags & TDES_FLAGS_ENCRYPT) + crypt = 0; + else + crypt = 1; + + write_modes(dd->ctx->key_idx, + tdes_mode, /* cbc_en */ + crypt, /* decrypt */ + (crypt == 1) ? 5 : 2); /* des_modes */ + + set_tdes_key(dd->ctx->key, dd->ctx->keylen, + dd->ctx->key_idx, crypt); + if (dd->flags & TDES_FLAGS_CBC) + set_tdes_iv(dd->req->info, dd->ctx->key_idx); + else + set_tdes_iv(NULL, dd->ctx->key_idx); + + return err; +} + +static int aml_tdes_handle_queue(struct aml_tdes_dev *dd, + struct ablkcipher_request *req) +{ + struct crypto_async_request *async_req, *backlog; + struct aml_tdes_ctx *ctx; + struct aml_tdes_reqctx *rctx; + unsigned long flags; + int err, ret = 0; + + spin_lock_irqsave(&dd->lock, flags); + if (req) + ret = ablkcipher_enqueue_request(&dd->queue, req); + + if (dd->flags & TDES_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 |= TDES_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->in_offset = 0; + dd->in_sg = req->src; + dd->out_offset = 0; + dd->out_sg = req->dst; + + rctx = ablkcipher_request_ctx(req); + ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); + rctx->mode &= TDES_FLAGS_MODE_MASK; + dd->flags = (dd->flags & ~TDES_FLAGS_MODE_MASK) | rctx->mode; + dd->ctx = ctx; + ctx->dd = dd; + + err = aml_tdes_write_ctrl(dd); + if (!err) + err = aml_tdes_crypt_dma_start(dd, 1); + + if (err) { + /* tdes_task will not finish it, so do it here */ + aml_tdes_finish_req(dd, err); + tasklet_schedule(&dd->queue_task); + } + + return ret; +} + +static int aml_tdes_crypt_dma_stop(struct aml_tdes_dev *dd) +{ + int err = -EBUSY; + size_t count; + + if (dd->flags & TDES_FLAGS_DMA) { + err = 0; + + dma_sync_single_for_device(dd->dev, dd->dma_descript_tab, + NDMA_TABLE_SIZE, DMA_FROM_DEVICE); + dma_sync_single_for_device(dd->dev, dd->dma_addr_out, + dd->dma_size, DMA_FROM_DEVICE); + + /* copy data */ + count = aml_tdes_sg_copy(&dd->out_sg, &dd->out_offset, + dd->buf_out, dd->buflen, dd->dma_size, 1); + + if (count != dd->dma_size) { + err = -EBUSY; + pr_err("not all data converted: %u\n", count); + } + dd->flags &= ~TDES_FLAGS_DMA; + } + + return err; +} + + +static int aml_tdes_buff_init(struct aml_tdes_dev *dd) +{ + int err = -ENOMEM; + + dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, 0); + dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, 0); + dd->buflen = PAGE_SIZE; + dd->buflen &= ~(DES_BLOCK_SIZE - 1); + + if (!dd->buf_in || !dd->buf_out) { + dev_err(dd->dev, "unable to alloc pages.\n"); + goto err_alloc; + } + + /* MAP here */ + dd->dma_addr_in = dma_map_single(dd->dev, dd->buf_in, + dd->buflen, DMA_TO_DEVICE); + if (dma_mapping_error(dd->dev, dd->dma_addr_in)) { + dev_err(dd->dev, "dma %d bytes error\n", dd->buflen); + err = -EINVAL; + goto err_map_in; + } + + dd->dma_addr_out = dma_map_single(dd->dev, dd->buf_out, + dd->buflen, DMA_FROM_DEVICE); + if (dma_mapping_error(dd->dev, dd->dma_addr_out)) { + dev_err(dd->dev, "dma %d bytes error\n", dd->buflen); + err = -EINVAL; + goto err_map_out; + } + + dd->dma_descript_tab = dma_map_single(dd->dev, + (void *)ndma_tdes_table_ptr, + NDMA_TABLE_SIZE, DMA_TO_DEVICE); + + if (dma_mapping_error(dd->dev, dd->dma_descript_tab)) { + dev_err(dd->dev, "dma descriptor error\n"); + err = -EINVAL; + goto err_map_descriptor; + } + + return 0; + +err_map_descriptor: + dma_unmap_single(dd->dev, dd->dma_descript_tab, NDMA_TABLE_SIZE, + DMA_TO_DEVICE); + +err_map_out: + dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, + DMA_TO_DEVICE); +err_map_in: + free_page((unsigned long)dd->buf_out); + free_page((unsigned long)dd->buf_in); +err_alloc: + if (err) + pr_err("error: %d\n", err); + return err; +} + +static void aml_tdes_buff_cleanup(struct aml_tdes_dev *dd) +{ + dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, + DMA_FROM_DEVICE); + dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, + DMA_TO_DEVICE); + free_page((unsigned long)dd->buf_out); + free_page((unsigned long)dd->buf_in); +} + +static int aml_tdes_crypt(struct ablkcipher_request *req, unsigned long mode) +{ + struct aml_tdes_ctx *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + struct aml_tdes_reqctx *rctx = ablkcipher_request_ctx(req); + struct aml_tdes_dev *dd; + + if (!IS_ALIGNED(req->nbytes, DES_BLOCK_SIZE)) { + pr_err("request size is not exact amount of TDES blocks\n"); + return -EINVAL; + } + ctx->block_size = DES_BLOCK_SIZE; + + dd = aml_tdes_find_dev(ctx); + if (!dd) + return -ENODEV; + + ctx->key_idx = dd->idx; + dd->idx = (dd->idx + 1) % 4; + rctx->mode = mode; + + return aml_tdes_handle_queue(dd, req); +} + +static int aml_des_setkey(struct crypto_ablkcipher *tfm, const u8 *key, + unsigned int keylen) +{ + u32 tmp[DES_EXPKEY_WORDS]; + int err; + struct crypto_tfm *ctfm = crypto_ablkcipher_tfm(tfm); + struct aml_tdes_ctx *ctx = crypto_ablkcipher_ctx(tfm); + + if (keylen != DES_KEY_SIZE) { + crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + err = des_ekey(tmp, key); + if (err == 0 && (ctfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) { + ctfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY; + return -EINVAL; + } + + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + + return 0; +} + +static int aml_tdes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct aml_tdes_ctx *ctx = crypto_ablkcipher_ctx(tfm); + + if ((keylen != 2*DES_KEY_SIZE) && (keylen != 3*DES_KEY_SIZE)) { + crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + + return 0; +} + +static int aml_tdes_ecb_encrypt(struct ablkcipher_request *req) +{ + return aml_tdes_crypt(req, + TDES_FLAGS_ENCRYPT); +} + +static int aml_tdes_ecb_decrypt(struct ablkcipher_request *req) +{ + return aml_tdes_crypt(req, + 0); +} + +static int aml_tdes_cbc_encrypt(struct ablkcipher_request *req) +{ + return aml_tdes_crypt(req, + TDES_FLAGS_ENCRYPT | TDES_FLAGS_CBC); +} + +static int aml_tdes_cbc_decrypt(struct ablkcipher_request *req) +{ + return aml_tdes_crypt(req, + TDES_FLAGS_CBC); +} + +static int aml_tdes_cra_init(struct crypto_tfm *tfm) +{ + tfm->crt_ablkcipher.reqsize = sizeof(struct aml_tdes_reqctx); + + return 0; +} + +static void aml_tdes_cra_exit(struct crypto_tfm *tfm) +{ +} + +static struct crypto_alg tdes_algs[] = { + { + .cra_name = "ecb(des)", + .cra_driver_name = "ecb-des-aml", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = DES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct aml_tdes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = aml_tdes_cra_init, + .cra_exit = aml_tdes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .setkey = aml_des_setkey, + .encrypt = aml_tdes_ecb_encrypt, + .decrypt = aml_tdes_ecb_decrypt, + } + }, + { + .cra_name = "cbc(des)", + .cra_driver_name = "cbc-des-aml", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = DES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct aml_tdes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = aml_tdes_cra_init, + .cra_exit = aml_tdes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + .setkey = aml_des_setkey, + .encrypt = aml_tdes_cbc_encrypt, + .decrypt = aml_tdes_cbc_decrypt, + } + }, + { + .cra_name = "ecb(des3_ede)", + .cra_driver_name = "ecb-tdes-aml", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = DES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct aml_tdes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = aml_tdes_cra_init, + .cra_exit = aml_tdes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = 2 * DES_KEY_SIZE, + .max_keysize = 3 * DES_KEY_SIZE, + .setkey = aml_tdes_setkey, + .encrypt = aml_tdes_ecb_encrypt, + .decrypt = aml_tdes_ecb_decrypt, + } + }, + { + .cra_name = "cbc(des3_ede)", + .cra_driver_name = "cbc-tdes-aml", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = DES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct aml_tdes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = aml_tdes_cra_init, + .cra_exit = aml_tdes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = 2 * DES_KEY_SIZE, + .max_keysize = 3 * DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + .setkey = aml_tdes_setkey, + .encrypt = aml_tdes_cbc_encrypt, + .decrypt = aml_tdes_cbc_decrypt, + } + } +}; + +static void aml_tdes_queue_task(unsigned long data) +{ + struct aml_tdes_dev *dd = (struct aml_tdes_dev *)data; + + aml_tdes_handle_queue(dd, NULL); +} + +static void aml_tdes_done_task(unsigned long data) +{ + struct aml_tdes_dev *dd = (struct aml_tdes_dev *) data; + int err; +#if AML_CRYPTO_DEBUG + int i = 0; +#endif + + err = aml_tdes_crypt_dma_stop(dd); + +#if AML_CRYPTO_DEBUG + for (i = 0x2270; i < 0x228D; i++) + pr_err("reg(%4x) = 0x%8x\n", i, cbus_rd(i)); + + for (i = 0; i < 8; i++) { + pr_err("table addr(%d) = 0x%8lx\n", i, + *(unsigned long *)(ndma_tdes_table_ptr + i * 4)); + } +#endif + err = dd->err ? : err; + + if (dd->total && !err) { + if (!err) + err = aml_tdes_crypt_dma_start(dd, 0); + if (!err) + return; /* DMA started. Not fininishing. */ + } + + aml_tdes_finish_req(dd, err); + aml_tdes_handle_queue(dd, NULL); +} + +static irqreturn_t aml_tdes_irq(int irq, void *dev_id) +{ + struct aml_tdes_dev *tdes_dd = dev_id; + + if (!(cbus_rd(NDMA_TABLE_ADD_REG) & (0xF << (TDES_THREAD_INDEX * 8))) + && (cbus_rd(NDMA_THREAD_REG) & (1 << (TDES_THREAD_INDEX + 8)))) { + if (TDES_FLAGS_BUSY & tdes_dd->flags) { + ndma_stop(TDES_THREAD_INDEX); + tasklet_schedule(&tdes_dd->done_task); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } + } + + return IRQ_NONE; +} + +static void aml_tdes_unregister_algs(struct aml_tdes_dev *dd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tdes_algs); i++) + crypto_unregister_alg(&tdes_algs[i]); +} + +static int aml_tdes_register_algs(struct aml_tdes_dev *dd) +{ + int err, i, j; + + for (i = 0; i < ARRAY_SIZE(tdes_algs); i++) { + err = crypto_register_alg(&tdes_algs[i]); + if (err) + goto err_tdes_algs; + } + + return 0; + +err_tdes_algs: + for (j = 0; j < i; j++) + crypto_unregister_alg(&tdes_algs[j]); + + return err; +} + +static int aml_tdes_probe(struct platform_device *pdev) +{ + struct aml_tdes_dev *tdes_dd; + struct device *dev = &pdev->dev; + struct resource *res_irq = 0; + int err; + + ndma_tdes_table_ptr = NDMA_TDES_TABLE_START; + + tdes_dd = kzalloc(sizeof(struct aml_tdes_dev), GFP_KERNEL); + if (tdes_dd == NULL) { + err = -ENOMEM; + goto tdes_dd_err; + } + + tdes_dd->dev = dev; + + platform_set_drvdata(pdev, tdes_dd); + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + tdes_dd->clk = devm_clk_get(&pdev->dev, "blkmv"); + + INIT_LIST_HEAD(&tdes_dd->list); + + tasklet_init(&tdes_dd->done_task, aml_tdes_done_task, + (unsigned long)tdes_dd); + tasklet_init(&tdes_dd->queue_task, aml_tdes_queue_task, + (unsigned long)tdes_dd); + + crypto_init_queue(&tdes_dd->queue, AML_TDES_QUEUE_LENGTH); + + tdes_dd->irq = res_irq->start; + + err = request_irq(tdes_dd->irq, aml_tdes_irq, IRQF_SHARED, "aml-tdes", + tdes_dd); + if (err) { + dev_err(dev, "unable to request tdes irq.\n"); + goto tdes_irq_err; + } + + tdes_dd->idx = 0; + err = aml_tdes_hw_init(tdes_dd); + if (err) + goto err_tdes_buff; + + err = aml_tdes_buff_init(tdes_dd); + if (err) + goto err_tdes_buff; + + spin_lock(&aml_tdes.lock); + list_add_tail(&tdes_dd->list, &aml_tdes.dev_list); + spin_unlock(&aml_tdes.lock); + + err = aml_tdes_register_algs(tdes_dd); + if (err) + goto err_algs; + + dev_info(dev, "Aml TDES\n"); + + return 0; + +err_algs: + spin_lock(&aml_tdes.lock); + list_del(&tdes_dd->list); + spin_unlock(&aml_tdes.lock); + aml_tdes_buff_cleanup(tdes_dd); +err_tdes_buff: + free_irq(tdes_dd->irq, tdes_dd); +tdes_irq_err: + tasklet_kill(&tdes_dd->done_task); + tasklet_kill(&tdes_dd->queue_task); + if (tdes_dd->clk) { + clk_disable_unprepare(tdes_dd->clk); + devm_clk_put(&pdev->dev, tdes_dd->clk); + } + kfree(tdes_dd); + tdes_dd = NULL; +tdes_dd_err: + dev_err(dev, "initialization failed.\n"); + + return err; +} + +static int aml_tdes_remove(struct platform_device *pdev) +{ + static struct aml_tdes_dev *tdes_dd; + + tdes_dd = platform_get_drvdata(pdev); + if (!tdes_dd) + return -ENODEV; + spin_lock(&aml_tdes.lock); + list_del(&tdes_dd->list); + spin_unlock(&aml_tdes.lock); + + aml_tdes_unregister_algs(tdes_dd); + + tasklet_kill(&tdes_dd->done_task); + tasklet_kill(&tdes_dd->queue_task); + + if (tdes_dd->irq > 0) + free_irq(tdes_dd->irq, tdes_dd); + + if (tdes_dd->clk) { + clk_disable_unprepare(tdes_dd->clk); + devm_clk_put(&pdev->dev, tdes_dd->clk); + } + kfree(tdes_dd); + tdes_dd = NULL; + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id aml_tdes_dt_match[] = { + { .compatible = "amlogic,des,tdes", + }, + {}, +}; +#else +#define aml_tdes_dt_match NULL +#endif + +static struct platform_driver aml_tdes_driver = { + .probe = aml_tdes_probe, + .remove = aml_tdes_remove, + .driver = { + .name = "aml_tdes_blkmv", + .owner = THIS_MODULE, + .of_match_table = aml_tdes_dt_match, + }, +}; + +module_platform_driver(aml_tdes_driver); + +MODULE_DESCRIPTION("Aml TDES hw acceleration support."); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("matthew.shyu ");