18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Qualcomm ICE (Inline Crypto Engine) support.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
68c2ecf20Sopenharmony_ci * Copyright 2019 Google LLC
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/qcom_scm.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "ufshcd-crypto.h"
138c2ecf20Sopenharmony_ci#include "ufs-qcom.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define AES_256_XTS_KEY_SIZE			64
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* QCOM ICE registers */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_CONTROL			0x0000
208c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_RESET			0x0004
218c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_VERSION			0x0008
228c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_FUSE_SETTING		0x0010
238c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_PARAMETERS_1		0x0014
248c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_PARAMETERS_2		0x0018
258c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_PARAMETERS_3		0x001C
268c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_PARAMETERS_4		0x0020
278c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_PARAMETERS_5		0x0024
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* QCOM ICE v3.X only */
308c2ecf20Sopenharmony_ci#define QCOM_ICE_GENERAL_ERR_STTS		0x0040
318c2ecf20Sopenharmony_ci#define QCOM_ICE_INVALID_CCFG_ERR_STTS		0x0030
328c2ecf20Sopenharmony_ci#define QCOM_ICE_GENERAL_ERR_MASK		0x0044
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* QCOM ICE v2.X only */
358c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_NON_SEC_IRQ_STTS		0x0040
368c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_NON_SEC_IRQ_MASK		0x0044
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_NON_SEC_IRQ_CLR		0x0048
398c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_STREAM1_ERROR_SYNDROME1	0x0050
408c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_STREAM1_ERROR_SYNDROME2	0x0054
418c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_STREAM2_ERROR_SYNDROME1	0x0058
428c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_STREAM2_ERROR_SYNDROME2	0x005C
438c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_STREAM1_BIST_ERROR_VEC	0x0060
448c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_STREAM2_BIST_ERROR_VEC	0x0064
458c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_STREAM1_BIST_FINISH_VEC	0x0068
468c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_STREAM2_BIST_FINISH_VEC	0x006C
478c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_BIST_STATUS		0x0070
488c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_BYPASS_STATUS		0x0074
498c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_ADVANCED_CONTROL		0x1000
508c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_ENDIAN_SWAP		0x1004
518c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_TEST_BUS_CONTROL		0x1010
528c2ecf20Sopenharmony_ci#define QCOM_ICE_REG_TEST_BUS_REG		0x1014
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* BIST ("built-in self-test"?) status flags */
558c2ecf20Sopenharmony_ci#define QCOM_ICE_BIST_STATUS_MASK		0xF0000000
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define QCOM_ICE_FUSE_SETTING_MASK		0x1
588c2ecf20Sopenharmony_ci#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK	0x2
598c2ecf20Sopenharmony_ci#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK	0x4
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define qcom_ice_writel(host, val, reg)	\
628c2ecf20Sopenharmony_ci	writel((val), (host)->ice_mmio + (reg))
638c2ecf20Sopenharmony_ci#define qcom_ice_readl(host, reg)	\
648c2ecf20Sopenharmony_ci	readl((host)->ice_mmio + (reg))
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic bool qcom_ice_supported(struct ufs_qcom_host *host)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct device *dev = host->hba->dev;
698c2ecf20Sopenharmony_ci	u32 regval = qcom_ice_readl(host, QCOM_ICE_REG_VERSION);
708c2ecf20Sopenharmony_ci	int major = regval >> 24;
718c2ecf20Sopenharmony_ci	int minor = (regval >> 16) & 0xFF;
728c2ecf20Sopenharmony_ci	int step = regval & 0xFFFF;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* For now this driver only supports ICE version 3. */
758c2ecf20Sopenharmony_ci	if (major != 3) {
768c2ecf20Sopenharmony_ci		dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n",
778c2ecf20Sopenharmony_ci			 major, minor, step);
788c2ecf20Sopenharmony_ci		return false;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	dev_info(dev, "Found QC Inline Crypto Engine (ICE) v%d.%d.%d\n",
828c2ecf20Sopenharmony_ci		 major, minor, step);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* If fuses are blown, ICE might not work in the standard way. */
858c2ecf20Sopenharmony_ci	regval = qcom_ice_readl(host, QCOM_ICE_REG_FUSE_SETTING);
868c2ecf20Sopenharmony_ci	if (regval & (QCOM_ICE_FUSE_SETTING_MASK |
878c2ecf20Sopenharmony_ci		      QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK |
888c2ecf20Sopenharmony_ci		      QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) {
898c2ecf20Sopenharmony_ci		dev_warn(dev, "Fuses are blown; ICE is unusable!\n");
908c2ecf20Sopenharmony_ci		return false;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	return true;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ciint ufs_qcom_ice_init(struct ufs_qcom_host *host)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct ufs_hba *hba = host->hba;
988c2ecf20Sopenharmony_ci	struct device *dev = hba->dev;
998c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
1008c2ecf20Sopenharmony_ci	struct resource *res;
1018c2ecf20Sopenharmony_ci	int err;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (!(ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES) &
1048c2ecf20Sopenharmony_ci	      MASK_CRYPTO_SUPPORT))
1058c2ecf20Sopenharmony_ci		return 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ice");
1088c2ecf20Sopenharmony_ci	if (!res) {
1098c2ecf20Sopenharmony_ci		dev_warn(dev, "ICE registers not found\n");
1108c2ecf20Sopenharmony_ci		goto disable;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (!qcom_scm_ice_available()) {
1148c2ecf20Sopenharmony_ci		dev_warn(dev, "ICE SCM interface not found\n");
1158c2ecf20Sopenharmony_ci		goto disable;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	host->ice_mmio = devm_ioremap_resource(dev, res);
1198c2ecf20Sopenharmony_ci	if (IS_ERR(host->ice_mmio)) {
1208c2ecf20Sopenharmony_ci		err = PTR_ERR(host->ice_mmio);
1218c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to map ICE registers; err=%d\n", err);
1228c2ecf20Sopenharmony_ci		return err;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (!qcom_ice_supported(host))
1268c2ecf20Sopenharmony_ci		goto disable;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cidisable:
1318c2ecf20Sopenharmony_ci	dev_warn(dev, "Disabling inline encryption support\n");
1328c2ecf20Sopenharmony_ci	hba->caps &= ~UFSHCD_CAP_CRYPTO;
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic void qcom_ice_low_power_mode_enable(struct ufs_qcom_host *host)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	u32 regval;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	regval = qcom_ice_readl(host, QCOM_ICE_REG_ADVANCED_CONTROL);
1418c2ecf20Sopenharmony_ci	/*
1428c2ecf20Sopenharmony_ci	 * Enable low power mode sequence
1438c2ecf20Sopenharmony_ci	 * [0]-0, [1]-0, [2]-0, [3]-E, [4]-0, [5]-0, [6]-0, [7]-0
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	regval |= 0x7000;
1468c2ecf20Sopenharmony_ci	qcom_ice_writel(host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void qcom_ice_optimization_enable(struct ufs_qcom_host *host)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	u32 regval;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* ICE Optimizations Enable Sequence */
1548c2ecf20Sopenharmony_ci	regval = qcom_ice_readl(host, QCOM_ICE_REG_ADVANCED_CONTROL);
1558c2ecf20Sopenharmony_ci	regval |= 0xD807100;
1568c2ecf20Sopenharmony_ci	/* ICE HPG requires delay before writing */
1578c2ecf20Sopenharmony_ci	udelay(5);
1588c2ecf20Sopenharmony_ci	qcom_ice_writel(host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
1598c2ecf20Sopenharmony_ci	udelay(5);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciint ufs_qcom_ice_enable(struct ufs_qcom_host *host)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	if (!(host->hba->caps & UFSHCD_CAP_CRYPTO))
1658c2ecf20Sopenharmony_ci		return 0;
1668c2ecf20Sopenharmony_ci	qcom_ice_low_power_mode_enable(host);
1678c2ecf20Sopenharmony_ci	qcom_ice_optimization_enable(host);
1688c2ecf20Sopenharmony_ci	return ufs_qcom_ice_resume(host);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/* Poll until all BIST bits are reset */
1728c2ecf20Sopenharmony_cistatic int qcom_ice_wait_bist_status(struct ufs_qcom_host *host)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	int count;
1758c2ecf20Sopenharmony_ci	u32 reg;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	for (count = 0; count < 100; count++) {
1788c2ecf20Sopenharmony_ci		reg = qcom_ice_readl(host, QCOM_ICE_REG_BIST_STATUS);
1798c2ecf20Sopenharmony_ci		if (!(reg & QCOM_ICE_BIST_STATUS_MASK))
1808c2ecf20Sopenharmony_ci			break;
1818c2ecf20Sopenharmony_ci		udelay(50);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	if (reg)
1848c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ciint ufs_qcom_ice_resume(struct ufs_qcom_host *host)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int err;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!(host->hba->caps & UFSHCD_CAP_CRYPTO))
1938c2ecf20Sopenharmony_ci		return 0;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	err = qcom_ice_wait_bist_status(host);
1968c2ecf20Sopenharmony_ci	if (err) {
1978c2ecf20Sopenharmony_ci		dev_err(host->hba->dev, "BIST status error (%d)\n", err);
1988c2ecf20Sopenharmony_ci		return err;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/*
2048c2ecf20Sopenharmony_ci * Program a key into a QC ICE keyslot, or evict a keyslot.  QC ICE requires
2058c2ecf20Sopenharmony_ci * vendor-specific SCM calls for this; it doesn't support the standard way.
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_ciint ufs_qcom_ice_program_key(struct ufs_hba *hba,
2088c2ecf20Sopenharmony_ci			     const union ufs_crypto_cfg_entry *cfg, int slot)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	union ufs_crypto_cap_entry cap;
2118c2ecf20Sopenharmony_ci	union {
2128c2ecf20Sopenharmony_ci		u8 bytes[AES_256_XTS_KEY_SIZE];
2138c2ecf20Sopenharmony_ci		u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
2148c2ecf20Sopenharmony_ci	} key;
2158c2ecf20Sopenharmony_ci	int i;
2168c2ecf20Sopenharmony_ci	int err;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (!(cfg->config_enable & UFS_CRYPTO_CONFIGURATION_ENABLE))
2198c2ecf20Sopenharmony_ci		return qcom_scm_ice_invalidate_key(slot);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Only AES-256-XTS has been tested so far. */
2228c2ecf20Sopenharmony_ci	cap = hba->crypto_cap_array[cfg->crypto_cap_idx];
2238c2ecf20Sopenharmony_ci	if (cap.algorithm_id != UFS_CRYPTO_ALG_AES_XTS ||
2248c2ecf20Sopenharmony_ci	    cap.key_size != UFS_CRYPTO_KEY_SIZE_256) {
2258c2ecf20Sopenharmony_ci		dev_err_ratelimited(hba->dev,
2268c2ecf20Sopenharmony_ci				    "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n",
2278c2ecf20Sopenharmony_ci				    cap.algorithm_id, cap.key_size);
2288c2ecf20Sopenharmony_ci		return -EINVAL;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	memcpy(key.bytes, cfg->crypto_key, AES_256_XTS_KEY_SIZE);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/*
2348c2ecf20Sopenharmony_ci	 * The SCM call byte-swaps the 32-bit words of the key.  So we have to
2358c2ecf20Sopenharmony_ci	 * do the same, in order for the final key be correct.
2368c2ecf20Sopenharmony_ci	 */
2378c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(key.words); i++)
2388c2ecf20Sopenharmony_ci		__cpu_to_be32s(&key.words[i]);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
2418c2ecf20Sopenharmony_ci				   QCOM_SCM_ICE_CIPHER_AES_256_XTS,
2428c2ecf20Sopenharmony_ci				   cfg->data_unit_size);
2438c2ecf20Sopenharmony_ci	memzero_explicit(&key, sizeof(key));
2448c2ecf20Sopenharmony_ci	return err;
2458c2ecf20Sopenharmony_ci}
246