[Armadeus-commitlog] armadeus branch, master, updated. armadeus-4.0-2550-g7516ca9
Brought to you by:
sszy
|
From: Julien B a. A. <ar...@us...> - 2011-10-20 14:53:04
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "armadeus".
The branch, master has been updated
via 7516ca9db192bc835e6fffb8d8b8ce3b927aeb51 (commit)
from 0cd171766a17ed7a8e11083223414d57225084d6 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 7516ca9db192bc835e6fffb8d8b8ce3b927aeb51
Author: Julien Boibessot <jul...@ar...>
Date: Thu Oct 20 16:52:06 2011 +0200
[LINUX] Add IIM and DCP drivers
-----------------------------------------------------------------------
Summary of changes:
...escale-0034-ENGR00126692-3-add_iim_driver.patch | 523 ++
...26692-3-add_mx23_mx25_mx28_security_stuff.patch | 5592 ++++++++++++++++++++
2 files changed, 6115 insertions(+), 0 deletions(-)
create mode 100644 patches/linux/2.6.38/442-freescale-0034-ENGR00126692-3-add_iim_driver.patch
create mode 100644 patches/linux/2.6.38/442-freescale-0034-ENGR00126692-3-add_mx23_mx25_mx28_security_stuff.patch
diff --git a/patches/linux/2.6.38/442-freescale-0034-ENGR00126692-3-add_iim_driver.patch b/patches/linux/2.6.38/442-freescale-0034-ENGR00126692-3-add_iim_driver.patch
new file mode 100644
index 0000000..0d66022
--- /dev/null
+++ b/patches/linux/2.6.38/442-freescale-0034-ENGR00126692-3-add_iim_driver.patch
@@ -0,0 +1,523 @@
+From 3eb9039c5392abaabd96d08c0006ea4066346a60 Mon Sep 17 00:00:00 2001
+From: Dinh Nguyen <Din...@fr...>
+Date: Tue, 17 Aug 2010 16:46:40 -0500
+Subject: [PATCH] ENGR00126692-3: Upgrade kernel to 2.6.35
+
+This patch contains changes to /drivers files
+Contains all checkpatch and copyright fixes.
+
+Acked-by: Rob Herring <r.h...@fr...>
+Signed-off-by: Dinh Nguyen <Din...@fr...>
+---
+ Changes by Armadeus systems:
+ - extract IIM stuff from original patch
+ - correctly register IIM resources
+
+ drivers/char/Kconfig | 23 +
+ drivers/char/Makefile | 5 +
+ drivers/char/mxc_iim.c | 161 +
+ drivers/char/mxs_viim.c | 175 +
+
+Index: linux-2.6.38.8/drivers/char/Kconfig
+===================================================================
+--- linux-2.6.38.8.orig/drivers/char/Kconfig 2011-06-03 03:35:11.000000000 +0200
++++ linux-2.6.38.8/drivers/char/Kconfig 2011-08-26 19:03:11.000000000 +0200
+@@ -426,6 +426,18 @@
+ If you have an SGI Altix with an attached SABrick
+ say Y or M here, otherwise say N.
+
++config MXC_IIM
++ tristate "MXC IIM device driver"
++ depends on ARCH_MXC
++ help
++ Support for access to MXC IIM device, most people should say N here.
++
++config MXS_VIIM
++ tristate "MXS Virtual IIM device driver"
++ depends on (ARCH_STMP3XXX || ARCH_MXS || ARCH_MX5)
++ help
++ Support for access to MXS Virtual IIM device, most people should say N here.
++
+ source "drivers/tty/serial/Kconfig"
+
+ config UNIX98_PTYS
+Index: linux-2.6.38.8/drivers/char/Makefile
+===================================================================
+--- linux-2.6.38.8.orig/drivers/char/Makefile 2011-06-03 03:35:11.000000000 +0200
++++ linux-2.6.38.8/drivers/char/Makefile 2011-08-26 19:03:11.000000000 +0200
+@@ -73,6 +73,9 @@
+ obj-$(CONFIG_GPIO_TB0219) += tb0219.o
+ obj-$(CONFIG_TELCLOCK) += tlclk.o
+
++obj-$(CONFIG_MXC_IIM) += mxc_iim.o
++obj-$(CONFIG_MXS_VIIM) += mxs_viim.o
++
+ obj-$(CONFIG_MWAVE) += mwave/
+ obj-$(CONFIG_AGP) += agp/
+ obj-$(CONFIG_PCMCIA) += pcmcia/
+Index: linux-2.6.38.8/drivers/char/mxc_iim.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.38.8/drivers/char/mxc_iim.c 2011-08-26 19:03:11.000000000 +0200
+@@ -0,0 +1,161 @@
++/*
++ * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
++ */
++
++/*
++ * The code contained herein is licensed under the GNU General Public
++ * License. You may obtain a copy of the GNU General Public License
++ * Version 2 or later at the following locations:
++ *
++ * http://www.opensource.org/licenses/gpl-license.html
++ * http://www.gnu.org/copyleft/gpl.html
++ */
++
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/mm.h>
++#include <linux/clk.h>
++#include <linux/miscdevice.h>
++
++static unsigned long iim_reg_base, iim_reg_end, iim_reg_size;
++static struct clk *iim_clk;
++static struct device *iim_dev;
++
++/*!
++ * MXC IIM interface - memory map function
++ * This function maps 4KB IIM registers from IIM base address.
++ *
++ * @param file struct file *
++ * @param vma structure vm_area_struct *
++ *
++ * @return Return 0 on success or negative error code on error
++ */
++static int mxc_iim_mmap(struct file *file, struct vm_area_struct *vma)
++{
++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++ /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
++ if (remap_pfn_range(vma,
++ vma->vm_start,
++ iim_reg_base >> PAGE_SHIFT,
++ iim_reg_size,
++ vma->vm_page_prot))
++ return -EAGAIN;
++
++ return 0;
++}
++
++/*!
++ * MXC IIM interface - open function
++ *
++ * @param inode struct inode *
++ * @param filp struct file *
++ *
++ * @return Return 0 on success or negative error code on error
++ */
++static int mxc_iim_open(struct inode *inode, struct file *filp)
++{
++ iim_clk = clk_get(NULL, "iim_clk");
++ if (IS_ERR(iim_clk)) {
++ dev_err(iim_dev, "No IIM clock defined\n");
++ return -ENODEV;
++ }
++ clk_enable(iim_clk);
++
++ return 0;
++}
++
++/*!
++ * MXC IIM interface - release function
++ *
++ * @param inode struct inode *
++ * @param filp struct file *
++ *
++ * @return Return 0 on success or negative error code on error
++ */
++static int mxc_iim_release(struct inode *inode, struct file *filp)
++{
++ clk_disable(iim_clk);
++ clk_put(iim_clk);
++ return 0;
++}
++
++static const struct file_operations mxc_iim_fops = {
++ .mmap = mxc_iim_mmap,
++ .open = mxc_iim_open,
++ .release = mxc_iim_release,
++};
++
++static struct miscdevice mxc_iim_miscdev = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = "mxc_iim",
++ .fops = &mxc_iim_fops,
++};
++
++/*!
++ * This function is called by the driver framework to get iim base/end address
++ * and register iim misc device.
++ *
++ * @param dev The device structure for IIM passed in by the driver
++ * framework.
++ *
++ * @return Returns 0 on success or negative error code on error
++ */
++static int mxc_iim_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++ int ret;
++
++ iim_dev = &pdev->dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (IS_ERR(res)) {
++ dev_err(iim_dev, "Unable to get IIM resource\n");
++ return -ENODEV;
++ }
++
++ iim_reg_base = res->start;
++ iim_reg_end = res->end;
++ iim_reg_size = iim_reg_end - iim_reg_base + 1;
++
++ ret = misc_register(&mxc_iim_miscdev);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int mxc_iim_remove(struct platform_device *pdev)
++{
++ misc_deregister(&mxc_iim_miscdev);
++ return 0;
++}
++
++static struct platform_driver mxc_iim_driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "mxc_iim",
++ },
++ .probe = mxc_iim_probe,
++ .remove = mxc_iim_remove,
++};
++
++static int __init mxc_iim_dev_init(void)
++{
++ return platform_driver_register(&mxc_iim_driver);
++}
++
++static void __exit mxc_iim_dev_cleanup(void)
++{
++ platform_driver_unregister(&mxc_iim_driver);
++}
++
++module_init(mxc_iim_dev_init);
++module_exit(mxc_iim_dev_cleanup);
++
++MODULE_AUTHOR("Freescale Semiconductor, Inc.");
++MODULE_DESCRIPTION("MXC IIM driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
+Index: linux-2.6.38.8/drivers/char/mxs_viim.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.38.8/drivers/char/mxs_viim.c 2011-08-26 19:03:11.000000000 +0200
+@@ -0,0 +1,175 @@
++/*
++ * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
++ */
++
++/*
++ * The code contained herein is licensed under the GNU General Public
++ * License. You may obtain a copy of the GNU General Public License
++ * Version 2 or later at the following locations:
++ *
++ * http://www.opensource.org/licenses/gpl-license.html
++ * http://www.gnu.org/copyleft/gpl.html
++ */
++
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++
++static unsigned long iim_reg_base0, iim_reg_end0, iim_reg_size0;
++static unsigned long iim_reg_base1, iim_reg_end1, iim_reg_size1;
++static struct device *iim_dev;
++
++/*!
++ * MXS Virtual IIM interface - memory map function
++ * This function maps one page size VIIM registers from VIIM base address0
++ * if the size of the required virtual memory space is less than or equal to
++ * one page size, otherwise this function will also map one page size VIIM
++ * registers from VIIM base address1.
++ *
++ * @param file struct file *
++ * @param vma structure vm_area_struct *
++ *
++ * @return Return 0 on success or negative error code on error
++ */
++static int mxs_viim_mmap(struct file *file, struct vm_area_struct *vma)
++{
++ size_t size = vma->vm_end - vma->vm_start;
++
++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++ /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
++ if (remap_pfn_range(vma,
++ vma->vm_start,
++ iim_reg_base0 >> PAGE_SHIFT,
++ iim_reg_size0,
++ vma->vm_page_prot))
++ return -EAGAIN;
++
++ if (size > iim_reg_size0) {
++ if (remap_pfn_range(vma,
++ vma->vm_start + iim_reg_size0,
++ iim_reg_base1 >> PAGE_SHIFT,
++ iim_reg_size1,
++ vma->vm_page_prot))
++ return -EAGAIN;
++ }
++
++ return 0;
++}
++
++/*!
++ * MXS Virtual IIM interface - open function
++ *
++ * @param inode struct inode *
++ * @param filp struct file *
++ *
++ * @return Return 0 on success or negative error code on error
++ */
++static int mxs_viim_open(struct inode *inode, struct file *filp)
++{
++ return 0;
++}
++
++/*!
++ * MXS Virtual IIM interface - release function
++ *
++ * @param inode struct inode *
++ * @param filp struct file *
++ *
++ * @return Return 0 on success or negative error code on error
++ */
++static int mxs_viim_release(struct inode *inode, struct file *filp)
++{
++ return 0;
++}
++
++static const struct file_operations mxs_viim_fops = {
++ .mmap = mxs_viim_mmap,
++ .open = mxs_viim_open,
++ .release = mxs_viim_release,
++};
++
++static struct miscdevice mxs_viim_miscdev = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = "mxs_viim",
++ .fops = &mxs_viim_fops,
++};
++
++/*!
++ * This function is called by the driver framework to get virtual iim base/end
++ * address and register iim misc device.
++ *
++ * @param dev The device structure for Virtual IIM passed in by the
++ * driver framework.
++ *
++ * @return Returns 0 on success or negative error code on error
++ */
++static int mxs_viim_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++ int ret;
++
++ iim_dev = &pdev->dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (IS_ERR(res)) {
++ dev_err(iim_dev, "Unable to get Virtual IIM resource 0\n");
++ return -ENODEV;
++ }
++
++ iim_reg_base0 = res->start;
++ iim_reg_end0 = res->end;
++ iim_reg_size0 = iim_reg_end0 - iim_reg_base0 + 1;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++ if (IS_ERR(res)) {
++ dev_err(iim_dev, "Unable to get Virtual IIM resource 1\n");
++ return -ENODEV;
++ }
++
++ iim_reg_base1 = res->start;
++ iim_reg_end1 = res->end;
++ iim_reg_size1 = iim_reg_end1 - iim_reg_base1 + 1;
++
++ ret = misc_register(&mxs_viim_miscdev);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int mxs_viim_remove(struct platform_device *pdev)
++{
++ misc_deregister(&mxs_viim_miscdev);
++ return 0;
++}
++
++static struct platform_driver mxs_viim_driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "mxs_viim",
++ },
++ .probe = mxs_viim_probe,
++ .remove = mxs_viim_remove,
++};
++
++static int __init mxs_viim_dev_init(void)
++{
++ return platform_driver_register(&mxs_viim_driver);
++}
++
++static void __exit mxs_viim_dev_cleanup(void)
++{
++ platform_driver_unregister(&mxs_viim_driver);
++}
++
++module_init(mxs_viim_dev_init);
++module_exit(mxs_viim_dev_cleanup);
++
++MODULE_AUTHOR("Freescale Semiconductor, Inc.");
++MODULE_DESCRIPTION("MXS Virtual IIM driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
+Index: linux-2.6.38.8/arch/arm/plat-mxc/devices/Makefile
+===================================================================
+--- linux-2.6.38.8.orig/arch/arm/plat-mxc/devices/Makefile 2011-08-27 14:39:36.000000000 +0200
++++ linux-2.6.38.8/arch/arm/plat-mxc/devices/Makefile 2011-10-20 16:04:33.000000000 +0200
+@@ -23,3 +23,4 @@
+ obj-$(CONFIG_IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX) += platform-sdhci-esdhc-imx.o
+ obj-$(CONFIG_IMX_HAVE_PLATFORM_SPI_IMX) += platform-spi_imx.o
+ obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_IPUV3) += platform-imx_ipuv3.o
++obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_IIM) += platform-mxc-iim.o
+Index: linux-2.6.38.8/arch/arm/plat-mxc/devices/Kconfig
+===================================================================
+--- linux-2.6.38.8.orig/arch/arm/plat-mxc/devices/Kconfig 2011-08-27 14:44:08.000000000 +0200
++++ linux-2.6.38.8/arch/arm/plat-mxc/devices/Kconfig 2011-10-20 16:03:48.000000000 +0200
+@@ -75,3 +75,6 @@
+ config IMX_HAVE_PLATFORM_IMX_IPUV3
+ bool
+
++config IMX_HAVE_PLATFORM_MXC_IIM
++ bool
++
+Index: linux-2.6.38.8/arch/arm/plat-mxc/devices/platform-mxc-iim.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.38.8/arch/arm/plat-mxc/devices/platform-mxc-iim.c 2011-08-30 11:20:50.000000000 +0200
+@@ -0,0 +1,66 @@
++/*
++ * Copyright (C) 2011 ARMadeus Systems
++ * Julien Boibessot <jul...@ar...>
++ *
++ * Based on code which is:
++ * Copyright (C) 2010 Pengutronix
++ * Uwe Kleine-Koenig <u.k...@pe...>
++ *
++ * This program is free software; you can redistribute it and/or modify it under
++ * the terms of the GNU General Public License version 2 as published by the
++ * Free Software Foundation.
++ */
++#include <asm/sizes.h>
++#include <mach/hardware.h>
++#include <mach/devices-common.h>
++
++#define imx_iim_data_entry(soc, _id, _hwid, _size) \
++ { \
++ .id = _id, \
++ .iobase = soc ## _IIM ## _BASE_ADDR, \
++ .iosize = _size, \
++ }
++
++#ifdef CONFIG_SOC_IMX21
++const struct imx_iim_data imx21_iim_data __initconst =
++ imx_iim_data_entry(MX21, 0, , SZ_4K);
++#endif /* ifdef CONFIG_SOC_IMX21 */
++
++#ifdef CONFIG_SOC_IMX25
++const struct imx_iim_data imx25_iim_data __initconst =
++ imx_iim_data_entry(MX25, 0, , SZ_16K);
++#endif /* ifdef CONFIG_SOC_IMX25 */
++
++#ifdef CONFIG_SOC_IMX27
++const struct imx_iim_data imx27_iim_data __initconst =
++ imx_iim_data_entry(MX27, 0, , SZ_4K);
++#endif /* ifdef CONFIG_SOC_IMX27 */
++
++#ifdef CONFIG_SOC_IMX31
++const struct imx_iim_data imx31_iim_data __initconst =
++ imx_iim_data_entry(MX31, 0, , SZ_16K);
++#endif /* ifdef CONFIG_SOC_IMX31 */
++
++#ifdef CONFIG_SOC_IMX35
++const struct imx_iim_data imx35_iim_data __initconst =
++ imx_iim_data_entry(MX35, 0, , SZ_16K);
++#endif /* ifdef CONFIG_SOC_IMX35 */
++
++#ifdef CONFIG_SOC_IMX51
++const struct imx_iim_data imx51_iim_data[] __initconst = {
++ imx_iim_data_entry(MX51, 0, , SZ_4K)
++};
++#endif /* ifdef CONFIG_SOC_IMX51 */
++
++struct platform_device *__init imx_add_iim(const struct imx_iim_data *data)
++{
++ struct resource res[] = {
++ {
++ .start = data->iobase,
++ .end = data->iobase + data->iosize - 1,
++ .flags = IORESOURCE_MEM,
++ },
++ };
++ return imx_add_platform_device("mxc-iim", data->id,
++ res, ARRAY_SIZE(res), NULL, 0);
++}
+Index: linux-2.6.38.8/arch/arm/plat-mxc/include/mach/devices-common.h
+===================================================================
+--- linux-2.6.38.8.orig/arch/arm/plat-mxc/include/mach/devices-common.h 2011-08-30 11:45:23.000000000 +0200
++++ linux-2.6.38.8/arch/arm/plat-mxc/include/mach/devices-common.h 2011-08-30 11:47:10.000000000 +0200
+@@ -274,3 +274,12 @@
+ struct platform_device *__init imx_add_ipuv3(
+ const struct imx_ipuv3_data *data,
+ const struct imx_ipuv3_platform_data *pdata);
++
++struct imx_iim_data {
++ int id;
++ resource_size_t iobase;
++ resource_size_t iosize;
++};
++struct platform_device *__init imx_add_iim(
++ const struct imx_iim_data *data);
++
+Index: linux-2.6.38.8/arch/arm/mach-mx5/devices-imx51.h
+===================================================================
+--- linux-2.6.38.8.orig/arch/arm/mach-mx5/devices-imx51.h 2011-08-30 11:55:51.000000000 +0200
++++ linux-2.6.38.8/arch/arm/mach-mx5/devices-imx51.h 2011-08-30 11:58:09.000000000 +0200
+@@ -59,3 +59,7 @@
+ extern const struct imx_ipuv3_data imx51_ipuv3_data __initconst;
+ #define imx51_add_ipuv3(pdata) \
+ imx_add_ipuv3(&imx51_ipuv3_data, pdata)
++
++extern const struct imx_iim_data imx51_iim_data __initconst;
++#define imx51_add_iim(pdata) \
++ imx_add_iim(&imx51_iim_data)
diff --git a/patches/linux/2.6.38/442-freescale-0034-ENGR00126692-3-add_mx23_mx25_mx28_security_stuff.patch b/patches/linux/2.6.38/442-freescale-0034-ENGR00126692-3-add_mx23_mx25_mx28_security_stuff.patch
new file mode 100644
index 0000000..0aa3551
--- /dev/null
+++ b/patches/linux/2.6.38/442-freescale-0034-ENGR00126692-3-add_mx23_mx25_mx28_security_stuff.patch
@@ -0,0 +1,5592 @@
+From 3eb9039c5392abaabd96d08c0006ea4066346a60 Mon Sep 17 00:00:00 2001
+From: Dinh Nguyen <Din...@fr...>
+Date: Tue, 17 Aug 2010 16:46:40 -0500
+Subject: [PATCH] ENGR00126692-3: Upgrade kernel to 2.6.35
+
+This patch contains changes to /drivers files
+Contains all checkpatch and copyright fixes.
+
+Acked-by: Rob Herring <r.h...@fr...>
+Signed-off-by: Dinh Nguyen <Din...@fr...>
+---
+ Changes from Armadeus systems:
+ - extracted DCP stuff from original patch
+
+diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
+--- a/drivers/crypto/Kconfig
++++ b/drivers/crypto/Kconfig
+@@ -243,4 +243,16 @@ config CRYPTO_DEV_OMAP_SHAM
+ OMAP processors have SHA1/MD5 hw accelerator. Select this if you
+ want to use the OMAP module for SHA1/MD5 algorithms.
+
++config CRYPTO_DEV_DCP
++ tristate "Support for the DCP engine"
++ depends on ARCH_MX28 || ARCH_MX23
++ select CRYPTO_ALGAPI
++ select CRYPTO_BLKCIPHER
++ help
++ Say 'Y' here to use the DCP AES and SHA
++ engine for the CryptoAPI algorithms.
++
++ To compile this driver as a module, choose M here: the module
++ will be called geode-aes.
++
+ endif # CRYPTO_HW
+diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
+index 6dbbe00..c375d19 100644
+--- a/drivers/crypto/Makefile
++++ b/drivers/crypto/Makefile
+@@ -9,4 +9,5 @@ obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
+ obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
+ obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
+ obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o
++obj-$(CONFIG_CRYPTO_DEV_DCP) += dcp.o
+
+diff --git a/drivers/crypto/dcp.c b/drivers/crypto/dcp.c
+new file mode 100644
+index 0000000..9589a19
+--- /dev/null
++++ b/drivers/crypto/dcp.c
+@@ -0,0 +1,1697 @@
++/*
++ * Copyright (C) 2008-2010 Freescale Semiconductor, Inc.
++ */
++
++/*
++ * The code contained herein is licensed under the GNU General Public
++ * License. You may obtain a copy of the GNU General Public License
++ * Version 2 or later at the following locations:
++ *
++ * http://www.opensource.org/licenses/gpl-license.html
++ * http://www.gnu.org/copyleft/gpl.html
++ */
++/*
++ * Based on geode-aes.c
++ * Copyright (C) 2004-2006, Advanced Micro Devices, Inc.
++ */
++
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/sysdev.h>
++#include <linux/bitops.h>
++#include <linux/crypto.h>
++#include <linux/spinlock.h>
++#include <linux/miscdevice.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/sysfs.h>
++#include <linux/fs.h>
++#include <crypto/algapi.h>
++#include <crypto/aes.h>
++#include <crypto/sha.h>
++#include <crypto/hash.h>
++#include <crypto/internal/hash.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/uaccess.h>
++
++#include <linux/io.h>
++#include <linux/delay.h>
++
++#include <asm/cacheflush.h>
++#include <mach/hardware.h>
++#include "dcp.h"
++#include "dcp_bootstream_ioctl.h"
++
++/* Following data only used by DCP bootstream interface */
++struct dcpboot_dma_area {
++ struct dcp_hw_packet hw_packet;
++ uint16_t block[16];
++};
++
++struct dcp {
++ struct device *dev;
++ spinlock_t lock;
++ struct mutex op_mutex[DCP_NUM_CHANNELS];
++ struct completion op_wait[DCP_NUM_CHANNELS];
++ int wait[DCP_NUM_CHANNELS];
++ int dcp_vmi_irq;
++ int dcp_irq;
++ u32 dcp_regs_base;
++
++ /* Following buffers used in hashing to meet 64-byte len alignment */
++ char *buf1;
++ char *buf2;
++ dma_addr_t buf1_phys;
++ dma_addr_t buf2_phys;
++ struct dcp_hash_coherent_block *buf1_desc;
++ struct dcp_hash_coherent_block *buf2_desc;
++ struct dcp_hash_coherent_block *user_buf_desc;
++
++ /* Following data only used by DCP bootstream interface */
++ struct dcpboot_dma_area *dcpboot_dma_area;
++ dma_addr_t dcpboot_dma_area_phys;
++};
++
++/* cipher flags */
++#define DCP_ENC 0x0001
++#define DCP_DEC 0x0002
++#define DCP_ECB 0x0004
++#define DCP_CBC 0x0008
++#define DCP_CBC_INIT 0x0010
++#define DCP_OTPKEY 0x0020
++
++/* hash flags */
++#define DCP_INIT 0x0001
++#define DCP_UPDATE 0x0002
++#define DCP_FINAL 0x0004
++
++#define DCP_AES 0x1000
++#define DCP_SHA1 0x2000
++#define DCP_CRC32 0x3000
++#define DCP_COPY 0x4000
++#define DCP_FILL 0x5000
++#define DCP_MODE_MASK 0xf000
++
++struct dcp_op {
++
++ unsigned int flags;
++
++ void *src;
++ dma_addr_t src_phys;
++
++ void *dst;
++ dma_addr_t dst_phys;
++
++ int len;
++
++ /* the key contains the IV for block modes */
++ union {
++ struct {
++ u8 key[2 * AES_KEYSIZE_128]
++ __attribute__ ((__aligned__(32)));
++ dma_addr_t key_phys;
++ int keylen;
++ } cipher;
++ struct {
++ u8 digest[SHA256_DIGEST_SIZE]
++ __attribute__ ((__aligned__(32)));
++ dma_addr_t digest_phys;
++ int digestlen;
++ int init;
++ } hash;
++ };
++
++ union {
++ struct crypto_blkcipher *blk;
++ struct crypto_cipher *cip;
++ struct crypto_hash *hash;
++ } fallback;
++
++ struct dcp_hw_packet pkt
++ __attribute__ ((__aligned__(32)));
++};
++
++struct dcp_hash_coherent_block {
++ struct dcp_hw_packet pkt[1]
++ __attribute__ ((__aligned__(32)));
++ u8 digest[SHA256_DIGEST_SIZE]
++ __attribute__ ((__aligned__(32)));
++ unsigned int len;
++ dma_addr_t src_phys;
++ void *src;
++ void *dst;
++ dma_addr_t my_phys;
++ u32 hash_sel;
++ struct dcp_hash_coherent_block *next;
++};
++
++struct dcp_hash_op {
++
++ unsigned int flags;
++
++ /* the key contains the IV for block modes */
++ union {
++ struct {
++ u8 key[2 * AES_KEYSIZE_128]
++ __attribute__ ((__aligned__(32)));
++ dma_addr_t key_phys;
++ int keylen;
++ } cipher;
++ struct {
++ u8 digest[SHA256_DIGEST_SIZE]
++ __attribute__ ((__aligned__(32)));
++ dma_addr_t digest_phys;
++ int digestlen;
++ int init;
++ } hash;
++ };
++
++ u32 length;
++ struct dcp_hash_coherent_block *head_desc;
++ struct dcp_hash_coherent_block *tail_desc;
++};
++
++/* only one */
++static struct dcp *global_sdcp;
++
++static void dcp_perform_op(struct dcp_op *op)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct mutex *mutex;
++ struct dcp_hw_packet *pkt;
++ int chan;
++ u32 pkt1, pkt2;
++ unsigned long timeout;
++ dma_addr_t pkt_phys;
++ u32 stat;
++
++ pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE | BM_DCP_PACKET1_INTERRUPT;
++
++ switch (op->flags & DCP_MODE_MASK) {
++
++ case DCP_AES:
++
++ chan = CIPHER_CHAN;
++
++ /* key is at the payload */
++ pkt1 |= BM_DCP_PACKET1_ENABLE_CIPHER;
++ if ((op->flags & DCP_OTPKEY) == 0)
++ pkt1 |= BM_DCP_PACKET1_PAYLOAD_KEY;
++ if (op->flags & DCP_ENC)
++ pkt1 |= BM_DCP_PACKET1_CIPHER_ENCRYPT;
++ if (op->flags & DCP_CBC_INIT)
++ pkt1 |= BM_DCP_PACKET1_CIPHER_INIT;
++
++ pkt2 = BF(0, DCP_PACKET2_CIPHER_CFG) |
++ BF(0, DCP_PACKET2_KEY_SELECT) |
++ BF(BV_DCP_PACKET2_CIPHER_SELECT__AES128,
++ DCP_PACKET2_CIPHER_SELECT);
++
++ if (op->flags & DCP_ECB)
++ pkt2 |= BF(BV_DCP_PACKET2_CIPHER_MODE__ECB,
++ DCP_PACKET2_CIPHER_MODE);
++ else if (op->flags & DCP_CBC)
++ pkt2 |= BF(BV_DCP_PACKET2_CIPHER_MODE__CBC,
++ DCP_PACKET2_CIPHER_MODE);
++
++ break;
++
++ case DCP_SHA1:
++
++ chan = HASH_CHAN;
++
++ pkt1 |= BM_DCP_PACKET1_ENABLE_HASH;
++ if (op->flags & DCP_INIT)
++ pkt1 |= BM_DCP_PACKET1_HASH_INIT;
++ if (op->flags & DCP_FINAL) {
++ pkt1 |= BM_DCP_PACKET1_HASH_TERM;
++ BUG_ON(op->hash.digest == NULL);
++ }
++
++ pkt2 = BF(BV_DCP_PACKET2_HASH_SELECT__SHA1,
++ DCP_PACKET2_HASH_SELECT);
++ break;
++
++ default:
++ dev_err(sdcp->dev, "Unsupported mode\n");
++ return;
++ }
++
++ mutex = &sdcp->op_mutex[chan];
++ pkt = &op->pkt;
++
++ pkt->pNext = 0;
++ pkt->pkt1 = pkt1;
++ pkt->pkt2 = pkt2;
++ pkt->pSrc = (u32)op->src_phys;
++ pkt->pDst = (u32)op->dst_phys;
++ pkt->size = op->len;
++ pkt->pPayload = chan == CIPHER_CHAN ?
++ (u32)op->cipher.key_phys : (u32)op->hash.digest_phys;
++ pkt->stat = 0;
++
++ pkt_phys = dma_map_single(sdcp->dev, pkt, sizeof(*pkt),
++ DMA_BIDIRECTIONAL);
++ if (dma_mapping_error(sdcp->dev, pkt_phys)) {
++ dev_err(sdcp->dev, "Unable to map packet descriptor\n");
++ return;
++ }
++
++ /* submit the work */
++ mutex_lock(mutex);
++
++ __raw_writel(-1, sdcp->dcp_regs_base + HW_DCP_CHnSTAT_CLR(chan));
++
++ /* Load the work packet pointer and bump the channel semaphore */
++ __raw_writel((u32)pkt_phys, sdcp->dcp_regs_base +
++ HW_DCP_CHnCMDPTR(chan));
++
++ /* XXX wake from interrupt instead of looping */
++ timeout = jiffies + msecs_to_jiffies(1000);
++
++ sdcp->wait[chan] = 0;
++ __raw_writel(BF(1, DCP_CHnSEMA_INCREMENT), sdcp->dcp_regs_base
++ + HW_DCP_CHnSEMA(chan));
++ while (time_before(jiffies, timeout) && sdcp->wait[chan] == 0)
++ cpu_relax();
++
++ if (!time_before(jiffies, timeout)) {
++ dev_err(sdcp->dev, "Timeout while waiting STAT 0x%08x\n",
++ __raw_readl(sdcp->dcp_regs_base + HW_DCP_STAT));
++ goto out;
++ }
++
++ stat = __raw_readl(sdcp->dcp_regs_base + HW_DCP_CHnSTAT(chan));
++ if ((stat & 0xff) != 0)
++ dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
++ __raw_readl(sdcp->dcp_regs_base +
++ HW_DCP_CHnSTAT(chan)) & 0xff);
++out:
++ mutex_unlock(mutex);
++
++ dma_unmap_single(sdcp->dev, pkt_phys, sizeof(*pkt), DMA_TO_DEVICE);
++}
++
++static int dcp_aes_setkey_cip(struct crypto_tfm *tfm, const u8 *key,
++ unsigned int len)
++{
++ struct dcp_op *op = crypto_tfm_ctx(tfm);
++ unsigned int ret;
++
++ op->cipher.keylen = len;
++
++ if (len == AES_KEYSIZE_128) {
++ memcpy(op->cipher.key, key, len);
++ return 0;
++ }
++
++ if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) {
++ /* not supported at all */
++ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
++ return -EINVAL;
++ }
++
++ /*
++ * The requested key size is not supported by HW, do a fallback
++ */
++ op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
++ op->fallback.blk->base.crt_flags |= (tfm->crt_flags &
++ CRYPTO_TFM_REQ_MASK);
++
++ ret = crypto_cipher_setkey(op->fallback.cip, key, len);
++ if (ret) {
++ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
++ tfm->crt_flags |= (op->fallback.blk->base.crt_flags &
++ CRYPTO_TFM_RES_MASK);
++ }
++ return ret;
++}
++
++static void dcp_aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcp_op *op = crypto_tfm_ctx(tfm);
++
++ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128)) {
++ crypto_cipher_encrypt_one(op->fallback.cip, out, in);
++ return;
++ }
++
++ op->src = (void *) in;
++ op->dst = (void *) out;
++ op->flags = DCP_AES | DCP_ENC | DCP_ECB;
++ op->len = AES_KEYSIZE_128;
++
++ /* map the data */
++ op->src_phys = dma_map_single(sdcp->dev, (void *)in, AES_KEYSIZE_128,
++ DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
++ dev_err(sdcp->dev, "Unable to map source\n");
++ return;
++ }
++
++ op->dst_phys = dma_map_single(sdcp->dev, out, AES_KEYSIZE_128,
++ DMA_FROM_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
++ dev_err(sdcp->dev, "Unable to map dest\n");
++ goto err_unmap_src;
++ }
++
++ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
++ AES_KEYSIZE_128, DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
++ dev_err(sdcp->dev, "Unable to map key\n");
++ goto err_unmap_dst;
++ }
++
++ /* perform the operation */
++ dcp_perform_op(op);
++
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
++ DMA_TO_DEVICE);
++err_unmap_dst:
++ dma_unmap_single(sdcp->dev, op->dst_phys, op->len, DMA_FROM_DEVICE);
++err_unmap_src:
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len, DMA_TO_DEVICE);
++}
++
++static void dcp_aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcp_op *op = crypto_tfm_ctx(tfm);
++
++ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128)) {
++ crypto_cipher_decrypt_one(op->fallback.cip, out, in);
++ return;
++ }
++
++ op->src = (void *) in;
++ op->dst = (void *) out;
++ op->flags = DCP_AES | DCP_DEC | DCP_ECB;
++ op->len = AES_KEYSIZE_128;
++
++ /* map the data */
++ op->src_phys = dma_map_single(sdcp->dev, (void *)in, AES_KEYSIZE_128,
++ DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
++ dev_err(sdcp->dev, "Unable to map source\n");
++ return;
++ }
++
++ op->dst_phys = dma_map_single(sdcp->dev, out, AES_KEYSIZE_128,
++ DMA_FROM_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
++ dev_err(sdcp->dev, "Unable to map dest\n");
++ goto err_unmap_src;
++ }
++
++ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
++ AES_KEYSIZE_128, DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
++ dev_err(sdcp->dev, "Unable to map key\n");
++ goto err_unmap_dst;
++ }
++
++ /* perform the operation */
++ dcp_perform_op(op);
++
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
++ DMA_TO_DEVICE);
++err_unmap_dst:
++ dma_unmap_single(sdcp->dev, op->dst_phys, op->len, DMA_FROM_DEVICE);
++err_unmap_src:
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len, DMA_TO_DEVICE);
++}
++
++static int fallback_init_cip(struct crypto_tfm *tfm)
++{
++ const char *name = tfm->__crt_alg->cra_name;
++ struct dcp_op *op = crypto_tfm_ctx(tfm);
++
++ op->fallback.cip = crypto_alloc_cipher(name, 0,
++ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
++
++ if (IS_ERR(op->fallback.cip)) {
++ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
++ return PTR_ERR(op->fallback.cip);
++ }
++
++ return 0;
++}
++
++static void fallback_exit_cip(struct crypto_tfm *tfm)
++{
++ struct dcp_op *op = crypto_tfm_ctx(tfm);
++
++ crypto_free_cipher(op->fallback.cip);
++ op->fallback.cip = NULL;
++}
++
++static struct crypto_alg dcp_aes_alg = {
++ .cra_name = "aes",
++ .cra_driver_name = "dcp-aes",
++ .cra_priority = 300,
++ .cra_alignmask = 15,
++ .cra_flags = CRYPTO_ALG_TYPE_CIPHER |
++ CRYPTO_ALG_NEED_FALLBACK,
++ .cra_init = fallback_init_cip,
++ .cra_exit = fallback_exit_cip,
++ .cra_blocksize = AES_KEYSIZE_128,
++ .cra_ctxsize = sizeof(struct dcp_op),
++ .cra_module = THIS_MODULE,
++ .cra_list = LIST_HEAD_INIT(dcp_aes_alg.cra_list),
++ .cra_u = {
++ .cipher = {
++ .cia_min_keysize = AES_MIN_KEY_SIZE,
++ .cia_max_keysize = AES_MAX_KEY_SIZE,
++ .cia_setkey = dcp_aes_setkey_cip,
++ .cia_encrypt = dcp_aes_encrypt,
++ .cia_decrypt = dcp_aes_decrypt
++ }
++ }
++};
++
++static int dcp_aes_setkey_blk(struct crypto_tfm *tfm, const u8 *key,
++ unsigned int len)
++{
++ struct dcp_op *op = crypto_tfm_ctx(tfm);
++ unsigned int ret;
++
++ op->cipher.keylen = len;
++
++ if (len == AES_KEYSIZE_128) {
++ memcpy(op->cipher.key, key, len);
++ return 0;
++ }
++
++ if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) {
++ /* not supported at all */
++ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
++ return -EINVAL;
++ }
++
++ /*
++ * The requested key size is not supported by HW, do a fallback
++ */
++ op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
++ op->fallback.blk->base.crt_flags |= (tfm->crt_flags &
++ CRYPTO_TFM_REQ_MASK);
++
++ ret = crypto_blkcipher_setkey(op->fallback.blk, key, len);
++ if (ret) {
++ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
++ tfm->crt_flags |= (op->fallback.blk->base.crt_flags &
++ CRYPTO_TFM_RES_MASK);
++ }
++ return ret;
++}
++
++static int fallback_blk_dec(struct blkcipher_desc *desc,
++ struct scatterlist *dst, struct scatterlist *src,
++ unsigned int nbytes)
++{
++ unsigned int ret;
++ struct crypto_blkcipher *tfm;
++ struct dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
++
++ tfm = desc->tfm;
++ desc->tfm = op->fallback.blk;
++
++ ret = crypto_blkcipher_decrypt_iv(desc, dst, src, nbytes);
++
++ desc->tfm = tfm;
++ return ret;
++}
++
++static int fallback_blk_enc(struct blkcipher_desc *desc,
++ struct scatterlist *dst, struct scatterlist *src,
++ unsigned int nbytes)
++{
++ unsigned int ret;
++ struct crypto_blkcipher *tfm;
++ struct dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
++
++ tfm = desc->tfm;
++ desc->tfm = op->fallback.blk;
++
++ ret = crypto_blkcipher_encrypt_iv(desc, dst, src, nbytes);
++
++ desc->tfm = tfm;
++ return ret;
++}
++
++static int fallback_init_blk(struct crypto_tfm *tfm)
++{
++ const char *name = tfm->__crt_alg->cra_name;
++ struct dcp_op *op = crypto_tfm_ctx(tfm);
++
++ op->fallback.blk = crypto_alloc_blkcipher(name, 0,
++ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
++
++ if (IS_ERR(op->fallback.blk)) {
++ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
++ return PTR_ERR(op->fallback.blk);
++ }
++
++ return 0;
++}
++
++static void fallback_exit_blk(struct crypto_tfm *tfm)
++{
++ struct dcp_op *op = crypto_tfm_ctx(tfm);
++
++ crypto_free_blkcipher(op->fallback.blk);
++ op->fallback.blk = NULL;
++}
++
++static int
++dcp_aes_ecb_decrypt(struct blkcipher_desc *desc,
++ struct scatterlist *dst, struct scatterlist *src,
++ unsigned int nbytes)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
++ struct blkcipher_walk walk;
++ int err;
++
++ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
++ return fallback_blk_dec(desc, dst, src, nbytes);
++
++ blkcipher_walk_init(&walk, dst, src, nbytes);
++
++ /* key needs to be mapped only once */
++ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
++ AES_KEYSIZE_128, DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
++ dev_err(sdcp->dev, "Unable to map key\n");
++ return -ENOMEM;
++ }
++
++ err = blkcipher_walk_virt(desc, &walk);
++ while (err == 0 && (nbytes = walk.nbytes) > 0) {
++ op->src = walk.src.virt.addr,
++ op->dst = walk.dst.virt.addr;
++ op->flags = DCP_AES | DCP_DEC |
++ DCP_ECB;
++ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
++
++ /* map the data */
++ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
++ DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
++ dev_err(sdcp->dev, "Unable to map source\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
++ DMA_FROM_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
++ DMA_TO_DEVICE);
++ dev_err(sdcp->dev, "Unable to map dest\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ /* perform! */
++ dcp_perform_op(op);
++
++ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
++ DMA_FROM_DEVICE);
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
++ DMA_TO_DEVICE);
++
++ nbytes -= op->len;
++ err = blkcipher_walk_done(desc, &walk, nbytes);
++ }
++
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
++ DMA_TO_DEVICE);
++
++ return err;
++}
++
++static int
++dcp_aes_ecb_encrypt(struct blkcipher_desc *desc,
++ struct scatterlist *dst, struct scatterlist *src,
++ unsigned int nbytes)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
++ struct blkcipher_walk walk;
++ int err, ret;
++
++ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
++ return fallback_blk_enc(desc, dst, src, nbytes);
++
++ blkcipher_walk_init(&walk, dst, src, nbytes);
++
++ /* key needs to be mapped only once */
++ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
++ AES_KEYSIZE_128, DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
++ dev_err(sdcp->dev, "Unable to map key\n");
++ return -ENOMEM;
++ }
++
++ err = blkcipher_walk_virt(desc, &walk);
++
++ err = 0;
++ while (err == 0 && (nbytes = walk.nbytes) > 0) {
++ op->src = walk.src.virt.addr,
++ op->dst = walk.dst.virt.addr;
++ op->flags = DCP_AES | DCP_ENC |
++ DCP_ECB;
++ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
++
++ /* map the data */
++ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
++ DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
++ dev_err(sdcp->dev, "Unable to map source\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
++ DMA_FROM_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
++ DMA_TO_DEVICE);
++ dev_err(sdcp->dev, "Unable to map dest\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ /* perform! */
++ dcp_perform_op(op);
++
++ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
++ DMA_FROM_DEVICE);
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
++ DMA_TO_DEVICE);
++
++ nbytes -= op->len;
++ ret = blkcipher_walk_done(desc, &walk, nbytes);
++ }
++
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
++ DMA_TO_DEVICE);
++
++ return err;
++}
++
++
++static struct crypto_alg dcp_aes_ecb_alg = {
++ .cra_name = "ecb(aes)",
++ .cra_driver_name = "dcp-ecb-aes",
++ .cra_priority = 400,
++ .cra_alignmask = 15,
++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER |
++ CRYPTO_ALG_NEED_FALLBACK,
++ .cra_init = fallback_init_blk,
++ .cra_exit = fallback_exit_blk,
++ .cra_blocksize = AES_KEYSIZE_128,
++ .cra_ctxsize = sizeof(struct dcp_op),
++ .cra_type = &crypto_blkcipher_type,
++ .cra_module = THIS_MODULE,
++ .cra_list = LIST_HEAD_INIT(dcp_aes_ecb_alg.cra_list),
++ .cra_u = {
++ .blkcipher = {
++ .min_keysize = AES_MIN_KEY_SIZE,
++ .max_keysize = AES_MAX_KEY_SIZE,
++ .setkey = dcp_aes_setkey_blk,
++ .encrypt = dcp_aes_ecb_encrypt,
++ .decrypt = dcp_aes_ecb_decrypt
++ }
++ }
++};
++
++static int
++dcp_aes_cbc_decrypt(struct blkcipher_desc *desc,
++ struct scatterlist *dst, struct scatterlist *src,
++ unsigned int nbytes)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
++ struct blkcipher_walk walk;
++ int err, blockno;
++
++ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
++ return fallback_blk_dec(desc, dst, src, nbytes);
++
++ blkcipher_walk_init(&walk, dst, src, nbytes);
++
++ blockno = 0;
++ err = blkcipher_walk_virt(desc, &walk);
++ while (err == 0 && (nbytes = walk.nbytes) > 0) {
++ op->src = walk.src.virt.addr,
++ op->dst = walk.dst.virt.addr;
++ op->flags = DCP_AES | DCP_DEC |
++ DCP_CBC;
++ if (blockno == 0) {
++ op->flags |= DCP_CBC_INIT;
++ memcpy(op->cipher.key + AES_KEYSIZE_128, walk.iv,
++ AES_KEYSIZE_128);
++ }
++ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
++
++ /* key (+iv) needs to be mapped only once */
++ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
++ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
++ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
++ dev_err(sdcp->dev, "Unable to map key\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ /* map the data */
++ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
++ DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
++ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
++ dev_err(sdcp->dev, "Unable to map source\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
++ DMA_FROM_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
++ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
++ DMA_TO_DEVICE);
++ dev_err(sdcp->dev, "Unable to map dest\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ /* perform! */
++ dcp_perform_op(op);
++
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
++ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
++ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
++ DMA_FROM_DEVICE);
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
++ DMA_TO_DEVICE);
++
++ nbytes -= op->len;
++ err = blkcipher_walk_done(desc, &walk, nbytes);
++
++ blockno++;
++ }
++
++ return err;
++}
++
++static int
++dcp_aes_cbc_encrypt(struct blkcipher_desc *desc,
++ struct scatterlist *dst, struct scatterlist *src,
++ unsigned int nbytes)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
++ struct blkcipher_walk walk;
++ int err, ret, blockno;
++
++ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
++ return fallback_blk_enc(desc, dst, src, nbytes);
++
++ blkcipher_walk_init(&walk, dst, src, nbytes);
++
++ blockno = 0;
++
++ err = blkcipher_walk_virt(desc, &walk);
++ while (err == 0 && (nbytes = walk.nbytes) > 0) {
++ op->src = walk.src.virt.addr,
++ op->dst = walk.dst.virt.addr;
++ op->flags = DCP_AES | DCP_ENC |
++ DCP_CBC;
++ if (blockno == 0) {
++ op->flags |= DCP_CBC_INIT;
++ memcpy(op->cipher.key + AES_KEYSIZE_128, walk.iv,
++ AES_KEYSIZE_128);
++ }
++ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
++
++ /* key needs to be mapped only once */
++ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
++ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
++ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
++ dev_err(sdcp->dev, "Unable to map key\n");
++ return -ENOMEM;
++ }
++
++ /* map the data */
++ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
++ DMA_TO_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
++ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
++ dev_err(sdcp->dev, "Unable to map source\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
++ DMA_FROM_DEVICE);
++ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
++ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
++ DMA_TO_DEVICE);
++ dev_err(sdcp->dev, "Unable to map dest\n");
++ err = -ENOMEM;
++ break;
++ }
++
++ /* perform! */
++ dcp_perform_op(op);
++
++ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
++ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
++ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
++ DMA_FROM_DEVICE);
++ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
++ DMA_TO_DEVICE);
++
++ nbytes -= op->len;
++ ret = blkcipher_walk_done(desc, &walk, nbytes);
++
++ blockno++;
++ }
++
++ return err;
++}
++
++static struct crypto_alg dcp_aes_cbc_alg = {
++ .cra_name = "cbc(aes)",
++ .cra_driver_name = "dcp-cbc-aes",
++ .cra_priority = 400,
++ .cra_alignmask = 15,
++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER |
++ CRYPTO_ALG_NEED_FALLBACK,
++ .cra_init = fallback_init_blk,
++ .cra_exit = fallback_exit_blk,
++ .cra_blocksize = AES_KEYSIZE_128,
++ .cra_ctxsize = sizeof(struct dcp_op),
++ .cra_type = &crypto_blkcipher_type,
++ .cra_module = THIS_MODULE,
++ .cra_list = LIST_HEAD_INIT(dcp_aes_cbc_alg.cra_list),
++ .cra_u = {
++ .blkcipher = {
++ .min_keysize = AES_MIN_KEY_SIZE,
++ .max_keysize = AES_MAX_KEY_SIZE,
++ .setkey = dcp_aes_setkey_blk,
++ .encrypt = dcp_aes_cbc_encrypt,
++ .decrypt = dcp_aes_cbc_decrypt,
++ .ivsize = AES_KEYSIZE_128,
++ }
++ }
++};
++
++static int dcp_perform_hash_op(
++ struct dcp_hash_coherent_block *input,
++ u32 num_desc, bool init, bool terminate)
++{
++ struct dcp *sdcp = global_sdcp;
++ int chan;
++ struct dcp_hw_packet *pkt;
++ struct dcp_hash_coherent_block *hw;
++ unsigned long timeout;
++ u32 stat;
++ int descno, mapped;
++
++ chan = HASH_CHAN;
++
++ hw = input;
++ pkt = hw->pkt;
++
++ for (descno = 0; descno < num_desc; descno++) {
++
++ if (descno != 0) {
++
++ /* set next ptr and CHAIN bit in last packet */
++ pkt->pNext = hw->next->my_phys + offsetof(
++ struct dcp_hash_coherent_block,
++ pkt[0]);
++ pkt->pkt1 |= BM_DCP_PACKET1_CHAIN;
++
++ /* iterate to next descriptor */
++ hw = hw->next;
++ pkt = hw->pkt;
++ }
++
++ pkt->pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE |
++ BM_DCP_PACKET1_ENABLE_HASH;
++
++ if (init && descno == 0)
++ pkt->pkt1 |= BM_DCP_PACKET1_HASH_INIT;
++
++ pkt->pkt2 = BF(hw->hash_sel,
++ DCP_PACKET2_HASH_SELECT);
++
++ /* no need to flush buf1 or buf2, which are uncached */
++ if (hw->src != sdcp->buf1 && hw->src != sdcp->buf2) {
++
++ /* we have to flush the cache for the buffer */
++ hw->src_phys = dma_map_single(sdcp->dev,
++ hw->src, hw->len, DMA_TO_DEVICE);
++
++ if (dma_mapping_error(sdcp->dev, hw->src_phys)) {
++ dev_err(sdcp->dev, "Unable to map source\n");
++
++ /* unmap any previous mapped buffers */
++ for (mapped = 0, hw = input; mapped < descno;
++ mapped++) {
++
++ if (mapped != 0)
++ hw = hw->next;
++ if (hw->src != sdcp->buf1 &&
++ hw->src != sdcp->buf2)
++ dma_unmap_single(sdcp->dev,
++ hw->src_phys, hw->len,
++ DMA_TO_DEVICE);
++ }
++
++ return -EFAULT;
++ }
++ }
++
++ pkt->pSrc = (u32)hw->src_phys;
++ pkt->pDst = 0;
++ pkt->size = hw->len;
++ pkt->pPayload = 0;
++ pkt->stat = 0;
++
++ /* set HASH_TERM bit on last buf if terminate was set */
++ if (terminate && (descno == (num_desc - 1))) {
++ pkt->pkt1 |= BM_DCP_PACKET1_HASH_TERM;
++
++ memset(input->digest, 0, sizeof(input->digest));
++
++ /* set payload ptr to the 1st buffer's digest */
++ pkt->pPayload = (u32)input->my_phys +
++ offsetof(
++ struct dcp_hash_coherent_block,
++ digest);
++ }
++ }
++
++ /* submit the work */
++
++ __raw_writel(-1, sdcp->dcp_regs_base + HW_DCP_CHnSTAT_CLR(chan));
++
++ mb();
++ /* Load the 1st descriptor's physical address */
++ __raw_writel((u32)input->my_phys +
++ offsetof(struct dcp_hash_coherent_block,
++ pkt[0]), sdcp->dcp_regs_base + HW_DCP_CHnCMDPTR(chan));
++
++ /* XXX wake from interrupt instead of looping */
++ timeout = jiffies + msecs_to_jiffies(1000);
++
++ /* write num_desc into sema register */
++ __raw_writel(BF(num_desc, DCP_CHnSEMA_INCREMENT),
++ sdcp->dcp_regs_base + HW_DCP_CHnSEMA(chan));
++
++ while (time_before(jiffies, timeout) &&
++ ((__raw_readl(sdcp->dcp_regs_base +
++ HW_DCP_CHnSEMA(chan)) >> 16) & 0xff) != 0) {
++
++ cpu_relax();
++ }
++
++ if (!time_before(jiffies, timeout)) {
++ dev_err(sdcp->dev,
++ "Timeout while waiting STAT 0x%08x\n",
++ __raw_readl(sdcp->dcp_regs_base + HW_DCP_STAT));
++ }
++
++ stat = __raw_readl(sdcp->dcp_regs_base + HW_DCP_CHnSTAT(chan));
++ if ((stat & 0xff) != 0)
++ dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
++ __raw_readl(sdcp->dcp_regs_base +
++ HW_DCP_CHnSTAT(chan)) & 0xff);
++
++ /* unmap all src buffers */
++ for (descno = 0, hw = input; descno < num_desc; descno++) {
++ if (descno != 0)
++ hw = hw->next;
++ if (hw->src != sdcp->buf1 && hw->src != sdcp->buf2)
++ dma_unmap_single(sdcp->dev, hw->src_phys, hw->len,
++ DMA_TO_DEVICE);
++ }
++
++ return 0;
++
++}
++
++static int dcp_sha_init(struct shash_desc *desc)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcp_hash_op *op = shash_desc_ctx(desc);
++ struct mutex *mutex = &sdcp->op_mutex[HASH_CHAN];
++
++ mutex_lock(mutex);
++
++ op->length = 0;
++
++ /* reset the lengths and the pointers of buffer descriptors */
++ sdcp->buf1_desc->len = 0;
++ sdcp->buf1_desc->src = sdcp->buf1;
++ sdcp->buf2_desc->len = 0;
++ sdcp->buf2_desc->src = sdcp->buf2;
++ op->head_desc = sdcp->buf1_desc;
++ op->tail_desc = sdcp->buf2_desc;
++
++ return 0;
++}
++
++static int dcp_sha_update(struct shash_desc *desc, const u8 *data,
++ unsigned int length)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcp_hash_op *op = shash_desc_ctx(desc);
++ struct dcp_hash_coherent_block *temp;
++ u32 rem_bytes, bytes_borrowed, hash_sel;
++ int ret = 0;
++
++ if (strcmp(desc->tfm->base.__crt_alg->cra_name, "sha1") == 0)
++ hash_sel = BV_DCP_PACKET2_HASH_SELECT__SHA1;
++ else
++ hash_sel = BV_DCP_PACKET2_HASH_SELECT__SHA256;
++
++ sdcp->user_buf_desc->src = (void *)data;
++ sdcp->user_buf_desc->len = length;
++
++ op->tail_desc->len = 0;
++
++ /* check if any pending data from previous updates */
++ if (op->head_desc->len) {
++
++ /* borrow from this buffer to make it 64 bytes */
++ bytes_borrowed = min(64 - op->head_desc->len,
++ sdcp->user_buf_desc->len);
++
++ /* copy n bytes to head */
++ memcpy(op->head_desc->src + op->head_desc->len,
++ sdcp->user_buf_desc->src, bytes_borrowed);
++ op->head_desc->len += bytes_borrowed;
++
++ /* update current buffer's src and len */
++ sdcp->user_buf_desc->src += bytes_borrowed;
++ sdcp->user_buf_desc->len -= bytes_borrowed;
++ }
++
++ /* Is current buffer unaligned to 64 byte length?
++ * Each buffer's length must be a multiple of 64 bytes for DCP
++ */
++ rem_bytes = sdcp->user_buf_desc->len % 64;
++
++ /* if length is unaligned, copy remainder to tail */
++ if (rem_bytes) {
++
++ memcpy(op->tail_desc->src, (sdcp->user_buf_desc->src +
++ sdcp->user_buf_desc->len - rem_bytes),
++ rem_bytes);
++
++ /* update length of current buffer */
++ sdcp->user_buf_desc->len -= rem_bytes;
++
++ op->tail_desc->len = rem_bytes;
++ }
++
++ /* do not send to DCP if length is < 64 */
++ if ((op->head_desc->len + sdcp->user_buf_desc->len) >= 64) {
++
++ /* set hash alg to be used (SHA1 or SHA256) */
++ op->head_desc->hash_sel = hash_sel;
++ sdcp->user_buf_desc->hash_sel = hash_sel;
++
++ if (op->head_desc->len) {
++ op->head_desc->next = sdcp->user_buf_desc;
++
++ ret = dcp_perform_hash_op(op->head_desc,
++ sdcp->user_buf_desc->len ? 2 : 1,
++ op->length == 0, false);
++ } else {
++ ret = dcp_perform_hash_op(sdcp->user_buf_desc, 1,
++ op->length == 0, false);
++ }
++
++ op->length += op->head_desc->len + sdcp->user_buf_desc->len;
++ op->head_desc->len = 0;
++ }
++
++ /* if tail has bytes, make it the head for next time */
++ if (op->tail_desc->len) {
++ temp = op->head_desc;
++ op->head_desc = op->tail_desc;
++ op->tail_desc = temp;
++ }
++
++ /* hash_sel to be used by final function */
++ op->head_desc->hash_sel = hash_sel;
++
++ return ret;
++}
++
++static int dcp_sha_final(struct shash_desc *desc, u8 *out)
++{
++ struct dcp_hash_op *op = shash_desc_ctx(desc);
++ const uint8_t *digest;
++ struct dcp *sdcp = global_sdcp;
++ u32 i, digest_len;
++ struct mutex *mutex = &sdcp->op_mutex[HASH_CHAN];
++ int ret = 0;
++
++ /* Send the leftover bytes in head, which can be length 0,
++ * but DCP still produces hash result in payload ptr.
++ * Last data bytes need not be 64-byte multiple.
++ */
++ ret = dcp_perform_hash_op(op->head_desc, 1, op->length == 0, true);
++
++ op->length += op->head_desc->len;
++
++ digest_len = (op->head_desc->hash_sel ==
++ BV_DCP_PACKET2_HASH_SELECT__SHA1) ? SHA1_DIGEST_SIZE :
++ SHA256_DIGEST_SIZE;
++
++ /* hardware reverses the digest (for some unexplicable reason) */
++ digest = op->head_desc->digest + digest_len;
++ for (i = 0; i < digest_len; i++)
++ *out++ = *--digest;
++
++ mutex_unlock(mutex);
++
++ return ret;
++}
++
++static struct shash_alg dcp_sha1_alg = {
++ .init = dcp_sha_init,
++ .update = dcp_sha_update,
++ .final = dcp_sha_final,
++ .descsize = sizeof(struct dcp_hash_op),
++ .digestsize = SHA1_DIGEST_SIZE,
++ .base = {
++ .cra_name = "sha1",
++ .cra_driver_name = "sha1-dcp",
++ .cra_priority = 300,
++ .cra_blocksize = SHA1_BLOCK_SIZE,
++ .cra_ctxsize =
++ sizeof(struct dcp_hash_op),
++ .cra_module = THIS_MODULE,
++ }
++};
++
++static struct shash_alg dcp_sha256_alg = {
++ .init = dcp_sha_init,
++ .update = dcp_sha_update,
++ .final = dcp_sha_final,
++ .descsize = sizeof(struct dcp_hash_op),
++ .digestsize = SHA256_DIGEST_SIZE,
++ .base = {
++ .cra_name = "sha256",
++ .cra_driver_name = "sha256-dcp",
++ .cra_priority = 300,
++ .cra_blocksize = SHA256_BLOCK_SIZE,
++ .cra_ctxsize =
++ sizeof(struct dcp_hash_op),
++ .cra_module = THIS_MODULE,
++ }
++};
++
++static irqreturn_t dcp_common_irq(int irq, void *context)
++{
++ struct dcp *sdcp = context;
++ u32 msk;
++
++ /* check */
++ msk = __raw_readl(sdcp->dcp_regs_base + HW_DCP_STAT) &
++ BF(0x0f, DCP_STAT_IRQ);
++ if (msk == 0)
++ return IRQ_NONE;
++
++ /* clear this channel */
++ __raw_writel(msk, sdcp->dcp_regs_base + HW_DCP_STAT_CLR);
++ if (msk & BF(0x01, DCP_STAT_IRQ))
++ sdcp->wait[0]++;
++ if (msk & BF(0x02, DCP_STAT_IRQ))
++ sdcp->wait[1]++;
++ if (msk & BF(0x04, DCP_STAT_IRQ))
++ sdcp->wait[2]++;
++ if (msk & BF(0x08, DCP_STAT_IRQ))
++ sdcp->wait[3]++;
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t dcp_vmi_irq(int irq, void *context)
++{
++ return dcp_common_irq(irq, context);
++}
++
++static irqreturn_t dcp_irq(int irq, void *context)
++{
++ return dcp_common_irq(irq, context);
++}
++
++/* DCP bootstream verification interface: uses OTP key for crypto */
++static int dcp_bootstream_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct dcp *sdcp = global_sdcp;
++ struct dcpboot_dma_area *da = sdcp->dcpboot_dma_area;
++ void __user *argp = (void __user *)arg;
++ int chan = ROM_DCP_CHAN;
++ unsigned long timeout;
++ struct mutex *mutex;
++ int retVal;
++
++ /* be paranoid */
++ if (sdcp == NULL)
++ return -EBADF;
++
++ if (cmd != DBS_ENC && cmd != DBS_DEC)
++ return -EINVAL;
++
++ /* copy to (aligned) block */
++ if (copy_from_user(da->block, argp, 16))
++ return -EFAULT;
++
++ mutex = &sdcp->op_mutex[chan];
++ mutex_lock(mutex);
++
++ __raw_writel(-1, sdcp->dcp_regs_base +
++ HW_DCP_CHnSTAT_CLR(ROM_DCP_CHAN));
++ __raw_writel(BF(ROM_DCP_CHAN_MASK, DCP_STAT_IRQ),
++ sdcp->dcp_regs_base + HW_DCP_STAT_CLR);
++
++ da->hw_packet.pNext = 0;
++ da->hw_packet.pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE |
++ BM_DCP_PACKET1_ENABLE_CIPHER | BM_DCP_PACKET1_OTP_KEY |
++ BM_DCP_PACKET1_INTERRUPT |
++ (cmd == DBS_ENC ? BM_DCP_PACKET1_CIPHER_ENCRYPT : 0);
++ da->hw_packet.pkt2 = BF(0, DCP_PACKET2_CIPHER_CFG) |
++ BF(0, DCP_PACKET2_KEY_SELECT) |
++ BF(BV_DCP_PACKET2_CIPHER_MODE__ECB, DCP_PACKET2_CIPHER_MODE) |
++ BF(BV_DCP_PACKET2_CIPHER_SELECT__AES128, DCP_PACKET2_CIPHER_SELECT);
++ da->hw_packet.pSrc = sdcp->dcpboot_dma_area_phys +
++ offsetof(struct dcpboot_dma_area, block);
++ da->hw_packet.pDst = da->hw_packet.pSrc; /* in-place */
++ da->hw_packet.size = 16;
++ da->hw_packet.pPayload = 0;
++ da->hw_packet.stat = 0;
++
++ /* Load the work packet pointer and bump the channel semaphore */
++ __raw_writel(sdcp->dcpboot_dma_area_phys +
++ offsetof(struct dcpboot_dma_area, hw_packet),
++ sdcp->dcp_regs_base + HW_DCP_CHnCMDPTR(ROM_DCP_CHAN));
++
++ sdcp->wait[chan] = 0;
++ __raw_writel(BF(1, DCP_CHnSEMA_INCREMENT),
++ sdcp->dcp_regs_base + HW_DCP_CHnSEMA(ROM_DCP_CHAN));
++
++ timeout = jiffies + msecs_to_jiffies(100);
++
++ while (time_before(jiffies, timeout) && sdcp->wait[chan] == 0)
++ cpu_relax();
++
++ if (!time_before(jiffies, timeout)) {
++ dev_err(sdcp->dev,
++ "Timeout while waiting for operation to complete\n");
++ retVal = -ETIMEDOUT;
++ goto exit;
++ }
++
++ if ((__raw_readl(sdcp->dcp_regs_base + HW_DCP_CHnSTAT(ROM_DCP_CHAN))
++ & 0xff) != 0) {
++ dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
++ __raw_readl(sdcp->dcp_regs_base +
++ HW_DCP_CHnSTAT(ROM_DCP_CHAN)) & 0xff);
++ retVal = -EFAULT;
++ goto exit;
++ }
++
++ if (copy_to_user(argp, da->block, 16)) {
++ retVal = -EFAULT;
++ goto exit;
++ }
++
++ retVal = 0;
++
++exit:
++ mutex_unlock(mutex);
++ return retVal;
++}
++
++static const struct file_operations dcp_bootstream_fops = {
++ .owner = THIS_MODULE,
++ .ioctl = dcp_bootstream_ioctl,
++};
++
++static struct miscdevice dcp_bootstream_misc = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = "dcpboot",
++ .fops = &dcp_bootstream_fops,
++};
++
++static int dcp_probe(struct platform_device *pdev)
++{
++ struct dcp *sdcp = NULL;
++ struct resource *r;
++ int i, ret;
++ dma_addr_t hw_phys;
++
++ if (global_sdcp != NULL) {
++ dev_err(&pdev->dev, "Only one instance allowed\n");
++ ret = -ENODEV;
++ goto err;
++ }
++
++ /* allocate memory */
++ sdcp = kzalloc(sizeof(*sdcp), GFP_KERNEL);
++ if (sdcp == NULL) {
++ dev_err(&pdev->dev, "Failed to allocate structure\n");
++ ret = -ENOMEM;
++ goto err;
++ }
++
++ sdcp->dev = &pdev->dev;
++ spin_lock_init(&sdcp->lock);
++
++ for (i = 0; i < DCP_NUM_CHANNELS; i++) {
++ mutex_init(&sdcp->op_mutex[i]);
++ init_completion(&sdcp->op_wait[i]);
++ }
++
++ platform_set_drvdata(pdev, sdcp);
++
++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!r) {
++ dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n");
++ ret = -ENXIO;
++ goto err_kfree;
++ }
++ sdcp->dcp_regs_base = (u32) IO_ADDRESS(r->start);
++
++ /* Soft reset and remove the clock gate */
++ __raw_writel(BM_DCP_CTRL_SFTRST, sdcp->dcp_regs_base + HW_DCP_CTRL_SET);
++
++ /* At 24Mhz, it takes no more than 4 clocks (160 ns) Maximum for
++ * the part to reset, reading the register twice should
++ * be sufficient to get 4 clks delay.
++ */
++ __raw_readl(sdcp->dcp_regs_base + HW_DCP_CTRL);
++ __raw_readl(sdcp->dcp_regs_base + HW_DCP_CTRL);
++
++ __raw_writel(BM_DCP_CTRL_SFTRST | BM_DCP_CTRL_CLKGATE,
++ sdcp->dcp_regs_base + HW_DCP_CTRL_CLR);
++
++ /* Initialize control registers */
++ __raw_writel(DCP_CTRL_INIT, sdcp->dcp_regs_base + HW_DCP_CTRL);
++ __raw_writel(DCP_CHANNELCTRL_INIT, sdcp->dcp_regs_base +
++ HW_DCP_CHANNELCTRL);
++
++ /* We do not enable context switching. Give the context
++ * buffer pointer an illegal address so if context switching is
++ * inadvertantly enabled, the dcp will return an error instead of
++ * trashing good memory. The dcp dma cannot access rom, so any rom
++ * address will do.
++ */
++ __raw_writel(0xFFFF0000, sdcp->dcp_regs_base + HW_DCP_CONTEXT);
++
++ for (i = 0; i < DCP_NUM_CHANNELS; i++)
++ __raw_writel(-1, sdcp->dcp_regs_base + HW_DCP_CHnSTAT_CLR(i));
++ __raw_writel(-1, sdcp->dcp_regs_base + HW_DCP_STAT_CLR);
++
++ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
++ if (!r) {
++ dev_err(&pdev->dev, "can't get IRQ resource (0)\n");
++ ret = -EIO;
++ goto err_kfree;
++ }
++ sdcp->dcp_vmi_irq = r->start;
++ ret = request_irq(sdcp->dcp_vmi_irq, dcp_vmi_irq, 0, "dcp",
++ sdcp);
++ if (ret != 0) {
++ dev_err(&pdev->dev, "can't request_irq (0)\n");
++ goto err_kfree;
++ }
++
++ r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
++ if (!r) {
++ dev_err(&pdev->dev, "can't get IRQ resource (1)\n");
++ ret = -EIO;
++ goto err_free_irq0;
++ }
++ sdcp->dcp_irq = r->start;
++ ret = request_irq(sdcp->dcp_irq, dcp_irq, 0, "dcp", sdcp);
++ if (ret != 0) {
++ dev_err(&pdev->dev, "can't request_irq (1)\n");
++ goto err_free_irq0;
++ }
++
++ global_sdcp = sdcp;
++
++ ret = crypto_register_alg(&dcp_aes_alg);
++ if (ret != 0) {
++ dev_err(&pdev->dev, "Failed to register aes crypto\n");
++ goto err_kfree;
++ }
++
++ ret = crypto_register_alg(...
[truncated message content] |