diff mbox

[v7,5/5] scsi: ufs-qcom-ice: add Inline Crypto Engine (ICE) support for UFS

Message ID 1421332359-31785-6-git-send-email-ygardi@codeaurora.org (mailing list archive)
State Not Applicable, archived
Delegated to: Andy Gross
Headers show

Commit Message

Yaniv Gardi Jan. 15, 2015, 2:32 p.m. UTC
From: Yaniv Gardi <ygardi@qti.qualcomm.com>

In-order to enhance storage encryption performance,
an Inline Cryptographic Engine is introduced to UFS.
This patch adds in-line encryption capabilities to the UFS
driver.

Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>

---
 drivers/scsi/ufs/Kconfig        |  12 +
 drivers/scsi/ufs/Makefile       |   1 +
 drivers/scsi/ufs/ufs-qcom-ice.c | 520 ++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/ufs/ufs-qcom-ice.h | 113 +++++++++
 drivers/scsi/ufs/ufs-qcom.c     |  56 ++++-
 drivers/scsi/ufs/ufs-qcom.h     |  25 ++
 6 files changed, 726 insertions(+), 1 deletion(-)
 create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.c
 create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.h

Comments

dovl@codeaurora.org Jan. 15, 2015, 3:21 p.m. UTC | #1
Reviewed-by: Dov Levenglick <dovl@codeaurora.org>

> From: Yaniv Gardi <ygardi@qti.qualcomm.com>
>
> In-order to enhance storage encryption performance,
> an Inline Cryptographic Engine is introduced to UFS.
> This patch adds in-line encryption capabilities to the UFS
> driver.
>
> Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
>
> ---
>  drivers/scsi/ufs/Kconfig        |  12 +
>  drivers/scsi/ufs/Makefile       |   1 +
>  drivers/scsi/ufs/ufs-qcom-ice.c | 520
> ++++++++++++++++++++++++++++++++++++++++
>  drivers/scsi/ufs/ufs-qcom-ice.h | 113 +++++++++
>  drivers/scsi/ufs/ufs-qcom.c     |  56 ++++-
>  drivers/scsi/ufs/ufs-qcom.h     |  25 ++
>  6 files changed, 726 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.c
>  create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.h
>
> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
> index 8a1f4b3..ecf34ed 100644
> --- a/drivers/scsi/ufs/Kconfig
> +++ b/drivers/scsi/ufs/Kconfig
> @@ -83,3 +83,15 @@ config SCSI_UFS_QCOM
>
>  	  Select this if you have UFS controller on QCOM chipset.
>  	  If unsure, say N.
> +
> +config SCSI_UFS_QCOM_ICE
> +	bool "QCOM specific hooks to Inline Crypto Engine for UFS driver"
> +	depends on SCSI_UFS_QCOM && CRYPTO_DEV_QCOM_ICE
> +	help
> +	  This selects the QCOM specific additions to support Inline
> Crypto
> +	  Engine (ICE).
> +	  ICE accelerates the crypto operations and maintains the high UFS
> +	  performance.
> +
> +	  Select this if you have ICE supported for UFS on QCOM chipset.
> +	  If unsure, say N.
> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
> index 8303bcc..31adca5 100644
> --- a/drivers/scsi/ufs/Makefile
> +++ b/drivers/scsi/ufs/Makefile
> @@ -1,5 +1,6 @@
>  # UFSHCD makefile
>  obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
> +obj-$(CONFIG_SCSI_UFS_QCOM_ICE) += ufs-qcom-ice.o
>  obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
>  obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
>  obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
> diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c
> b/drivers/scsi/ufs/ufs-qcom-ice.c
> new file mode 100644
> index 0000000..9202b73
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs-qcom-ice.c
> @@ -0,0 +1,520 @@
> +/* Copyright (c) 2014-2015, The Linux Foundation. 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 version 2 and
> + * only version 2 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.
> + */
> +
> +#include <linux/of.h>
> +#include <linux/async.h>
> +#include <linux/blkdev.h>
> +#include <crypto/ice.h>
> +
> +#include "ufs-qcom-ice.h"
> +#include "ufshcd.h"
> +
> +#define UFS_QCOM_CRYPTO_LABEL "ufs-qcom-crypto"
> +/* Timeout waiting for ICE initialization, that requires TZ access */
> +#define UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS 500
> +
> +static void ufs_qcom_ice_success_cb(void *host_ctrl,
> +				enum ice_event_completion evt)
> +{
> +	struct ufs_qcom_host *qcom_host = (struct ufs_qcom_host
> *)host_ctrl;
> +
> +	if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_DISABLED &&
> +	    evt == ICE_INIT_COMPLETION)
> +		qcom_host->ice.state = UFS_QCOM_ICE_STATE_ACTIVE;
> +	 else if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_SUSPENDED &&
> +		   evt == ICE_RESUME_COMPLETION)
> +		qcom_host->ice.state = UFS_QCOM_ICE_STATE_ACTIVE;
> +
> +	complete(&qcom_host->ice.async_done);
> +}
> +
> +static void ufs_qcom_ice_error_cb(void *host_ctrl, enum ice_error_code
> evt)
> +{
> +	struct ufs_qcom_host *qcom_host = (struct ufs_qcom_host
> *)host_ctrl;
> +
> +	dev_err(qcom_host->hba->dev, "%s: Error in ice operation %d",
> +		__func__, evt);
> +
> +	if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_ACTIVE)
> +		qcom_host->ice.state = UFS_QCOM_ICE_STATE_DISABLED;
> +
> +	complete(&qcom_host->ice.async_done);
> +}
> +
> +static struct platform_device *ufs_qcom_ice_get_pdevice(struct device
> *ufs_dev)
> +{
> +	struct device_node *node;
> +	struct platform_device *ice_pdev = NULL;
> +
> +	node = of_parse_phandle(ufs_dev->of_node, UFS_QCOM_CRYPTO_LABEL,
> 0);
> +
> +	if (!node) {
> +		dev_err(ufs_dev, "%s: ufs-qcom-crypto property not
> specified\n",
> +			__func__);
> +		goto out;
> +	}
> +
> +	ice_pdev = qcom_ice_get_pdevice(node);
> +out:
> +	return ice_pdev;
> +}
> +
> +static
> +struct qcom_ice_variant_ops *ufs_qcom_ice_get_vops(struct device
> *ufs_dev)
> +{
> +	struct qcom_ice_variant_ops *ice_vops = NULL;
> +	struct device_node *node;
> +
> +	node = of_parse_phandle(ufs_dev->of_node, UFS_QCOM_CRYPTO_LABEL,
> 0);
> +
> +	if (!node) {
> +		dev_err(ufs_dev, "%s: ufs-qcom-crypto property not
> specified\n",
> +			__func__);
> +		goto out;
> +	}
> +
> +	ice_vops = qcom_ice_get_variant_ops(node);
> +
> +	if (!ice_vops)
> +		dev_err(ufs_dev, "%s: invalid ice_vops\n", __func__);
> +
> +	of_node_put(node);
> +out:
> +	return ice_vops;
> +}
> +
> +/**
> + * ufs_qcom_ice_get_dev() - sets pointers to ICE data structs in UFS QCom
> host
> + * @qcom_host:	Pointer to a UFS QCom internal host structure.
> + *
> + * Sets ICE platform device pointer and ICE vops structure
> + * corresponding to the current UFS device.
> + *
> + * Return: -EINVAL in-case of invalid input parameters:
> + *  qcom_host, qcom_host->hba or qcom_host->hba->dev
> + *         -ENODEV in-case ICE device is not required
> + *         -EPROBE_DEFER in-case ICE is required and hasn't been probed
> yet
> + *         0 otherwise
> + */
> +int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host)
> +{
> +	struct device *ufs_dev;
> +	int err = 0;
> +
> +	if (!qcom_host || !qcom_host->hba || !qcom_host->hba->dev) {
> +		pr_err("%s: invalid qcom_host %p or qcom_host->hba or
> qcom_host->hba->dev\n",
> +			__func__, qcom_host);
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	ufs_dev = qcom_host->hba->dev;
> +
> +	qcom_host->ice.vops  = ufs_qcom_ice_get_vops(ufs_dev);
> +	qcom_host->ice.pdev = ufs_qcom_ice_get_pdevice(ufs_dev);
> +
> +	if (qcom_host->ice.pdev == ERR_PTR(-EPROBE_DEFER)) {
> +		dev_err(ufs_dev, "%s: ICE device not probed yet\n",
> +			__func__);
> +		qcom_host->ice.pdev = NULL;
> +		qcom_host->ice.vops = NULL;
> +		err = -EPROBE_DEFER;
> +		goto out;
> +	}
> +
> +	if (!qcom_host->ice.pdev || !qcom_host->ice.vops) {
> +		dev_err(ufs_dev, "%s: invalid platform device %p or vops
> %p\n",
> +			__func__, qcom_host->ice.pdev,
> qcom_host->ice.vops);
> +		qcom_host->ice.pdev = NULL;
> +		qcom_host->ice.vops = NULL;
> +		err = -ENODEV;
> +		goto out;
> +	}
> +
> +	qcom_host->ice.state = UFS_QCOM_ICE_STATE_DISABLED;
> +
> +out:
> +	return err;
> +
> +}
> +
> +/**
> + * ufs_qcom_ice_init() - initializes the ICE-UFS interface and ICE device
> + * @qcom_host:	Pointer to a UFS QCom internal host structure.
> + *		qcom_host, qcom_host->hba and qcom_host->hba->dev should
> all
> + *		be valid pointers.
> + *
> + * Return: -EINVAL in-case of an error
> + *         0 otherwise
> + */
> +int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host)
> +{
> +	struct device *ufs_dev = qcom_host->hba->dev;
> +	int err = -EINVAL;
> +
> +	init_completion(&qcom_host->ice.async_done);
> +	err = qcom_host->ice.vops->init(qcom_host->ice.pdev,
> +				qcom_host,
> +				ufs_qcom_ice_success_cb,
> +				ufs_qcom_ice_error_cb);
> +	if (err) {
> +		dev_err(ufs_dev, "%s: ice init failed. err = %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
> +	if (!wait_for_completion_timeout(&qcom_host->ice.async_done,
> +
> msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
> +		dev_err(qcom_host->hba->dev,
> +			"%s: error. got timeout after %d ms\n",
> +			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
> +		err = -ETIMEDOUT;
> +		goto out;
> +	}
> +
> +	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
> +		dev_err(qcom_host->hba->dev,
> +			"%s: error. ice.state (%d) is not in active
> state\n",
> +			__func__, qcom_host->ice.state);
> +		err = -EINVAL;
> +	}
> +
> +out:
> +	return err;
> +}
> +
> +static inline bool ufs_qcom_is_data_cmd(char cmd_op, bool is_write)
> +{
> +	if (is_write) {
> +		if (cmd_op == WRITE_6 || cmd_op == WRITE_10 ||
> +		    cmd_op == WRITE_16)
> +			return true;
> +	} else {
> +		if (cmd_op == READ_6 || cmd_op == READ_10 ||
> +		    cmd_op == READ_16)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +/**
> + * ufs_qcom_ice_cfg() - configures UFS's ICE registers for an ICE
> transaction
> + * @qcom_host:	Pointer to a UFS QCom internal host structure.
> + *		qcom_host, qcom_host->hba and qcom_host->hba->dev should
> all
> + *		be valid pointers.
> + * @cmd:	Pointer to a valid scsi command. cmd->request should also
> be
> + *              a valid pointer.
> + *
> + * Return: -EINVAL in-case of an error
> + *         0 otherwise
> + */
> +int ufs_qcom_ice_cfg(struct ufs_qcom_host *qcom_host, struct scsi_cmnd
> *cmd)
> +{
> +	struct device *dev = qcom_host->hba->dev;
> +	int err = 0;
> +	struct ice_data_setting ice_set;
> +	unsigned int slot = 0;
> +	sector_t lba = 0;
> +	unsigned int ctrl_info_2_val = 0;
> +	unsigned int bypass = 0;
> +	struct request *req;
> +	char cmd_op;
> +
> +	if (!qcom_host->ice.pdev || !qcom_host->ice.vops) {
> +		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
> +		goto out;
> +	}
> +
> +	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
> +		dev_err(dev, "%s: ice state (%d) is not active\n",
> +			__func__, qcom_host->ice.state);
> +		return -EINVAL;
> +	}
> +
> +	req = cmd->request;
> +	if (req->bio)
> +		lba = req->bio->bi_sector;
> +
> +	slot = req->tag;
> +	if (slot < 0 || slot > qcom_host->hba->nutrs) {
> +		dev_err(dev, "%s: slot (%d) is out of boundaries
> (0...%d)\n",
> +			__func__, slot, qcom_host->hba->nutrs);
> +		return -EINVAL;
> +	}
> +
> +	memset(&ice_set, sizeof(ice_set), 0);
> +	if (qcom_host->ice.vops->config) {
> +		err = qcom_host->ice.vops->config(qcom_host->ice.pdev,
> +							req, &ice_set);
> +
> +		if (err) {
> +			dev_err(dev, "%s: error in ice_vops->config %d\n",
> +				__func__, err);
> +			goto out;
> +		}
> +	}
> +
> +	cmd_op = cmd->cmnd[0];
> +
> +#define UFS_QCOM_DIR_WRITE	true
> +#define UFS_QCOM_DIR_READ	false
> +	/* if non data command, bypass shall be enabled */
> +	if (!ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_WRITE) &&
> +	    !ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_READ))
> +		bypass = UFS_QCOM_ICE_ENABLE_BYPASS;
> +	/* if writing data command */
> +	else if (ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_WRITE))
> +		bypass = ice_set.encr_bypass ? UFS_QCOM_ICE_ENABLE_BYPASS
> :
> +
> UFS_QCOM_ICE_DISABLE_BYPASS;
> +	/* if reading data command */
> +	else if (ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_READ))
> +		bypass = ice_set.decr_bypass ? UFS_QCOM_ICE_ENABLE_BYPASS
> :
> +
> UFS_QCOM_ICE_DISABLE_BYPASS;
> +
> +	/* Configure ICE index */
> +	ctrl_info_2_val =
> +		(ice_set.crypto_data.key_index &
> +		 MASK_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX)
> +		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX;
> +
> +	/* Configure data unit size of transfer request */
> +	ctrl_info_2_val |=
> +		(UFS_QCOM_ICE_TR_DATA_UNIT_4_KB &
> +		 MASK_UFS_QCOM_ICE_CTRL_INFO_2_CDU)
> +		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_CDU;
> +
> +	/* Configure ICE bypass mode */
> +	ctrl_info_2_val |=
> +		(bypass & MASK_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS)
> +		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS;
> +
> +	ufshcd_writel(qcom_host->hba, lba,
> +		     (REG_UFS_QCOM_ICE_CTRL_INFO_1_n + 8 * slot));
> +
> +	ufshcd_writel(qcom_host->hba, ctrl_info_2_val,
> +		     (REG_UFS_QCOM_ICE_CTRL_INFO_2_n + 8 * slot));
> +
> +	/*
> +	 * Ensure UFS-ICE registers are being configured
> +	 * before next operation, otherwise UFS Host Controller might
> +	 * set get errors
> +	 */
> +	mb();
> +out:
> +	return err;
> +}
> +
> +/**
> + * ufs_qcom_ice_reset() - resets UFS-ICE interface and ICE device
> + * @qcom_host:	Pointer to a UFS QCom internal host structure.
> + *		qcom_host, qcom_host->hba and qcom_host->hba->dev should
> all
> + *		be valid pointers.
> + *
> + * Return: -EINVAL in-case of an error
> + *         0 otherwise
> + */
> +int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host)
> +{
> +	struct device *dev = qcom_host->hba->dev;
> +	int err = 0;
> +
> +	if (!qcom_host->ice.pdev) {
> +		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
> +		goto out;
> +	}
> +
> +	if (!qcom_host->ice.vops) {
> +		dev_err(dev, "%s: invalid ice_vops\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE)
> +		goto out;
> +
> +	init_completion(&qcom_host->ice.async_done);
> +
> +	if (qcom_host->ice.vops->reset) {
> +		err = qcom_host->ice.vops->reset(qcom_host->ice.pdev);
> +		if (err) {
> +			dev_err(dev, "%s: ice_vops->reset failed. err
> %d\n",
> +				__func__, err);
> +			goto out;
> +		}
> +	}
> +
> +	if (!wait_for_completion_timeout(&qcom_host->ice.async_done,
> +	     msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
> +		dev_err(dev,
> +			"%s: error. got timeout after %d ms\n",
> +			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
> +		err = -ETIMEDOUT;
> +	}
> +
> +out:
> +	return err;
> +}
> +
> +/**
> + * ufs_qcom_ice_resume() - resumes UFS-ICE interface and ICE device from
> power
> + * collapse
> + * @qcom_host:	Pointer to a UFS QCom internal host structure.
> + *		qcom_host, qcom_host->hba and qcom_host->hba->dev should
> all
> + *		be valid pointers.
> + *
> + * Return: -EINVAL in-case of an error
> + *         0 otherwise
> + */
> +int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host)
> +{
> +	struct device *dev = qcom_host->hba->dev;
> +	int err = 0;
> +
> +	if (!qcom_host->ice.pdev) {
> +		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
> +		goto out;
> +	}
> +
> +	if (qcom_host->ice.state !=
> +			UFS_QCOM_ICE_STATE_SUSPENDED) {
> +		goto out;
> +	}
> +
> +	if (!qcom_host->ice.vops) {
> +		dev_err(dev, "%s: invalid ice_vops\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	init_completion(&qcom_host->ice.async_done);
> +
> +	if (qcom_host->ice.vops->resume) {
> +		err = qcom_host->ice.vops->resume(qcom_host->ice.pdev);
> +		if (err) {
> +			dev_err(dev, "%s: ice_vops->resume failed. err
> %d\n",
> +				__func__, err);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (!wait_for_completion_timeout(&qcom_host->ice.async_done,
> +
> msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
> +		dev_err(dev,
> +			"%s: error. got timeout after %d ms\n",
> +			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
> +		err = -ETIMEDOUT;
> +		goto out;
> +	}
> +
> +	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE)
> +		err = -EINVAL;
> +out:
> +	return err;
> +}
> +
> +/**
> + * ufs_qcom_ice_suspend() - suspends UFS-ICE interface and ICE device
> + * @qcom_host:	Pointer to a UFS QCom internal host structure.
> + *		qcom_host, qcom_host->hba and qcom_host->hba->dev should
> all
> + *		be valid pointers.
> + *
> + * Return: -EINVAL in-case of an error
> + *         0 otherwise
> + */
> +int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host)
> +{
> +	struct device *dev = qcom_host->hba->dev;
> +	int err = 0;
> +
> +	if (!qcom_host->ice.pdev) {
> +		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
> +		goto out;
> +	}
> +
> +	if (qcom_host->ice.vops->suspend) {
> +		err = qcom_host->ice.vops->suspend(qcom_host->ice.pdev);
> +		if (err) {
> +			dev_err(qcom_host->hba->dev,
> +				"%s: ice_vops->suspend failed. err %d\n",
> +				__func__, err);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_ACTIVE) {
> +		qcom_host->ice.state = UFS_QCOM_ICE_STATE_SUSPENDED;
> +	} else if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_DISABLED) {
> +		dev_err(qcom_host->hba->dev,
> +				"%s: ice state is invalid: disabled\n",
> +				__func__);
> +		err = -EINVAL;
> +	}
> +
> +out:
> +	return err;
> +}
> +
> +/**
> + * ufs_qcom_ice_get_status() - returns the status of an ICE transaction
> + * @qcom_host:	Pointer to a UFS QCom internal host structure.
> + *		qcom_host, qcom_host->hba and qcom_host->hba->dev should
> all
> + *		be valid pointers.
> + * @ice_status:	Pointer to a valid output parameter.
> + *		< 0 in case of ICE transaction failure.
> + *		0 otherwise.
> + *
> + * Return: -EINVAL in-case of an error
> + *         0 otherwise
> + */
> +int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host, int
> *ice_status)
> +{
> +	struct device *dev = NULL;
> +	int err = 0;
> +	int stat = -EINVAL;
> +
> +	ice_status = 0;
> +
> +	dev = qcom_host->hba->dev;
> +	if (!dev) {
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (!qcom_host->ice.pdev) {
> +		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
> +		goto out;
> +	}
> +
> +	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (!qcom_host->ice.vops) {
> +		dev_err(dev, "%s: invalid ice_vops\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (qcom_host->ice.vops->status) {
> +		stat = qcom_host->ice.vops->status(qcom_host->ice.pdev);
> +		if (stat < 0) {
> +			dev_err(dev, "%s: ice_vops->status failed. stat
> %d\n",
> +				__func__, stat);
> +			err = -EINVAL;
> +			goto out;
> +		}
> +
> +		*ice_status = stat;
> +	}
> +
> +out:
> +	return err;
> +}
> diff --git a/drivers/scsi/ufs/ufs-qcom-ice.h
> b/drivers/scsi/ufs/ufs-qcom-ice.h
> new file mode 100644
> index 0000000..5ccbf5f
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs-qcom-ice.h
> @@ -0,0 +1,113 @@
> +/* Copyright (c) 2014-2015, The Linux Foundation. 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 version 2 and
> + * only version 2 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.
> + *
> + */
> +
> +#ifndef _UFS_QCOM_ICE_H_
> +#define _UFS_QCOM_ICE_H_
> +
> +#include <scsi/scsi_cmnd.h>
> +
> +#include "ufs-qcom.h"
> +
> +/*
> + * UFS host controller ICE registers. There are n [0..31]
> + * of each of these registers
> + */
> +enum {
> +	REG_UFS_QCOM_ICE_CTRL_INFO_1_n           = 0x2204,
> +	REG_UFS_QCOM_ICE_CTRL_INFO_2_n           = 0x2208,
> +};
> +
> +/* UFS QCOM ICE CTRL Info 2 register offset */
> +enum {
> +	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS     = 0,
> +	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX  = 0x1,
> +	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_CDU        = 0x6,
> +};
> +
> +/* UFS QCOM ICE CTRL Info 2 register masks */
> +enum {
> +	MASK_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS     = 0x1,
> +	MASK_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX  = 0x1F,
> +	MASK_UFS_QCOM_ICE_CTRL_INFO_2_CDU        = 0x8,
> +};
> +
> +/* UFS QCOM ICE encryption/decryption bypass state */
> +enum {
> +	UFS_QCOM_ICE_DISABLE_BYPASS  = 0,
> +	UFS_QCOM_ICE_ENABLE_BYPASS = 1,
> +};
> +
> +/* UFS QCOM ICE Crypto Data Unit of target DUN of Transfer Request */
> +enum {
> +	UFS_QCOM_ICE_TR_DATA_UNIT_512_B          = 0,
> +	UFS_QCOM_ICE_TR_DATA_UNIT_1_KB           = 1,
> +	UFS_QCOM_ICE_TR_DATA_UNIT_2_KB           = 2,
> +	UFS_QCOM_ICE_TR_DATA_UNIT_4_KB           = 3,
> +	UFS_QCOM_ICE_TR_DATA_UNIT_8_KB           = 4,
> +	UFS_QCOM_ICE_TR_DATA_UNIT_16_KB          = 5,
> +	UFS_QCOM_ICE_TR_DATA_UNIT_32_KB          = 6,
> +};
> +
> +/* UFS QCOM ICE internal state */
> +enum {
> +	UFS_QCOM_ICE_STATE_DISABLED   = 0,
> +	UFS_QCOM_ICE_STATE_ACTIVE     = 1,
> +	UFS_QCOM_ICE_STATE_SUSPENDED  = 2,
> +};
> +
> +#ifdef CONFIG_SCSI_UFS_QCOM_ICE
> +int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host);
> +int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host);
> +int ufs_qcom_ice_cfg(struct ufs_qcom_host *qcom_host, struct scsi_cmnd
> *cmd);
> +int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host);
> +int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host);
> +int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host);
> +int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host, int
> *ice_status);
> +#else
> +inline int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host)
> +{
> +	if (qcom_host) {
> +		qcom_host->ice.pdev = NULL;
> +		qcom_host->ice.vops = NULL;
> +	}
> +	return -ENODEV;
> +}
> +inline int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host)
> +{
> +	return 0;
> +}
> +inline int ufs_qcom_ice_cfg(struct ufs_qcom_host *qcom_host,
> +			    struct scsi_cmnd *cmd)
> +{
> +	return 0;
> +}
> +inline int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host)
> +{
> +	return 0;
> +}
> +inline int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host)
> +{
> +	return 0;
> +}
> +inline int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host)
> +{
> +	return 0;
> +}
> +inline int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host,
> +				   int *ice_status)
> +{
> +	return 0;
> +}
> +#endif /* CONFIG_SCSI_UFS_QCOM_ICE */
> +
> +#endif /* UFS_QCOM_ICE_H_ */
> diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
> index 9217af9..fd01255 100644
> --- a/drivers/scsi/ufs/ufs-qcom.c
> +++ b/drivers/scsi/ufs/ufs-qcom.c
> @@ -22,6 +22,7 @@
>  #include "unipro.h"
>  #include "ufs-qcom.h"
>  #include "ufshci.h"
> +#include "ufs-qcom-ice.h"
>
>  static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS];
>
> @@ -294,6 +295,13 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba
> *hba, bool status)
>  		/* check if UFS PHY moved from DISABLED to HIBERN8 */
>  		err = ufs_qcom_check_hibern8(hba);
>  		ufs_qcom_enable_hw_clk_gating(hba);
> +		if (!err) {
> +			err = ufs_qcom_ice_reset(host);
> +			if (err)
> +				dev_err(hba->dev,
> +					"%s: ufs_qcom_ice_reset() failed
> %d\n",
> +					__func__, err);
> +		}
>
>  		break;
>  	default:
> @@ -453,6 +461,10 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum
> ufs_pm_op pm_op)
>  		 */
>  		ufs_qcom_disable_lane_clks(host);
>  		phy_power_off(phy);
> +		ret = ufs_qcom_ice_suspend(host);
> +		if (ret)
> +			dev_err(hba->dev, "%s: failed ufs_qcom_ice_suspend
> %d\n",
> +					__func__, ret);
>
>  		/* Assert PHY soft reset */
>  		ufs_qcom_assert_reset(hba);
> @@ -463,8 +475,10 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum
> ufs_pm_op pm_op)
>  	 * If UniPro link is not active, PHY ref_clk, main PHY analog
> power
>  	 * rail and low noise analog power rail for PLL can be switched
> off.
>  	 */
> -	if (!ufs_qcom_is_link_active(hba))
> +	if (!ufs_qcom_is_link_active(hba)) {
>  		phy_power_off(phy);
> +		ufs_qcom_ice_suspend(host);
> +	}
>
>  out:
>  	return ret;
> @@ -483,6 +497,13 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum
> ufs_pm_op pm_op)
>  		goto out;
>  	}
>
> +	err = ufs_qcom_ice_resume(host);
> +	if (err) {
> +		dev_err(hba->dev, "%s: ufs_qcom_ice_resume failed, err =
> %d\n",
> +			__func__, err);
> +		goto out;
> +	}
> +
>  	hba->is_sys_suspended = false;
>
>  out:
> @@ -917,6 +938,30 @@ static int ufs_qcom_init(struct ufs_hba *hba)
>  	host->hba = hba;
>  	hba->priv = (void *)host;
>
> +	err = ufs_qcom_ice_get_dev(host);
> +	if (err == -EPROBE_DEFER) {
> +		/*
> +		 * UFS driver might be probed before ICE driver does.
> +		 * In that case we would like to return EPROBE_DEFER code
> +		 * in order to delay its probing.
> +		 */
> +		dev_err(dev, "%s: required ICE device not probed yet err =
> %d\n",
> +			__func__, err);
> +		goto out_host_free;
> +
> +	} else if (err == -ENODEV) {
> +		/*
> +		 * ICE device is not enabled in DTS file. No need for
> further
> +		 * initialization of ICE driver.
> +		 */
> +		dev_warn(dev, "%s: ICE device is not enabled",
> +			__func__);
> +	} else if (err) {
> +		dev_err(dev, "%s: ufs_qcom_ice_get_dev failed %d\n",
> +			__func__, err);
> +		goto out_host_free;
> +	}
> +
>  	host->generic_phy = devm_phy_get(dev, "ufsphy");
>
>  	if (IS_ERR(host->generic_phy)) {
> @@ -944,6 +989,15 @@ static int ufs_qcom_init(struct ufs_hba *hba)
>  	hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
>
>  	ufs_qcom_setup_clocks(hba, true);
> +	if (host->ice.pdev) {
> +		err = ufs_qcom_ice_init(host);
> +		if (err) {
> +			dev_err(dev, "%s: ICE driver initialization failed
> (%d)\n",
> +				__func__, err);
> +			device_remove_file(dev,
> &host->bus_vote.max_bus_bw);
> +			goto out_disable_phy;
> +		}
> +	}
>
>  	if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
>  		ufs_qcom_hosts[hba->dev->id] = host;
> diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
> index 9a6febd..8340f2c 100644
> --- a/drivers/scsi/ufs/ufs-qcom.h
> +++ b/drivers/scsi/ufs/ufs-qcom.h
> @@ -151,6 +151,30 @@ struct ufs_qcom_bus_vote {
>  	struct device_attribute max_bus_bw;
>  };
>
> +/**
> + * struct ufs_qcom_ice_data - ICE related information
> + * @vops:	pointer to variant operations of ICE
> + * @async_done:	completion for supporting ICE's driver
> asynchronous nature
> + * @pdev:	pointer to the proper ICE platform device
> + * @state:      UFS-ICE interface's internal state (see
> + *       ufs-qcom-ice.h for possible internal states)
> + * @quirks:     UFS-ICE interface related quirks
> + */
> +struct ufs_qcom_ice_data {
> +	struct qcom_ice_variant_ops *vops;
> +	struct completion async_done;
> +	struct platform_device *pdev;
> +	int state;
> +
> +	/*
> +	 * If UFS host controller should handle cryptographic engine's
> +	 * errors, enables this quirk.
> +	 */
> +	#define UFS_QCOM_ICE_QUIRK_HANDLE_CRYPTO_ENGINE_ERRORS	UFS_BIT(0)
> +
> +	u16 quirks;
> +};
> +
>  struct ufs_qcom_host {
>  	struct phy *generic_phy;
>  	struct ufs_hba *hba;
> @@ -161,6 +185,7 @@ struct ufs_qcom_host {
>  	struct clk *rx_l1_sync_clk;
>  	struct clk *tx_l1_sync_clk;
>  	bool is_lane_clks_enabled;
> +	struct ufs_qcom_ice_data ice;
>  };
>
>  #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba)
> --
> 1.8.5.2
>
> --
> QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
> of Code Aurora Forum, hosted by The Linux Foundation
> --
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paul Bolle Jan. 23, 2015, 8:48 a.m. UTC | #2
Yaniv,

On Thu, 2015-01-15 at 16:32 +0200, Yaniv Gardi wrote:
> From: Yaniv Gardi <ygardi@qti.qualcomm.com>
> 
> In-order to enhance storage encryption performance,
> an Inline Cryptographic Engine is introduced to UFS.
> This patch adds in-line encryption capabilities to the UFS
> driver.
> 
> Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>

This patch became commit 8805ccd069b7 ("ufs-qcom-ice: add Inline Crypto
Engine (ICE) support for UFS") in today's linux-next (ie,
next-20150123). I noticed because a script I use to check linux-next
spotted a problem with it.

> ---
>  drivers/scsi/ufs/Kconfig        |  12 +
>  drivers/scsi/ufs/Makefile       |   1 +
>  drivers/scsi/ufs/ufs-qcom-ice.c | 520 ++++++++++++++++++++++++++++++++++++++++
>  drivers/scsi/ufs/ufs-qcom-ice.h | 113 +++++++++
>  drivers/scsi/ufs/ufs-qcom.c     |  56 ++++-
>  drivers/scsi/ufs/ufs-qcom.h     |  25 ++
>  6 files changed, 726 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.c
>  create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.h
> 
> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
> index 8a1f4b3..ecf34ed 100644
> --- a/drivers/scsi/ufs/Kconfig
> +++ b/drivers/scsi/ufs/Kconfig
> @@ -83,3 +83,15 @@ config SCSI_UFS_QCOM
>  
>  	  Select this if you have UFS controller on QCOM chipset.
>  	  If unsure, say N.
> +
> +config SCSI_UFS_QCOM_ICE
> +	bool "QCOM specific hooks to Inline Crypto Engine for UFS driver"
> +	depends on SCSI_UFS_QCOM && CRYPTO_DEV_QCOM_ICE

There's currently no Kconfig symbol CRYPTO_DEV_QCOM_ICE in linux-next.
So SCSI_UFS_QCOM_ICE can not be set and these "in-line encryption
capabilities" can not be enabled.

> +	help
> +	  This selects the QCOM specific additions to support Inline Crypto
> +	  Engine (ICE).
> +	  ICE accelerates the crypto operations and maintains the high UFS
> +	  performance.
> +
> +	  Select this if you have ICE supported for UFS on QCOM chipset.
> +	  If unsure, say N.
> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
> index 8303bcc..31adca5 100644
> --- a/drivers/scsi/ufs/Makefile
> +++ b/drivers/scsi/ufs/Makefile
> @@ -1,5 +1,6 @@
>  # UFSHCD makefile
>  obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
> +obj-$(CONFIG_SCSI_UFS_QCOM_ICE) += ufs-qcom-ice.o
>  obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
>  obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
>  obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
> diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c
> new file mode 100644
> index 0000000..9202b73
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs-qcom-ice.c
> @@ -0,0 +1,520 @@
> +/* Copyright (c) 2014-2015, The Linux Foundation. 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 version 2 and
> + * only version 2 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.
> + */
> +
> +#include <linux/of.h>
> +#include <linux/async.h>
> +#include <linux/blkdev.h>
> +#include <crypto/ice.h>

This header is not included in linux-next so manually building
ufs-qcom-ice.o isn't possible either.

I assume a series to add CRYPTO_DEV_QCOM_ICE and crypto/ice.h (and
whatever else is needed to build this) is queued somewhere. Is that
correct?


Paul Bolle

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Yaniv Gardi Feb. 2, 2015, 2:36 p.m. UTC | #3
Paul,

we have decided to revert the ICE change that support UFS.
a change already uploaded:
look for subject:

[PATCH v1] Revert "scsi: ufs-qcom-ice: add Inline Crypto Engine (ICE)
support for UFS"

thanks,
Yaniv

> Yaniv,
>
> On Thu, 2015-01-15 at 16:32 +0200, Yaniv Gardi wrote:
>> From: Yaniv Gardi <ygardi@qti.qualcomm.com>
>>
>> In-order to enhance storage encryption performance,
>> an Inline Cryptographic Engine is introduced to UFS.
>> This patch adds in-line encryption capabilities to the UFS
>> driver.
>>
>> Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
>
> This patch became commit 8805ccd069b7 ("ufs-qcom-ice: add Inline Crypto
> Engine (ICE) support for UFS") in today's linux-next (ie,
> next-20150123). I noticed because a script I use to check linux-next
> spotted a problem with it.
>
>> ---
>>  drivers/scsi/ufs/Kconfig        |  12 +
>>  drivers/scsi/ufs/Makefile       |   1 +
>>  drivers/scsi/ufs/ufs-qcom-ice.c | 520
>> ++++++++++++++++++++++++++++++++++++++++
>>  drivers/scsi/ufs/ufs-qcom-ice.h | 113 +++++++++
>>  drivers/scsi/ufs/ufs-qcom.c     |  56 ++++-
>>  drivers/scsi/ufs/ufs-qcom.h     |  25 ++
>>  6 files changed, 726 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.c
>>  create mode 100644 drivers/scsi/ufs/ufs-qcom-ice.h
>>
>> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
>> index 8a1f4b3..ecf34ed 100644
>> --- a/drivers/scsi/ufs/Kconfig
>> +++ b/drivers/scsi/ufs/Kconfig
>> @@ -83,3 +83,15 @@ config SCSI_UFS_QCOM
>>
>>  	  Select this if you have UFS controller on QCOM chipset.
>>  	  If unsure, say N.
>> +
>> +config SCSI_UFS_QCOM_ICE
>> +	bool "QCOM specific hooks to Inline Crypto Engine for UFS driver"
>> +	depends on SCSI_UFS_QCOM && CRYPTO_DEV_QCOM_ICE
>
> There's currently no Kconfig symbol CRYPTO_DEV_QCOM_ICE in linux-next.
> So SCSI_UFS_QCOM_ICE can not be set and these "in-line encryption
> capabilities" can not be enabled.
>
>> +	help
>> +	  This selects the QCOM specific additions to support Inline Crypto
>> +	  Engine (ICE).
>> +	  ICE accelerates the crypto operations and maintains the high UFS
>> +	  performance.
>> +
>> +	  Select this if you have ICE supported for UFS on QCOM chipset.
>> +	  If unsure, say N.
>> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
>> index 8303bcc..31adca5 100644
>> --- a/drivers/scsi/ufs/Makefile
>> +++ b/drivers/scsi/ufs/Makefile
>> @@ -1,5 +1,6 @@
>>  # UFSHCD makefile
>>  obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
>> +obj-$(CONFIG_SCSI_UFS_QCOM_ICE) += ufs-qcom-ice.o
>>  obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
>>  obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
>>  obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
>> diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c
>> b/drivers/scsi/ufs/ufs-qcom-ice.c
>> new file mode 100644
>> index 0000000..9202b73
>> --- /dev/null
>> +++ b/drivers/scsi/ufs/ufs-qcom-ice.c
>> @@ -0,0 +1,520 @@
>> +/* Copyright (c) 2014-2015, The Linux Foundation. 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 version 2 and
>> + * only version 2 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.
>> + */
>> +
>> +#include <linux/of.h>
>> +#include <linux/async.h>
>> +#include <linux/blkdev.h>
>> +#include <crypto/ice.h>
>
> This header is not included in linux-next so manually building
> ufs-qcom-ice.o isn't possible either.
>
> I assume a series to add CRYPTO_DEV_QCOM_ICE and crypto/ice.h (and
> whatever else is needed to build this) is queued somewhere. Is that
> correct?
>
>
> Paul Bolle
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index 8a1f4b3..ecf34ed 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -83,3 +83,15 @@  config SCSI_UFS_QCOM
 
 	  Select this if you have UFS controller on QCOM chipset.
 	  If unsure, say N.
+
+config SCSI_UFS_QCOM_ICE
+	bool "QCOM specific hooks to Inline Crypto Engine for UFS driver"
+	depends on SCSI_UFS_QCOM && CRYPTO_DEV_QCOM_ICE
+	help
+	  This selects the QCOM specific additions to support Inline Crypto
+	  Engine (ICE).
+	  ICE accelerates the crypto operations and maintains the high UFS
+	  performance.
+
+	  Select this if you have ICE supported for UFS on QCOM chipset.
+	  If unsure, say N.
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 8303bcc..31adca5 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -1,5 +1,6 @@ 
 # UFSHCD makefile
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
+obj-$(CONFIG_SCSI_UFS_QCOM_ICE) += ufs-qcom-ice.o
 obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c
new file mode 100644
index 0000000..9202b73
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-qcom-ice.c
@@ -0,0 +1,520 @@ 
+/* Copyright (c) 2014-2015, The Linux Foundation. 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 version 2 and
+ * only version 2 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.
+ */
+
+#include <linux/of.h>
+#include <linux/async.h>
+#include <linux/blkdev.h>
+#include <crypto/ice.h>
+
+#include "ufs-qcom-ice.h"
+#include "ufshcd.h"
+
+#define UFS_QCOM_CRYPTO_LABEL "ufs-qcom-crypto"
+/* Timeout waiting for ICE initialization, that requires TZ access */
+#define UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS 500
+
+static void ufs_qcom_ice_success_cb(void *host_ctrl,
+				enum ice_event_completion evt)
+{
+	struct ufs_qcom_host *qcom_host = (struct ufs_qcom_host *)host_ctrl;
+
+	if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_DISABLED &&
+	    evt == ICE_INIT_COMPLETION)
+		qcom_host->ice.state = UFS_QCOM_ICE_STATE_ACTIVE;
+	 else if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_SUSPENDED &&
+		   evt == ICE_RESUME_COMPLETION)
+		qcom_host->ice.state = UFS_QCOM_ICE_STATE_ACTIVE;
+
+	complete(&qcom_host->ice.async_done);
+}
+
+static void ufs_qcom_ice_error_cb(void *host_ctrl, enum ice_error_code evt)
+{
+	struct ufs_qcom_host *qcom_host = (struct ufs_qcom_host *)host_ctrl;
+
+	dev_err(qcom_host->hba->dev, "%s: Error in ice operation %d",
+		__func__, evt);
+
+	if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_ACTIVE)
+		qcom_host->ice.state = UFS_QCOM_ICE_STATE_DISABLED;
+
+	complete(&qcom_host->ice.async_done);
+}
+
+static struct platform_device *ufs_qcom_ice_get_pdevice(struct device *ufs_dev)
+{
+	struct device_node *node;
+	struct platform_device *ice_pdev = NULL;
+
+	node = of_parse_phandle(ufs_dev->of_node, UFS_QCOM_CRYPTO_LABEL, 0);
+
+	if (!node) {
+		dev_err(ufs_dev, "%s: ufs-qcom-crypto property not specified\n",
+			__func__);
+		goto out;
+	}
+
+	ice_pdev = qcom_ice_get_pdevice(node);
+out:
+	return ice_pdev;
+}
+
+static
+struct qcom_ice_variant_ops *ufs_qcom_ice_get_vops(struct device *ufs_dev)
+{
+	struct qcom_ice_variant_ops *ice_vops = NULL;
+	struct device_node *node;
+
+	node = of_parse_phandle(ufs_dev->of_node, UFS_QCOM_CRYPTO_LABEL, 0);
+
+	if (!node) {
+		dev_err(ufs_dev, "%s: ufs-qcom-crypto property not specified\n",
+			__func__);
+		goto out;
+	}
+
+	ice_vops = qcom_ice_get_variant_ops(node);
+
+	if (!ice_vops)
+		dev_err(ufs_dev, "%s: invalid ice_vops\n", __func__);
+
+	of_node_put(node);
+out:
+	return ice_vops;
+}
+
+/**
+ * ufs_qcom_ice_get_dev() - sets pointers to ICE data structs in UFS QCom host
+ * @qcom_host:	Pointer to a UFS QCom internal host structure.
+ *
+ * Sets ICE platform device pointer and ICE vops structure
+ * corresponding to the current UFS device.
+ *
+ * Return: -EINVAL in-case of invalid input parameters:
+ *  qcom_host, qcom_host->hba or qcom_host->hba->dev
+ *         -ENODEV in-case ICE device is not required
+ *         -EPROBE_DEFER in-case ICE is required and hasn't been probed yet
+ *         0 otherwise
+ */
+int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host)
+{
+	struct device *ufs_dev;
+	int err = 0;
+
+	if (!qcom_host || !qcom_host->hba || !qcom_host->hba->dev) {
+		pr_err("%s: invalid qcom_host %p or qcom_host->hba or qcom_host->hba->dev\n",
+			__func__, qcom_host);
+		err = -EINVAL;
+		goto out;
+	}
+
+	ufs_dev = qcom_host->hba->dev;
+
+	qcom_host->ice.vops  = ufs_qcom_ice_get_vops(ufs_dev);
+	qcom_host->ice.pdev = ufs_qcom_ice_get_pdevice(ufs_dev);
+
+	if (qcom_host->ice.pdev == ERR_PTR(-EPROBE_DEFER)) {
+		dev_err(ufs_dev, "%s: ICE device not probed yet\n",
+			__func__);
+		qcom_host->ice.pdev = NULL;
+		qcom_host->ice.vops = NULL;
+		err = -EPROBE_DEFER;
+		goto out;
+	}
+
+	if (!qcom_host->ice.pdev || !qcom_host->ice.vops) {
+		dev_err(ufs_dev, "%s: invalid platform device %p or vops %p\n",
+			__func__, qcom_host->ice.pdev, qcom_host->ice.vops);
+		qcom_host->ice.pdev = NULL;
+		qcom_host->ice.vops = NULL;
+		err = -ENODEV;
+		goto out;
+	}
+
+	qcom_host->ice.state = UFS_QCOM_ICE_STATE_DISABLED;
+
+out:
+	return err;
+
+}
+
+/**
+ * ufs_qcom_ice_init() - initializes the ICE-UFS interface and ICE device
+ * @qcom_host:	Pointer to a UFS QCom internal host structure.
+ *		qcom_host, qcom_host->hba and qcom_host->hba->dev should all
+ *		be valid pointers.
+ *
+ * Return: -EINVAL in-case of an error
+ *         0 otherwise
+ */
+int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host)
+{
+	struct device *ufs_dev = qcom_host->hba->dev;
+	int err = -EINVAL;
+
+	init_completion(&qcom_host->ice.async_done);
+	err = qcom_host->ice.vops->init(qcom_host->ice.pdev,
+				qcom_host,
+				ufs_qcom_ice_success_cb,
+				ufs_qcom_ice_error_cb);
+	if (err) {
+		dev_err(ufs_dev, "%s: ice init failed. err = %d\n",
+			__func__, err);
+		goto out;
+	}
+
+	if (!wait_for_completion_timeout(&qcom_host->ice.async_done,
+			msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
+		dev_err(qcom_host->hba->dev,
+			"%s: error. got timeout after %d ms\n",
+			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
+		err = -ETIMEDOUT;
+		goto out;
+	}
+
+	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
+		dev_err(qcom_host->hba->dev,
+			"%s: error. ice.state (%d) is not in active state\n",
+			__func__, qcom_host->ice.state);
+		err = -EINVAL;
+	}
+
+out:
+	return err;
+}
+
+static inline bool ufs_qcom_is_data_cmd(char cmd_op, bool is_write)
+{
+	if (is_write) {
+		if (cmd_op == WRITE_6 || cmd_op == WRITE_10 ||
+		    cmd_op == WRITE_16)
+			return true;
+	} else {
+		if (cmd_op == READ_6 || cmd_op == READ_10 ||
+		    cmd_op == READ_16)
+			return true;
+	}
+
+	return false;
+}
+
+/**
+ * ufs_qcom_ice_cfg() - configures UFS's ICE registers for an ICE transaction
+ * @qcom_host:	Pointer to a UFS QCom internal host structure.
+ *		qcom_host, qcom_host->hba and qcom_host->hba->dev should all
+ *		be valid pointers.
+ * @cmd:	Pointer to a valid scsi command. cmd->request should also be
+ *              a valid pointer.
+ *
+ * Return: -EINVAL in-case of an error
+ *         0 otherwise
+ */
+int ufs_qcom_ice_cfg(struct ufs_qcom_host *qcom_host, struct scsi_cmnd *cmd)
+{
+	struct device *dev = qcom_host->hba->dev;
+	int err = 0;
+	struct ice_data_setting ice_set;
+	unsigned int slot = 0;
+	sector_t lba = 0;
+	unsigned int ctrl_info_2_val = 0;
+	unsigned int bypass = 0;
+	struct request *req;
+	char cmd_op;
+
+	if (!qcom_host->ice.pdev || !qcom_host->ice.vops) {
+		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
+		goto out;
+	}
+
+	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
+		dev_err(dev, "%s: ice state (%d) is not active\n",
+			__func__, qcom_host->ice.state);
+		return -EINVAL;
+	}
+
+	req = cmd->request;
+	if (req->bio)
+		lba = req->bio->bi_sector;
+
+	slot = req->tag;
+	if (slot < 0 || slot > qcom_host->hba->nutrs) {
+		dev_err(dev, "%s: slot (%d) is out of boundaries (0...%d)\n",
+			__func__, slot, qcom_host->hba->nutrs);
+		return -EINVAL;
+	}
+
+	memset(&ice_set, sizeof(ice_set), 0);
+	if (qcom_host->ice.vops->config) {
+		err = qcom_host->ice.vops->config(qcom_host->ice.pdev,
+							req, &ice_set);
+
+		if (err) {
+			dev_err(dev, "%s: error in ice_vops->config %d\n",
+				__func__, err);
+			goto out;
+		}
+	}
+
+	cmd_op = cmd->cmnd[0];
+
+#define UFS_QCOM_DIR_WRITE	true
+#define UFS_QCOM_DIR_READ	false
+	/* if non data command, bypass shall be enabled */
+	if (!ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_WRITE) &&
+	    !ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_READ))
+		bypass = UFS_QCOM_ICE_ENABLE_BYPASS;
+	/* if writing data command */
+	else if (ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_WRITE))
+		bypass = ice_set.encr_bypass ? UFS_QCOM_ICE_ENABLE_BYPASS :
+						UFS_QCOM_ICE_DISABLE_BYPASS;
+	/* if reading data command */
+	else if (ufs_qcom_is_data_cmd(cmd_op, UFS_QCOM_DIR_READ))
+		bypass = ice_set.decr_bypass ? UFS_QCOM_ICE_ENABLE_BYPASS :
+						UFS_QCOM_ICE_DISABLE_BYPASS;
+
+	/* Configure ICE index */
+	ctrl_info_2_val =
+		(ice_set.crypto_data.key_index &
+		 MASK_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX)
+		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX;
+
+	/* Configure data unit size of transfer request */
+	ctrl_info_2_val |=
+		(UFS_QCOM_ICE_TR_DATA_UNIT_4_KB &
+		 MASK_UFS_QCOM_ICE_CTRL_INFO_2_CDU)
+		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_CDU;
+
+	/* Configure ICE bypass mode */
+	ctrl_info_2_val |=
+		(bypass & MASK_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS)
+		 << OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS;
+
+	ufshcd_writel(qcom_host->hba, lba,
+		     (REG_UFS_QCOM_ICE_CTRL_INFO_1_n + 8 * slot));
+
+	ufshcd_writel(qcom_host->hba, ctrl_info_2_val,
+		     (REG_UFS_QCOM_ICE_CTRL_INFO_2_n + 8 * slot));
+
+	/*
+	 * Ensure UFS-ICE registers are being configured
+	 * before next operation, otherwise UFS Host Controller might
+	 * set get errors
+	 */
+	mb();
+out:
+	return err;
+}
+
+/**
+ * ufs_qcom_ice_reset() - resets UFS-ICE interface and ICE device
+ * @qcom_host:	Pointer to a UFS QCom internal host structure.
+ *		qcom_host, qcom_host->hba and qcom_host->hba->dev should all
+ *		be valid pointers.
+ *
+ * Return: -EINVAL in-case of an error
+ *         0 otherwise
+ */
+int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host)
+{
+	struct device *dev = qcom_host->hba->dev;
+	int err = 0;
+
+	if (!qcom_host->ice.pdev) {
+		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
+		goto out;
+	}
+
+	if (!qcom_host->ice.vops) {
+		dev_err(dev, "%s: invalid ice_vops\n", __func__);
+		return -EINVAL;
+	}
+
+	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE)
+		goto out;
+
+	init_completion(&qcom_host->ice.async_done);
+
+	if (qcom_host->ice.vops->reset) {
+		err = qcom_host->ice.vops->reset(qcom_host->ice.pdev);
+		if (err) {
+			dev_err(dev, "%s: ice_vops->reset failed. err %d\n",
+				__func__, err);
+			goto out;
+		}
+	}
+
+	if (!wait_for_completion_timeout(&qcom_host->ice.async_done,
+	     msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
+		dev_err(dev,
+			"%s: error. got timeout after %d ms\n",
+			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
+		err = -ETIMEDOUT;
+	}
+
+out:
+	return err;
+}
+
+/**
+ * ufs_qcom_ice_resume() - resumes UFS-ICE interface and ICE device from power
+ * collapse
+ * @qcom_host:	Pointer to a UFS QCom internal host structure.
+ *		qcom_host, qcom_host->hba and qcom_host->hba->dev should all
+ *		be valid pointers.
+ *
+ * Return: -EINVAL in-case of an error
+ *         0 otherwise
+ */
+int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host)
+{
+	struct device *dev = qcom_host->hba->dev;
+	int err = 0;
+
+	if (!qcom_host->ice.pdev) {
+		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
+		goto out;
+	}
+
+	if (qcom_host->ice.state !=
+			UFS_QCOM_ICE_STATE_SUSPENDED) {
+		goto out;
+	}
+
+	if (!qcom_host->ice.vops) {
+		dev_err(dev, "%s: invalid ice_vops\n", __func__);
+		return -EINVAL;
+	}
+
+	init_completion(&qcom_host->ice.async_done);
+
+	if (qcom_host->ice.vops->resume) {
+		err = qcom_host->ice.vops->resume(qcom_host->ice.pdev);
+		if (err) {
+			dev_err(dev, "%s: ice_vops->resume failed. err %d\n",
+				__func__, err);
+			return -EINVAL;
+		}
+	}
+
+	if (!wait_for_completion_timeout(&qcom_host->ice.async_done,
+			msecs_to_jiffies(UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS))) {
+		dev_err(dev,
+			"%s: error. got timeout after %d ms\n",
+			__func__, UFS_QCOM_ICE_COMPLETION_TIMEOUT_MS);
+		err = -ETIMEDOUT;
+		goto out;
+	}
+
+	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE)
+		err = -EINVAL;
+out:
+	return err;
+}
+
+/**
+ * ufs_qcom_ice_suspend() - suspends UFS-ICE interface and ICE device
+ * @qcom_host:	Pointer to a UFS QCom internal host structure.
+ *		qcom_host, qcom_host->hba and qcom_host->hba->dev should all
+ *		be valid pointers.
+ *
+ * Return: -EINVAL in-case of an error
+ *         0 otherwise
+ */
+int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host)
+{
+	struct device *dev = qcom_host->hba->dev;
+	int err = 0;
+
+	if (!qcom_host->ice.pdev) {
+		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
+		goto out;
+	}
+
+	if (qcom_host->ice.vops->suspend) {
+		err = qcom_host->ice.vops->suspend(qcom_host->ice.pdev);
+		if (err) {
+			dev_err(qcom_host->hba->dev,
+				"%s: ice_vops->suspend failed. err %d\n",
+				__func__, err);
+			return -EINVAL;
+		}
+	}
+
+	if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_ACTIVE) {
+		qcom_host->ice.state = UFS_QCOM_ICE_STATE_SUSPENDED;
+	} else if (qcom_host->ice.state == UFS_QCOM_ICE_STATE_DISABLED) {
+		dev_err(qcom_host->hba->dev,
+				"%s: ice state is invalid: disabled\n",
+				__func__);
+		err = -EINVAL;
+	}
+
+out:
+	return err;
+}
+
+/**
+ * ufs_qcom_ice_get_status() - returns the status of an ICE transaction
+ * @qcom_host:	Pointer to a UFS QCom internal host structure.
+ *		qcom_host, qcom_host->hba and qcom_host->hba->dev should all
+ *		be valid pointers.
+ * @ice_status:	Pointer to a valid output parameter.
+ *		< 0 in case of ICE transaction failure.
+ *		0 otherwise.
+ *
+ * Return: -EINVAL in-case of an error
+ *         0 otherwise
+ */
+int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host, int *ice_status)
+{
+	struct device *dev = NULL;
+	int err = 0;
+	int stat = -EINVAL;
+
+	ice_status = 0;
+
+	dev = qcom_host->hba->dev;
+	if (!dev) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (!qcom_host->ice.pdev) {
+		dev_dbg(dev, "%s: ice device is not enabled\n", __func__);
+		goto out;
+	}
+
+	if (qcom_host->ice.state != UFS_QCOM_ICE_STATE_ACTIVE) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (!qcom_host->ice.vops) {
+		dev_err(dev, "%s: invalid ice_vops\n", __func__);
+		return -EINVAL;
+	}
+
+	if (qcom_host->ice.vops->status) {
+		stat = qcom_host->ice.vops->status(qcom_host->ice.pdev);
+		if (stat < 0) {
+			dev_err(dev, "%s: ice_vops->status failed. stat %d\n",
+				__func__, stat);
+			err = -EINVAL;
+			goto out;
+		}
+
+		*ice_status = stat;
+	}
+
+out:
+	return err;
+}
diff --git a/drivers/scsi/ufs/ufs-qcom-ice.h b/drivers/scsi/ufs/ufs-qcom-ice.h
new file mode 100644
index 0000000..5ccbf5f
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-qcom-ice.h
@@ -0,0 +1,113 @@ 
+/* Copyright (c) 2014-2015, The Linux Foundation. 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 version 2 and
+ * only version 2 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.
+ *
+ */
+
+#ifndef _UFS_QCOM_ICE_H_
+#define _UFS_QCOM_ICE_H_
+
+#include <scsi/scsi_cmnd.h>
+
+#include "ufs-qcom.h"
+
+/*
+ * UFS host controller ICE registers. There are n [0..31]
+ * of each of these registers
+ */
+enum {
+	REG_UFS_QCOM_ICE_CTRL_INFO_1_n           = 0x2204,
+	REG_UFS_QCOM_ICE_CTRL_INFO_2_n           = 0x2208,
+};
+
+/* UFS QCOM ICE CTRL Info 2 register offset */
+enum {
+	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS     = 0,
+	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX  = 0x1,
+	OFFSET_UFS_QCOM_ICE_CTRL_INFO_2_CDU        = 0x6,
+};
+
+/* UFS QCOM ICE CTRL Info 2 register masks */
+enum {
+	MASK_UFS_QCOM_ICE_CTRL_INFO_2_BYPASS     = 0x1,
+	MASK_UFS_QCOM_ICE_CTRL_INFO_2_KEY_INDEX  = 0x1F,
+	MASK_UFS_QCOM_ICE_CTRL_INFO_2_CDU        = 0x8,
+};
+
+/* UFS QCOM ICE encryption/decryption bypass state */
+enum {
+	UFS_QCOM_ICE_DISABLE_BYPASS  = 0,
+	UFS_QCOM_ICE_ENABLE_BYPASS = 1,
+};
+
+/* UFS QCOM ICE Crypto Data Unit of target DUN of Transfer Request */
+enum {
+	UFS_QCOM_ICE_TR_DATA_UNIT_512_B          = 0,
+	UFS_QCOM_ICE_TR_DATA_UNIT_1_KB           = 1,
+	UFS_QCOM_ICE_TR_DATA_UNIT_2_KB           = 2,
+	UFS_QCOM_ICE_TR_DATA_UNIT_4_KB           = 3,
+	UFS_QCOM_ICE_TR_DATA_UNIT_8_KB           = 4,
+	UFS_QCOM_ICE_TR_DATA_UNIT_16_KB          = 5,
+	UFS_QCOM_ICE_TR_DATA_UNIT_32_KB          = 6,
+};
+
+/* UFS QCOM ICE internal state */
+enum {
+	UFS_QCOM_ICE_STATE_DISABLED   = 0,
+	UFS_QCOM_ICE_STATE_ACTIVE     = 1,
+	UFS_QCOM_ICE_STATE_SUSPENDED  = 2,
+};
+
+#ifdef CONFIG_SCSI_UFS_QCOM_ICE
+int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host);
+int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host);
+int ufs_qcom_ice_cfg(struct ufs_qcom_host *qcom_host, struct scsi_cmnd *cmd);
+int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host);
+int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host);
+int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host);
+int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host, int *ice_status);
+#else
+inline int ufs_qcom_ice_get_dev(struct ufs_qcom_host *qcom_host)
+{
+	if (qcom_host) {
+		qcom_host->ice.pdev = NULL;
+		qcom_host->ice.vops = NULL;
+	}
+	return -ENODEV;
+}
+inline int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host)
+{
+	return 0;
+}
+inline int ufs_qcom_ice_cfg(struct ufs_qcom_host *qcom_host,
+			    struct scsi_cmnd *cmd)
+{
+	return 0;
+}
+inline int ufs_qcom_ice_reset(struct ufs_qcom_host *qcom_host)
+{
+	return 0;
+}
+inline int ufs_qcom_ice_resume(struct ufs_qcom_host *qcom_host)
+{
+	return 0;
+}
+inline int ufs_qcom_ice_suspend(struct ufs_qcom_host *qcom_host)
+{
+	return 0;
+}
+inline int ufs_qcom_ice_get_status(struct ufs_qcom_host *qcom_host,
+				   int *ice_status)
+{
+	return 0;
+}
+#endif /* CONFIG_SCSI_UFS_QCOM_ICE */
+
+#endif /* UFS_QCOM_ICE_H_ */
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 9217af9..fd01255 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -22,6 +22,7 @@ 
 #include "unipro.h"
 #include "ufs-qcom.h"
 #include "ufshci.h"
+#include "ufs-qcom-ice.h"
 
 static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS];
 
@@ -294,6 +295,13 @@  static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status)
 		/* check if UFS PHY moved from DISABLED to HIBERN8 */
 		err = ufs_qcom_check_hibern8(hba);
 		ufs_qcom_enable_hw_clk_gating(hba);
+		if (!err) {
+			err = ufs_qcom_ice_reset(host);
+			if (err)
+				dev_err(hba->dev,
+					"%s: ufs_qcom_ice_reset() failed %d\n",
+					__func__, err);
+		}
 
 		break;
 	default:
@@ -453,6 +461,10 @@  static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 		 */
 		ufs_qcom_disable_lane_clks(host);
 		phy_power_off(phy);
+		ret = ufs_qcom_ice_suspend(host);
+		if (ret)
+			dev_err(hba->dev, "%s: failed ufs_qcom_ice_suspend %d\n",
+					__func__, ret);
 
 		/* Assert PHY soft reset */
 		ufs_qcom_assert_reset(hba);
@@ -463,8 +475,10 @@  static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 	 * If UniPro link is not active, PHY ref_clk, main PHY analog power
 	 * rail and low noise analog power rail for PLL can be switched off.
 	 */
-	if (!ufs_qcom_is_link_active(hba))
+	if (!ufs_qcom_is_link_active(hba)) {
 		phy_power_off(phy);
+		ufs_qcom_ice_suspend(host);
+	}
 
 out:
 	return ret;
@@ -483,6 +497,13 @@  static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 		goto out;
 	}
 
+	err = ufs_qcom_ice_resume(host);
+	if (err) {
+		dev_err(hba->dev, "%s: ufs_qcom_ice_resume failed, err = %d\n",
+			__func__, err);
+		goto out;
+	}
+
 	hba->is_sys_suspended = false;
 
 out:
@@ -917,6 +938,30 @@  static int ufs_qcom_init(struct ufs_hba *hba)
 	host->hba = hba;
 	hba->priv = (void *)host;
 
+	err = ufs_qcom_ice_get_dev(host);
+	if (err == -EPROBE_DEFER) {
+		/*
+		 * UFS driver might be probed before ICE driver does.
+		 * In that case we would like to return EPROBE_DEFER code
+		 * in order to delay its probing.
+		 */
+		dev_err(dev, "%s: required ICE device not probed yet err = %d\n",
+			__func__, err);
+		goto out_host_free;
+
+	} else if (err == -ENODEV) {
+		/*
+		 * ICE device is not enabled in DTS file. No need for further
+		 * initialization of ICE driver.
+		 */
+		dev_warn(dev, "%s: ICE device is not enabled",
+			__func__);
+	} else if (err) {
+		dev_err(dev, "%s: ufs_qcom_ice_get_dev failed %d\n",
+			__func__, err);
+		goto out_host_free;
+	}
+
 	host->generic_phy = devm_phy_get(dev, "ufsphy");
 
 	if (IS_ERR(host->generic_phy)) {
@@ -944,6 +989,15 @@  static int ufs_qcom_init(struct ufs_hba *hba)
 	hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
 
 	ufs_qcom_setup_clocks(hba, true);
+	if (host->ice.pdev) {
+		err = ufs_qcom_ice_init(host);
+		if (err) {
+			dev_err(dev, "%s: ICE driver initialization failed (%d)\n",
+				__func__, err);
+			device_remove_file(dev, &host->bus_vote.max_bus_bw);
+			goto out_disable_phy;
+		}
+	}
 
 	if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
 		ufs_qcom_hosts[hba->dev->id] = host;
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 9a6febd..8340f2c 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -151,6 +151,30 @@  struct ufs_qcom_bus_vote {
 	struct device_attribute max_bus_bw;
 };
 
+/**
+ * struct ufs_qcom_ice_data - ICE related information
+ * @vops:	pointer to variant operations of ICE
+ * @async_done:	completion for supporting ICE's driver asynchronous nature
+ * @pdev:	pointer to the proper ICE platform device
+ * @state:      UFS-ICE interface's internal state (see
+ *       ufs-qcom-ice.h for possible internal states)
+ * @quirks:     UFS-ICE interface related quirks
+ */
+struct ufs_qcom_ice_data {
+	struct qcom_ice_variant_ops *vops;
+	struct completion async_done;
+	struct platform_device *pdev;
+	int state;
+
+	/*
+	 * If UFS host controller should handle cryptographic engine's
+	 * errors, enables this quirk.
+	 */
+	#define UFS_QCOM_ICE_QUIRK_HANDLE_CRYPTO_ENGINE_ERRORS	UFS_BIT(0)
+
+	u16 quirks;
+};
+
 struct ufs_qcom_host {
 	struct phy *generic_phy;
 	struct ufs_hba *hba;
@@ -161,6 +185,7 @@  struct ufs_qcom_host {
 	struct clk *rx_l1_sync_clk;
 	struct clk *tx_l1_sync_clk;
 	bool is_lane_clks_enabled;
+	struct ufs_qcom_ice_data ice;
 };
 
 #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba)