aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-07-26 13:00:59 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-26 13:00:59 -0700
commit44a6b8442190cf213081060b610dae2e822f802b (patch)
tree2280bfe385bef8b6416a6493ea8988a975008165 /drivers
parent945c40c6b007eb4b07374a38ea37b2a34da306b1 (diff)
parenta43478863b16cb0986fd2ec9d1f1b9ebaaec5922 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
Pull crypto updates from Herbert Xu: - Fixed algorithm construction hang when self-test fails. - Added SHA variants to talitos AEAD list. - New driver for Exynos random number generator. - Performance enhancements for arc4. - Added hwrng support to caam. - Added ahash support to caam. - Fixed bad kfree in aesni-intel. - Allow aesni-intel in FIPS mode. - Added atmel driver with support for AES/3DES/SHA. - Bug fixes for mv_cesa. - CRC hardware driver for BF60x family processors. * git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6: (66 commits) crypto: twofish-avx - remove useless instruction crypto: testmgr - add aead cbc aes hmac sha1,256,512 test vectors crypto: talitos - add sha224, sha384 and sha512 to existing AEAD algorithms crypto: talitos - export the talitos_submit function crypto: talitos - move talitos structures to header file crypto: atmel - add new tests to tcrypt crypto: atmel - add Atmel SHA1/SHA256 driver crypto: atmel - add Atmel DES/TDES driver crypto: atmel - add Atmel AES driver ARM: AT91SAM9G45: add crypto peripherals crypto: testmgr - allow aesni-intel and ghash_clmulni-intel in fips mode hwrng: exynos - Add support for Exynos random number generator crypto: aesni-intel - fix wrong kfree pointer crypto: caam - ERA retrieval and printing for SEC device crypto: caam - Using alloc_coherent for caam job rings crypto: algapi - Fix hang on crypto allocation crypto: arc4 - now arc needs blockcipher support crypto: caam - one tasklet per job ring crypto: caam - consolidate memory barriers from job ring en/dequeue crypto: caam - only query h/w in job ring dequeue path ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/hw_random/Kconfig12
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/exynos-rng.c182
-rw-r--r--drivers/char/hw_random/mxc-rnga.c21
-rw-r--r--drivers/crypto/Kconfig56
-rw-r--r--drivers/crypto/Makefile7
-rw-r--r--drivers/crypto/atmel-aes-regs.h62
-rw-r--r--drivers/crypto/atmel-aes.c1206
-rw-r--r--drivers/crypto/atmel-sha-regs.h46
-rw-r--r--drivers/crypto/atmel-sha.c1112
-rw-r--r--drivers/crypto/atmel-tdes-regs.h89
-rw-r--r--drivers/crypto/atmel-tdes.c1215
-rw-r--r--drivers/crypto/bfin_crc.c780
-rw-r--r--drivers/crypto/caam/Kconfig30
-rw-r--r--drivers/crypto/caam/Makefile4
-rw-r--r--drivers/crypto/caam/caamalg.c572
-rw-r--r--drivers/crypto/caam/caamhash.c1878
-rw-r--r--drivers/crypto/caam/caamrng.c309
-rw-r--r--drivers/crypto/caam/compat.h2
-rw-r--r--drivers/crypto/caam/ctrl.c179
-rw-r--r--drivers/crypto/caam/ctrl.h13
-rw-r--r--drivers/crypto/caam/desc.h31
-rw-r--r--drivers/crypto/caam/desc_constr.h57
-rw-r--r--drivers/crypto/caam/error.c44
-rw-r--r--drivers/crypto/caam/intern.h6
-rw-r--r--drivers/crypto/caam/jr.c115
-rw-r--r--drivers/crypto/caam/key_gen.c122
-rw-r--r--drivers/crypto/caam/key_gen.h17
-rw-r--r--drivers/crypto/caam/pdb.h401
-rw-r--r--drivers/crypto/caam/regs.h38
-rw-r--r--drivers/crypto/caam/sg_sw_sec4.h156
-rw-r--r--drivers/crypto/mv_cesa.c61
-rw-r--r--drivers/crypto/talitos.c283
-rw-r--r--drivers/crypto/talitos.h123
34 files changed, 8590 insertions, 640 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index f45dad39a18..b01d6732824 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -263,3 +263,15 @@ config HW_RANDOM_PSERIES
module will be called pseries-rng.
If unsure, say Y.
+
+config HW_RANDOM_EXYNOS
+ tristate "EXYNOS HW random number generator support"
+ depends on HW_RANDOM && HAS_IOMEM && HAVE_CLK
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on EXYNOS SOCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called exynos-rng.
+
+ If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index d901dfa3032..8d6d173b65e 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
+obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c
new file mode 100644
index 00000000000..232ba9ce579
--- /dev/null
+++ b/drivers/char/hw_random/exynos-rng.c
@@ -0,0 +1,182 @@
+/*
+ * exynos-rng.c - Random Number Generator driver for the exynos
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Jonghwa Lee <jonghwa3.lee@smasung.com>
+ *
+ * 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;
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/hw_random.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/err.h>
+
+#define EXYNOS_PRNG_STATUS_OFFSET 0x10
+#define EXYNOS_PRNG_SEED_OFFSET 0x140
+#define EXYNOS_PRNG_OUT1_OFFSET 0x160
+#define SEED_SETTING_DONE BIT(1)
+#define PRNG_START 0x18
+#define PRNG_DONE BIT(5)
+#define EXYNOS_AUTOSUSPEND_DELAY 100
+
+struct exynos_rng {
+ struct device *dev;
+ struct hwrng rng;
+ void __iomem *mem;
+ struct clk *clk;
+};
+
+static u32 exynos_rng_readl(struct exynos_rng *rng, u32 offset)
+{
+ return __raw_readl(rng->mem + offset);
+}
+
+static void exynos_rng_writel(struct exynos_rng *rng, u32 val, u32 offset)
+{
+ __raw_writel(val, rng->mem + offset);
+}
+
+static int exynos_init(struct hwrng *rng)
+{
+ struct exynos_rng *exynos_rng = container_of(rng,
+ struct exynos_rng, rng);
+ int i;
+ int ret = 0;
+
+ pm_runtime_get_sync(exynos_rng->dev);
+
+ for (i = 0 ; i < 5 ; i++)
+ exynos_rng_writel(exynos_rng, jiffies,
+ EXYNOS_PRNG_SEED_OFFSET + 4*i);
+
+ if (!(exynos_rng_readl(exynos_rng, EXYNOS_PRNG_STATUS_OFFSET)
+ & SEED_SETTING_DONE))
+ ret = -EIO;
+
+ pm_runtime_put_noidle(exynos_rng->dev);
+
+ return ret;
+}
+
+static int exynos_read(struct hwrng *rng, void *buf,
+ size_t max, bool wait)
+{
+ struct exynos_rng *exynos_rng = container_of(rng,
+ struct exynos_rng, rng);
+ u32 *data = buf;
+
+ pm_runtime_get_sync(exynos_rng->dev);
+
+ exynos_rng_writel(exynos_rng, PRNG_START, 0);
+
+ while (!(exynos_rng_readl(exynos_rng,
+ EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE))
+ cpu_relax();
+
+ exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET);
+
+ *data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET);
+
+ pm_runtime_mark_last_busy(exynos_rng->dev);
+ pm_runtime_autosuspend(exynos_rng->dev);
+
+ return 4;
+}
+
+static int __devinit exynos_rng_probe(struct platform_device *pdev)
+{
+ struct exynos_rng *exynos_rng;
+
+ exynos_rng = devm_kzalloc(&pdev->dev, sizeof(struct exynos_rng),
+ GFP_KERNEL);
+ if (!exynos_rng)
+ return -ENOMEM;
+
+ exynos_rng->dev = &pdev->dev;
+ exynos_rng->rng.name = "exynos";
+ exynos_rng->rng.init = exynos_init;
+ exynos_rng->rng.read = exynos_read;
+ exynos_rng->clk = devm_clk_get(&pdev->dev, "secss");
+ if (IS_ERR(exynos_rng->clk)) {
+ dev_err(&pdev->dev, "Couldn't get clock.\n");
+ return -ENOENT;
+ }
+
+ exynos_rng->mem = devm_request_and_ioremap(&pdev->dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ if (!exynos_rng->mem)
+ return -EBUSY;
+
+ platform_set_drvdata(pdev, exynos_rng);
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, EXYNOS_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ return hwrng_register(&exynos_rng->rng);
+}
+
+static int __devexit exynos_rng_remove(struct platform_device *pdev)
+{
+ struct exynos_rng *exynos_rng = platform_get_drvdata(pdev);
+
+ hwrng_unregister(&exynos_rng->rng);
+
+ return 0;
+}
+
+static int exynos_rng_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct exynos_rng *exynos_rng = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(exynos_rng->clk);
+
+ return 0;
+}
+
+static int exynos_rng_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct exynos_rng *exynos_rng = platform_get_drvdata(pdev);
+
+ return clk_prepare_enable(exynos_rng->clk);
+}
+
+
+UNIVERSAL_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_runtime_suspend,
+ exynos_rng_runtime_resume, NULL);
+
+static struct platform_driver exynos_rng_driver = {
+ .driver = {
+ .name = "exynos-rng",
+ .owner = THIS_MODULE,
+ .pm = &exynos_rng_pm_ops,
+ },
+ .probe = exynos_rng_probe,
+ .remove = __devexit_p(exynos_rng_remove),
+};
+
+module_platform_driver(exynos_rng_driver);
+
+MODULE_DESCRIPTION("EXYNOS 4 H/W Random Number Generator driver");
+MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c
index 187c6be80f4..85074de5042 100644
--- a/drivers/char/hw_random/mxc-rnga.c
+++ b/drivers/char/hw_random/mxc-rnga.c
@@ -24,6 +24,7 @@
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <linux/io.h>
/* RNGA Registers */
@@ -60,16 +61,20 @@
static struct platform_device *rng_dev;
-static int mxc_rnga_data_present(struct hwrng *rng)
+static int mxc_rnga_data_present(struct hwrng *rng, int wait)
{
- int level;
void __iomem *rng_base = (void __iomem *)rng->priv;
-
- /* how many random numbers is in FIFO? [0-16] */
- level = ((__raw_readl(rng_base + RNGA_STATUS) &
- RNGA_STATUS_LEVEL_MASK) >> 8);
-
- return level > 0 ? 1 : 0;
+ int i;
+
+ for (i = 0; i < 20; i++) {
+ /* how many random numbers are in FIFO? [0-16] */
+ int level = (__raw_readl(rng_base + RNGA_STATUS) &
+ RNGA_STATUS_LEVEL_MASK) >> 8;
+ if (level || !wait)
+ return !!level;
+ udelay(10);
+ }
+ return 0;
}
static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 1092a770482..7d74d092aa8 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -298,7 +298,7 @@ config CRYPTO_DEV_TEGRA_AES
will be called tegra-aes.
config CRYPTO_DEV_NX
- tristate "Support for Power7+ in-Nest cryptographic accleration"
+ tristate "Support for Power7+ in-Nest cryptographic acceleration"
depends on PPC64 && IBMVIO
select CRYPTO_AES
select CRYPTO_CBC
@@ -325,4 +325,58 @@ if CRYPTO_DEV_UX500
source "drivers/crypto/ux500/Kconfig"
endif # if CRYPTO_DEV_UX500
+config CRYPTO_DEV_BFIN_CRC
+ tristate "Support for Blackfin CRC hardware"
+ depends on BF60x
+ help
+ Newer Blackfin processors have CRC hardware. Select this if you
+ want to use the Blackfin CRC module.
+
+config CRYPTO_DEV_ATMEL_AES
+ tristate "Support for Atmel AES hw accelerator"
+ depends on ARCH_AT91
+ select CRYPTO_CBC
+ select CRYPTO_ECB
+ select CRYPTO_AES
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ select CONFIG_AT_HDMAC
+ help
+ Some Atmel processors have AES hw accelerator.
+ Select this if you want to use the Atmel module for
+ AES algorithms.
+
+ To compile this driver as a module, choose M here: the module
+ will be called atmel-aes.
+
+config CRYPTO_DEV_ATMEL_TDES
+ tristate "Support for Atmel DES/TDES hw accelerator"
+ depends on ARCH_AT91
+ select CRYPTO_DES
+ select CRYPTO_CBC
+ select CRYPTO_ECB
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ help
+ Some Atmel processors have DES/TDES hw accelerator.
+ Select this if you want to use the Atmel module for
+ DES/TDES algorithms.
+
+ To compile this driver as a module, choose M here: the module
+ will be called atmel-tdes.
+
+config CRYPTO_DEV_ATMEL_SHA
+ tristate "Support for Atmel SHA1/SHA256 hw accelerator"
+ depends on ARCH_AT91
+ select CRYPTO_SHA1
+ select CRYPTO_SHA256
+ select CRYPTO_ALGAPI
+ help
+ Some Atmel processors have SHA1/SHA256 hw accelerator.
+ Select this if you want to use the Atmel module for
+ SHA1/SHA256 algorithms.
+
+ To compile this driver as a module, choose M here: the module
+ will be called atmel-sha.
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 01390325d72..880a47b0b02 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -14,4 +14,9 @@ obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o
obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o
obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o
obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o
-obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/ \ No newline at end of file
+obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/
+obj-$(CONFIG_CRYPTO_DEV_BFIN_CRC) += bfin_crc.o
+obj-$(CONFIG_CRYPTO_DEV_NX) += nx/
+obj-$(CONFIG_CRYPTO_DEV_ATMEL_AES) += atmel-aes.o
+obj-$(CONFIG_CRYPTO_DEV_ATMEL_TDES) += atmel-tdes.o
+obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA) += atmel-sha.o
diff --git a/drivers/crypto/atmel-aes-regs.h b/drivers/crypto/atmel-aes-regs.h
new file mode 100644
index 00000000000..2786bb1a5aa
--- /dev/null
+++ b/drivers/crypto/atmel-aes-regs.h
@@ -0,0 +1,62 @@
+#ifndef __ATMEL_AES_REGS_H__
+#define __ATMEL_AES_REGS_H__
+
+#define AES_CR 0x00
+#define AES_CR_START (1 << 0)
+#define AES_CR_SWRST (1 << 8)
+#define AES_CR_LOADSEED (1 << 16)
+
+#define AES_MR 0x04
+#define AES_MR_CYPHER_DEC (0 << 0)
+#define AES_MR_CYPHER_ENC (1 << 0)
+#define AES_MR_DUALBUFF (1 << 3)
+#define AES_MR_PROCDLY_MASK (0xF << 4)
+#define AES_MR_PROCDLY_OFFSET 4
+#define AES_MR_SMOD_MASK (0x3 << 8)
+#define AES_MR_SMOD_MANUAL (0x0 << 8)
+#define AES_MR_SMOD_AUTO (0x1 << 8)
+#define AES_MR_SMOD_IDATAR0 (0x2 << 8)
+#define AES_MR_KEYSIZE_MASK (0x3 << 10)
+#define AES_MR_KEYSIZE_128 (0x0 << 10)
+#define AES_MR_KEYSIZE_192 (0x1 << 10)
+#define AES_MR_KEYSIZE_256 (0x2 << 10)
+#define AES_MR_OPMOD_MASK (0x7 << 12)
+#define AES_MR_OPMOD_ECB (0x0 << 12)
+#define AES_MR_OPMOD_CBC (0x1 << 12)
+#define AES_MR_OPMOD_OFB (0x2 << 12)
+#define AES_MR_OPMOD_CFB (0x3 << 12)
+#define AES_MR_OPMOD_CTR (0x4 << 12)
+#define AES_MR_LOD (0x1 << 15)
+#define AES_MR_CFBS_MASK (0x7 << 16)
+#define AES_MR_CFBS_128b (0x0 << 16)
+#define AES_MR_CFBS_64b (0x1 << 16)
+#define AES_MR_CFBS_32b (0x2 << 16)
+#define AES_MR_CFBS_16b (0x3 << 16)
+#define AES_MR_CFBS_8b (0x4 << 16)
+#define AES_MR_CKEY_MASK (0xF << 20)
+#define AES_MR_CKEY_OFFSET 20
+#define AES_MR_CMTYP_MASK (0x1F << 24)
+#define AES_MR_CMTYP_OFFSET 24
+
+#define AES_IER 0x10
+#define AES_IDR 0x14
+#define AES_IMR 0x18
+#define AES_ISR 0x1C
+#define AES_INT_DATARDY (1 << 0)
+#define AES_INT_URAD (1 << 8)
+#define AES_ISR_URAT_MASK (0xF << 12)
+#define AES_ISR_URAT_IDR_WR_PROC (0x0 << 12)
+#define AES_ISR_URAT_ODR_RD_PROC (0x1 << 12)
+#define AES_ISR_URAT_MR_WR_PROC (0x2 << 12)
+#define AES_ISR_URAT_ODR_RD_SUBK (0x3 << 12)
+#define AES_ISR_URAT_MR_WR_SUBK (0x4 << 12)
+#define AES_ISR_URAT_WOR_RD (0x5 << 12)
+
+#define AES_KEYWR(x) (0x20 + ((x) * 0x04))
+#define AES_IDATAR(x) (0x40 + ((x) * 0x04))
+#define AES_ODATAR(x) (0x50 + ((x) * 0x04))
+#define AES_IVR(x) (0x60 + ((x) * 0x04))
+
+#define AES_HW_VERSION 0xFC
+
+#endif /* __ATMEL_AES_REGS_H__ */
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
new file mode 100644
index 00000000000..6bb20fffbf4
--- /dev/null
+++ b/drivers/crypto/atmel-aes.c
@@ -0,0 +1,1206 @@
+/*
+ * Cryptographic API.
+ *
+ * Support for ATMEL AES HW acceleration.
+ *
+ * Copyright (c) 2012 Eukréa Electromatique - ATMEL
+ * Author: Nicolas Royer <nicolas@eukrea.com>
+ *
+ * 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.
+ *
+ * Some ideas are from omap-aes.c driver.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/hw_random.h>
+#include <linux/platform_device.h>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/crypto.h>
+#include <linux/cryptohash.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+#include <linux/platform_data/atmel-aes.h>
+#include "atmel-aes-regs.h"
+
+#define CFB8_BLOCK_SIZE 1
+#define CFB16_BLOCK_SIZE 2
+#define CFB32_BLOCK_SIZE 4
+#define CFB64_BLOCK_SIZE 8
+
+/* AES flags */
+#define AES_FLAGS_MODE_MASK 0x01ff
+#define AES_FLAGS_ENCRYPT BIT(0)
+#define AES_FLAGS_CBC BIT(1)
+#define AES_FLAGS_CFB BIT(2)
+#define AES_FLAGS_CFB8 BIT(3)
+#define AES_FLAGS_CFB16 BIT(4)
+#define AES_FLAGS_CFB32 BIT(5)
+#define AES_FLAGS_CFB64 BIT(6)
+#define AES_FLAGS_OFB BIT(7)
+#define AES_FLAGS_CTR BIT(8)
+
+#define AES_FLAGS_INIT BIT(16)
+#define AES_FLAGS_DMA BIT(17)
+#define AES_FLAGS_BUSY BIT(18)
+
+#define AES_FLAGS_DUALBUFF BIT(24)
+
+#define ATMEL_AES_QUEUE_LENGTH 1
+#define ATMEL_AES_CACHE_SIZE 0
+
+#define ATMEL_AES_DMA_THRESHOLD 16
+
+
+struct atmel_aes_dev;
+
+struct atmel_aes_ctx {
+ struct atmel_aes_dev *dd;
+
+ int keylen;
+ u32 key[AES_KEYSIZE_256 / sizeof(u32)];
+};
+
+struct atmel_aes_reqctx {
+ unsigned long mode;
+};
+
+struct atmel_aes_dma {
+ struct dma_chan *chan;
+ struct dma_slave_config dma_conf;
+};
+
+struct atmel_aes_dev {
+ struct list_head list;
+ unsigned long phys_base;
+ void __iomem *io_base;
+
+ struct atmel_aes_ctx *ctx;
+ struct device *dev;
+ struct clk *iclk;
+ 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;
+ unsigned int nb_in_sg;
+
+ struct scatterlist *out_sg;
+ unsigned int nb_out_sg;
+
+ size_t bufcnt;
+
+ u8 buf_in[ATMEL_AES_DMA_THRESHOLD] __aligned(sizeof(u32));
+ int dma_in;
+ struct atmel_aes_dma dma_lch_in;
+
+ u8 buf_out[ATMEL_AES_DMA_THRESHOLD] __aligned(sizeof(u32));
+ int dma_out;
+ struct atmel_aes_dma dma_lch_out;
+
+ u32 hw_version;
+};
+
+struct atmel_aes_drv {
+ struct list_head dev_list;
+ spinlock_t lock;
+};
+
+static struct atmel_aes_drv atmel_aes = {
+ .dev_list = LIST_HEAD_INIT(atmel_aes.dev_list),
+ .lock = __SPIN_LOCK_UNLOCKED(atmel_aes.lock),
+};
+
+static int atmel_aes_sg_length(struct ablkcipher_request *req,
+ struct scatterlist *sg)
+{
+ unsigned int total = req->nbytes;
+ int sg_nb;
+ unsigned int len;
+ struct scatterlist *sg_list;
+
+ sg_nb = 0;
+ sg_list = sg;
+ total = req->nbytes;
+
+ while (total) {
+ len = min(sg_list->length, total);
+
+ sg_nb++;
+ total -= len;
+
+ sg_list = sg_next(sg_list);
+ if (!sg_list)
+ total = 0;
+ }
+
+ return sg_nb;
+}
+
+static inline u32 atmel_aes_read(struct atmel_aes_dev *dd, u32 offset)
+{
+ return readl_relaxed(dd->io_base + offset);
+}
+
+static inline void atmel_aes_write(struct atmel_aes_dev *dd,
+ u32 offset, u32 value)
+{
+ writel_relaxed(value, dd->io_base + offset);
+}
+
+static void atmel_aes_read_n(struct atmel_aes_dev *dd, u32 offset,
+ u32 *value, int count)
+{
+ for (; count--; value++, offset += 4)
+ *value = atmel_aes_read(dd, offset);
+}
+
+static void atmel_aes_write_n(struct atmel_aes_dev *dd, u32 offset,
+ u32 *value, int count)
+{
+ for (; count--; value++, offset += 4)
+ atmel_aes_write(dd, offset, *value);
+}
+
+static void atmel_aes_dualbuff_test(struct atmel_aes_dev *dd)
+{
+ atmel_aes_write(dd, AES_MR, AES_MR_DUALBUFF);
+
+ if (atmel_aes_read(dd, AES_MR) & AES_MR_DUALBUFF)
+ dd->flags |= AES_FLAGS_DUALBUFF;
+}
+
+static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_ctx *ctx)
+{
+ struct atmel_aes_dev *aes_dd = NULL;
+ struct atmel_aes_dev *tmp;
+
+ spin_lock_bh(&atmel_aes.lock);
+ if (!ctx->dd) {
+ list_for_each_entry(tmp, &atmel_aes.dev_list, list) {
+ aes_dd = tmp;
+ break;
+ }
+ ctx->dd = aes_dd;
+ } else {
+ aes_dd = ctx->dd;
+ }
+
+ spin_unlock_bh(&atmel_aes.lock);
+
+ return aes_dd;
+}
+
+static int atmel_aes_hw_init(struct atmel_aes_dev *dd)
+{
+ clk_prepare_enable(dd->iclk);
+
+ if (!(dd->flags & AES_FLAGS_INIT)) {
+ atmel_aes_write(dd, AES_CR, AES_CR_SWRST);
+ atmel_aes_dualbuff_test(dd);
+ dd->flags |= AES_FLAGS_INIT;
+ dd->err = 0;
+ }
+
+ return 0;
+}
+
+static void atmel_aes_hw_version_init(struct atmel_aes_dev *dd)
+{
+ atmel_aes_hw_init(dd);
+
+ dd->hw_version = atmel_aes_read(dd, AES_HW_VERSION);
+
+ clk_disable_unprepare(dd->iclk);
+}
+
+static void atmel_aes_finish_req(struct atmel_aes_dev *dd, int err)
+{
+ struct ablkcipher_request *req = dd->req;
+
+ clk_disable_unprepare(dd->iclk);
+ dd->flags &= ~AES_FLAGS_BUSY;
+
+ req->base.complete(&req->base, err);
+}
+
+static void atmel_aes_dma_callback(void *data)
+{
+ struct atmel_aes_dev *dd = data;
+
+ /* dma_lch_out - completed */
+ tasklet_schedule(&dd->done_task);
+}
+
+static int atmel_aes_crypt_dma(struct atmel_aes_dev *dd)
+{
+ struct dma_async_tx_descriptor *in_desc, *out_desc;
+ int nb_dma_sg_in, nb_dma_sg_out;
+
+ dd->nb_in_sg = atmel_aes_sg_length(dd->req, dd->in_sg);
+ if (!dd->nb_in_sg)
+ goto exit_err;
+
+ nb_dma_sg_in = dma_map_sg(dd->dev, dd->in_sg, dd->nb_in_sg,
+ DMA_TO_DEVICE);
+ if (!nb_dma_sg_in)
+ goto exit_err;
+
+ in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, dd->in_sg,
+ nb_dma_sg_in, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+ if (!in_desc)
+ goto unmap_in;
+
+ /* callback not needed */
+
+ dd->nb_out_sg = atmel_aes_sg_length(dd->req, dd->out_sg);
+ if (!dd->nb_out_sg)
+ goto unmap_in;
+
+ nb_dma_sg_out = dma_map_sg(dd->dev, dd->out_sg, dd->nb_out_sg,
+ DMA_FROM_DEVICE);
+ if (!nb_dma_sg_out)
+ goto unmap_out;
+
+ out_desc = dmaengine_prep_slave_sg(dd->dma_lch_out.chan, dd->out_sg,
+ nb_dma_sg_out, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+ if (!out_desc)
+ goto unmap_out;
+
+ out_desc->callback = atmel_aes_dma_callback;
+ out_desc->callback_param = dd;
+
+ dd->total -= dd->req->nbytes;
+
+ dmaengine_submit(out_desc);
+ dma_async_issue_pending(dd->dma_lch_out.chan);
+
+ dmaengine_submit(in_desc);
+ dma_async_issue_pending(dd->dma_lch_in.chan);
+
+ return 0;
+
+unmap_out:
+ dma_unmap_sg(dd->dev, dd->out_sg, dd->nb_out_sg,
+ DMA_FROM_DEVICE);
+unmap_in:
+ dma_unmap_sg(dd->dev, dd->in_sg, dd->nb_in_sg,
+ DMA_TO_DEVICE);
+exit_err:
+ return -EINVAL;
+}
+
+static int atmel_aes_crypt_cpu_start(struct atmel_aes_dev *dd)
+{
+ dd->flags &= ~AES_FLAGS_DMA;
+
+ /* use cache buffers */
+ dd->nb_in_sg = atmel_aes_sg_length(dd->req, dd->in_sg);
+ if (!dd->nb_in_sg)
+ return -EINVAL;
+
+ dd->nb_out_sg = atmel_aes_sg_length(dd->req, dd->out_sg);
+ if (!dd->nb_in_sg)
+ return -EINVAL;
+
+ dd->bufcnt = sg_copy_to_buffer(dd->in_sg, dd->nb_in_sg,
+ dd->buf_in, dd->total);
+
+ if (!dd->bufcnt)
+ return -EINVAL;
+
+ dd->total -= dd->bufcnt;
+
+ atmel_aes_write(dd, AES_IER, AES_INT_DATARDY);
+ atmel_aes_write_n(dd, AES_IDATAR(0), (u32 *) dd->buf_in,
+ dd->bufcnt >> 2);
+
+ return 0;
+}
+
+static int atmel_aes_crypt_dma_start(struct atmel_aes_dev *dd)
+{
+ int err;
+
+ if (dd->flags & AES_FLAGS_CFB8) {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_1_BYTE;
+ } else if (dd->flags & AES_FLAGS_CFB16) {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_2_BYTES;
+ } else {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ }
+
+ dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf);
+ dmaengine_slave_config(dd->dma_lch_out.chan, &dd->dma_lch_out.dma_conf);
+
+ dd->flags |= AES_FLAGS_DMA;
+ err = atmel_aes_crypt_dma(dd);
+
+ return err;
+}
+
+static int atmel_aes_write_ctrl(struct atmel_aes_dev *dd)
+{
+ int err;
+ u32 valcr = 0, valmr = 0;
+
+ err = atmel_aes_hw_init(dd);
+
+ if (err)
+ return err;
+
+ /* MR register must be set before IV registers */
+ if (dd->ctx->keylen == AES_KEYSIZE_128)
+ valmr |= AES_MR_KEYSIZE_128;
+ else if (dd->ctx->keylen == AES_KEYSIZE_192)
+ valmr |= AES_MR_KEYSIZE_192;
+ else
+ valmr |= AES_MR_KEYSIZE_256;
+
+ if (dd->flags & AES_FLAGS_CBC) {
+ valmr |= AES_MR_OPMOD_CBC;
+ } else if (dd->flags & AES_FLAGS_CFB) {
+ valmr |= AES_MR_OPMOD_CFB;
+ if (dd->flags & AES_FLAGS_CFB8)
+ valmr |= AES_MR_CFBS_8b;
+ else if (dd->flags & AES_FLAGS_CFB16)
+ valmr |= AES_MR_CFBS_16b;
+ else if (dd->flags & AES_FLAGS_CFB32)
+ valmr |= AES_MR_CFBS_32b;
+ else if (dd->flags & AES_FLAGS_CFB64)
+ valmr |= AES_MR_CFBS_64b;
+ } else if (dd->flags & AES_FLAGS_OFB) {