If you have a controller with this interface, say Y or M here.
If unsure, say N.
+
config SCSI_UFS_EXYNOS_FMP
tristate "EXYNOS Flash Memory Protector for UFS Host"
- default y
depends on SCSI_UFS_EXYNOS && EXYNOS_FMP
+ select SCSI_UFS_EXYNOS_SMU
---help---
This selects the EXYNOS UFS FMP Driver.
config SCSI_UFS_EXYNOS_SMU
tristate "EXYNOS Secure Management Unit for UFS Host"
- default y
depends on SCSI_UFS_EXYNOS && EXYNOS_SMU
---help---
This selects the EXYNOS UFS SMU Driver.
obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o ufs-exynos-dbg.o
obj-$(CONFIG_SOC_EXYNOS9810) += ufs-cal-9810.o
obj-$(CONFIG_SOC_EXYNOS9610) += ufs-cal-9610.o
-obj-$(CONFIG_SCSI_UFS_EXYNOS_FMP) += ufs-exynos-fmp.o
-obj-$(CONFIG_SCSI_UFS_EXYNOS_SMU) += ufs-exynos-smu.o
+ifneq (,$(filter $(CONFIG_SCSI_UFS_EXYNOS_SMU) $(CONFIG_SCSI_UFS_EXYNOS_FMP),y))
+obj-y += ufs-exynos-fmp.o
+endif
/*
* Copyright (C) 2016 Samsung Electronics Co., Ltd.
+ * Author: Boojin Kim <boojin.kim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <linux/of.h>
#include <crypto/diskcipher.h>
#include <crypto/fmp.h>
-
#include "ufshcd.h"
#include "ufs-exynos.h"
+/* smu functions */
+#if defined(CONFIG_SCSI_UFS_EXYNOS_SMU)
+static void exynos_ufs_smu_entry0_init(struct exynos_ufs *ufs)
+{
+ writel(0x0, ufs->reg_ufsp + UFSPSBEGIN0);
+ writel(0xffffffff, ufs->reg_ufsp + UFSPSEND0);
+ writel(0xff, ufs->reg_ufsp + UFSPSLUN0);
+ writel(0xf1, ufs->reg_ufsp + UFSPSCTRL0);
+}
+
+int exynos_ufs_smu_init(struct exynos_ufs *ufs)
+{
+ if (!ufs || (ufs->smu == SMU_ID_MAX)) {
+ exynos_ufs_smu_entry0_init(ufs);
+ return 0;
+ }
+
+ dev_info(ufs->dev, "%s with id:%d\n", __func__, ufs->smu);
+ return exynos_smu_init(ufs->smu, SMU_INIT);
+}
+
+int exynos_ufs_smu_resume(struct exynos_ufs *ufs)
+{
+ int fmp_id;
+
+ if (!ufs)
+ return 0;
+
+ if (ufs->smu < SMU_ID_MAX)
+ fmp_id = ufs->smu;
+ else if (ufs->fmp < SMU_ID_MAX)
+ fmp_id = ufs->fmp;
+ else {
+ exynos_ufs_smu_entry0_init(ufs);
+ return 0;
+ }
+
+ dev_info(ufs->dev, "%s with id:%d\n", __func__, fmp_id);
+ return exynos_smu_resume(fmp_id);
+}
+
+int exynos_ufs_smu_abort(struct exynos_ufs *ufs)
+{
+ if (!ufs || (ufs->smu == SMU_ID_MAX))
+ return 0;
+
+ dev_info(ufs->dev, "%s with id:%d\n", __func__, ufs->smu);
+ return exynos_smu_abort(ufs->smu, SMU_ABORT);
+}
+#endif
+/* fmp functions */
+#ifdef CONFIG_SCSI_UFS_EXYNOS_FMP
+int exynos_ufs_fmp_sec_cfg(struct exynos_ufs *ufs)
+{
+ if (!ufs || (ufs->fmp == SMU_ID_MAX))
+ return 0;
+
+ if (ufs->fmp != SMU_EMBEDDED)
+ dev_err(ufs->dev, "%s is fails id:%d\n",
+ __func__, ufs->fmp);
+
+
+ dev_info(ufs->dev, "%s with id:%d\n", __func__, ufs->fmp);
+ return exynos_fmp_sec_config(ufs->fmp);
+}
+
+static struct bio *get_bio(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+ struct exynos_ufs *ufs;
+
+ if (!hba || !lrbp) {
+ pr_err("%s: Invalid MMC:%p data:%p\n", __func__, hba, lrbp);
+ return NULL;
+ }
+
+ ufs = dev_get_platdata(hba->dev);
+ if (ufs->fmp == SMU_ID_MAX)
+ return NULL;
+
+ if (!virt_addr_valid(lrbp->cmd)) {
+ dev_err(hba->dev, "Invalid cmd:%p\n", lrbp->cmd);
+ return NULL;
+ }
+
+ if (!virt_addr_valid(lrbp->cmd->request->bio)) {
+ if (lrbp->cmd->request->bio)
+ dev_err(hba->dev, "Invalid bio:%p\n", lrbp->cmd->request->bio);
+ return NULL;
+ }
+ else
+ return lrbp->cmd->request->bio;
+}
+
int exynos_ufs_fmp_cfg(struct ufs_hba *hba,
struct ufshcd_lrb *lrbp,
struct scatterlist *sg,
uint32_t index, int sector_offset)
{
struct fmp_request req;
- struct scsi_cmnd *cmd = lrbp->cmd;
- struct bio *bio;
struct crypto_diskcipher *dtfm;
sector_t iv;
+ struct bio *bio = get_bio(hba, lrbp);
- if (!cmd || !virt_addr_valid(cmd)) {
- dev_err(hba->dev, "Invalid cmd:%p\n", cmd);
- goto no_crypto;
- }
+ if (!bio)
+ return 0;
- /* fill fmp_data_setting */
- bio = cmd->request->bio;
dtfm = crypto_diskcipher_get(bio);
if (dtfm) {
-#ifdef EANBLE_DISKCIPHER_DEBUG
- crypto_diskcipher_check(bio, sg_page(sg));
-#endif
iv = bio->bi_iter.bi_sector + (sector_t) sector_offset;
req.table = (void *)&lrbp->ucd_prdt_ptr[index];
req.cmdq_enabled = 0;
/* check fips flag. use fmp without diskcipher */
if (!dtfm->algo) {
if (exynos_fmp_crypt((void *)dtfm, &req))
- goto no_crypto;
+ pr_warn("%s: fails to test fips\n", __func__);
return 0;
}
+#endif
+#ifdef FS_ENCRYPTION_DEBUG
+ crypto_diskcipher_check_fscrypt(bio, sg_page(sg));
#endif
if (crypto_diskcipher_set_crypt(dtfm, &req)) {
pr_warn("%s: fails to set crypt\n", __func__);
return -EINVAL;
}
-
return 0;
}
-no_crypto:
+
exynos_fmp_bypass(&lrbp->ucd_prdt_ptr[index], 0);
return 0;
}
struct crypto_diskcipher *dtfm;
struct fmp_crypto_info *ci;
struct fmp_request req;
+ struct bio *bio = get_bio(hba, lrbp);
- if (!lrbp->cmd)
- goto out;
+ if (!bio)
+ return 0;
sg_segments = scsi_sg_count(lrbp->cmd);
if (!sg_segments)
- goto out;
+ return 0;
- dtfm = crypto_diskcipher_get(lrbp->cmd->request->bio);
+ dtfm = crypto_diskcipher_get(bio);
if (dtfm) {
#ifdef CONFIG_EXYNOS_FMP_FIPS
/* check fips flag. use fmp without diskcipher */
scsi_for_each_sg(lrbp->cmd, sg, sg_segments, idx) {
req.table = (void *)&prd_table[idx];
ret = exynos_fmp_clear((void *)dtfm, &req);
- if (ret)
+ if (ret) {
+ pr_warn("%s: fails to clear fips\n",
+ __func__);
break;
+ }
}
- goto out;
+ return 0;
}
#endif
/* clear key on descrptor */
scsi_for_each_sg(lrbp->cmd, sg, sg_segments, idx) {
req.table = (void *)&prd_table[idx];
ret = crypto_diskcipher_clear_crypt(dtfm, &req);
- if (ret)
+ if (ret) {
+ pr_err("%s: fail to clear desc (%d)\n",
+ __func__, ret);
break;
+ }
}
}
}
-out:
- if (ret)
- pr_err("%s: Fail to clear desc (%d)\n", __func__, ret);
return ret;
}
+#endif
#include "ufshcd.h"
#include "ufs-exynos.h"
-#ifdef CONFIG_SCSI_UFS_EXYNOS_FMP
+#if defined(CONFIG_SCSI_UFS_EXYNOS_FMP)
int exynos_ufs_fmp_cfg(struct ufs_hba *hba,
struct ufshcd_lrb *lrbp,
struct scatterlist *sg,
uint32_t index,
int sector_offset);
int exynos_ufs_fmp_clear(struct ufs_hba *hba, struct ufshcd_lrb *lrbp);
+int exynos_ufs_fmp_sec_cfg(struct exynos_ufs *ufs);
#else
inline int exynos_ufs_fmp_cfg(struct ufs_hba *hba,
struct ufshcd_lrb *lrbp,
{
return 0;
}
+
+int exynos_ufs_fmp_sec_cfg(struct exynos_ufs *ufs)
+{
+ return 0;
+}
#endif /* CONFIG_SCSI_UFS_EXYNOS_FMP */
+#if defined(CONFIG_SCSI_UFS_EXYNOS_SMU)
+int exynos_ufs_smu_init(struct exynos_ufs *ufs);
+int exynos_ufs_smu_resume(struct exynos_ufs *ufs);
+int exynos_ufs_smu_abort(struct exynos_ufs *ufs);
+#else
+inline int exynos_ufs_smu_init(struct exynos_ufs *ufs)
+{
+ writel(0x0, ufs->reg_ufsp + UFSPSBEGIN0);
+ writel(0xffffffff, ufs->reg_ufsp + UFSPSEND0);
+ writel(0xff, ufs->reg_ufsp + UFSPSLUN0);
+ writel(0xf1, ufs->reg_ufsp + UFSPSCTRL0);
+
+ return 0;
+}
+
+inline int exynos_ufs_smu_resume(struct exynos_ufs *ufs)
+{
+ return exynos_ufs_smu_init(ufs);
+}
+
+inline int exynos_ufs_smu_abort(struct exynos_ufs *ufs)
+{
+ return 0;
+}
+#endif
#endif /* _UFS_EXYNOS_FMP_H_ */
+++ /dev/null
-/*
- * Copyright (C) 2016 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/of.h>
-#include <crypto/smu.h>
-
-#include "ufshcd.h"
-#include "ufs-exynos.h"
-#include "ufs-exynos-smu.h"
-
-#define EXYNOS_UFS_SMU_LABEL "ufs-exynos-smu"
-
-static struct exynos_smu_variant_ops *exynos_ufs_smu_get_vops(struct device *dev)
-{
- struct exynos_smu_variant_ops *smu_vops = NULL;
- struct device_node *node;
-
- node = of_parse_phandle(dev->of_node, EXYNOS_UFS_SMU_LABEL, 0);
- if (!node) {
- dev_err(dev, "%s: exynos-ufs-smu property not specified\n",
- __func__);
- goto err;
- }
-
- smu_vops = exynos_smu_get_variant_ops(node);
- if (!smu_vops)
- dev_err(dev, "%s: Fail to get smu_vops\n", __func__);
-
- of_node_put(node);
-err:
- return smu_vops;
-}
-
-static struct platform_device *exynos_ufs_smu_get_pdevice(struct device *dev)
-{
- struct device_node *node;
- struct platform_device *smu_pdev = NULL;
-
- node = of_parse_phandle(dev->of_node, EXYNOS_UFS_SMU_LABEL, 0);
- if (!node) {
- dev_err(dev, "%s: exynos-ufs-smu property not specified\n",
- __func__);
- goto err;
- }
-
- smu_pdev = exynos_smu_get_pdevice(node);
-err:
- return smu_pdev;
-}
-
-int exynos_ufs_smu_get_dev(struct exynos_ufs *ufs)
-{
- int ret;
- struct device *dev;
-
- if (!ufs || !ufs->dev) {
- pr_err("%s: invalid ufs host, host->dev\n", __func__);
- ret = -EINVAL;
- goto err;
- }
-
- dev = ufs->dev;
- ufs->smu.vops = exynos_ufs_smu_get_vops(dev);
- ufs->smu.pdev = exynos_ufs_smu_get_pdevice(dev);
-
- if (ufs->smu.pdev == ERR_PTR(-EPROBE_DEFER)) {
- dev_err(dev, "%s: SMU device not probed yet\n", __func__);
- ret = -EPROBE_DEFER;
- goto err;
- }
-
- if (!ufs->smu.pdev || !ufs->smu.vops) {
- dev_err(dev, "%s: Host device doesn't have SMU or fail to get SMU",
- __func__);
- ret = -ENODEV;
- goto err;
- }
-
- return 0;
-err:
- ufs->smu.pdev = NULL;
- ufs->smu.vops = NULL;
-
- return ret;
-}
-
-int exynos_ufs_smu_init(struct exynos_ufs *ufs)
-{
- struct smu_data_setting smu_set;
-
- if (!ufs->smu.vops || !ufs->smu.pdev)
- return 0;
-
- smu_set.id = SMU_EMBEDDED;
- smu_set.command = SMU_INIT;
- return ufs->smu.vops->init(ufs->smu.pdev, &smu_set);
-}
-
-int exynos_ufs_smu_sec_cfg(struct exynos_ufs *ufs)
-{
- struct smu_data_setting smu_set;
-
- if (!ufs->smu.vops || !ufs->smu.pdev)
- return 0;
-
- smu_set.id = SMU_EMBEDDED;
- smu_set.desc_type = CFG_DESCTYPE;
-
- return ufs->smu.vops->sec_config(ufs->smu.pdev, &smu_set);
-}
-
-int exynos_ufs_smu_resume(struct exynos_ufs *ufs)
-{
- struct smu_data_setting smu_set;
-
- if (!ufs->smu.vops || !ufs->smu.pdev)
- return 0;
-
- smu_set.id = SMU_EMBEDDED;
- return ufs->smu.vops->resume(ufs->smu.pdev, &smu_set);
-}
-
-int exynos_ufs_smu_abort(struct exynos_ufs *ufs)
-{
- struct smu_data_setting smu_set;
-
- if (!ufs->smu.vops || !ufs->smu.pdev)
- return 0;
-
- smu_set.id = SMU_EMBEDDED;
- smu_set.command = SMU_ABORT;
- return ufs->smu.vops->abort(ufs->smu.pdev, &smu_set);
-}
+++ /dev/null
-/*
- * Copyright (C) 2016 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef _UFS_EXYNOS_SMU_H_
-#define _UFS_EXYNOS_SMU_H_
-
-#ifdef CONFIG_SCSI_UFS_EXYNOS_SMU
-int exynos_ufs_smu_get_dev(struct exynos_ufs *ufs);
-int exynos_ufs_smu_init(struct exynos_ufs *ufs);
-int exynos_ufs_smu_sec_cfg(struct exynos_ufs *ufs);
-int exynos_ufs_smu_resume(struct exynos_ufs *ufs);
-int exynos_ufs_smu_abort(struct exynos_ufs *ufs);
-#else
-inline int exynos_ufs_smu_get_dev(struct exynos_ufs *ufs)
-{
- if (ufs) {
- ufs->smu.pdev = NULL;
- ufs->smu.vops = NULL;
- }
- return 0;
-}
-
-inline int exynos_ufs_smu_init(struct exynos_ufs *ufs)
-{
- writel(0x0, ufs->reg_ufsp + UFSPSBEGIN0);
- writel(0xffffffff, ufs->reg_ufsp + UFSPSEND0);
- writel(0xff, ufs->reg_ufsp + UFSPSLUN0);
- writel(0xf1, ufs->reg_ufsp + UFSPSCTRL0);
-
- return 0;
-}
-
-inline int exynos_ufs_smu_sec_cfg(struct exynos_ufs *ufs)
-{
- return 0;
-}
-
-inline int exynos_ufs_smu_resume(struct exynos_ufs *ufs)
-{
- writel(0x0, ufs->reg_ufsp + UFSPSBEGIN0);
- writel(0xffffffff, ufs->reg_ufsp + UFSPSEND0);
- writel(0xff, ufs->reg_ufsp + UFSPSLUN0);
- writel(0xf1, ufs->reg_ufsp + UFSPSCTRL0);
-
- return 0;
-}
-
-inline int exynos_ufs_smu_abort(struct exynos_ufs *ufs)
-{
- return 0;
-}
-
-#endif /* CONFIG_SCSI_UFS_EXYNOS_SMU */
-#endif /* _UFS_EXYNOS_SMU_H_ */
#include "ufshcd-pltfrm.h"
#include "ufs-exynos.h"
#include "ufs-exynos-fmp.h"
-#include "ufs-exynos-smu.h"
#include <soc/samsung/exynos-fsys0-tcxo.h>
#include <soc/samsung/exynos-cpupm.h>
#include <linux/mfd/syscon.h>
#include <linux/soc/samsung/exynos-soc.h>
#include <linux/spinlock.h>
-
-
-
/*
* Unipro attribute value
*/
{
struct exynos_ufs *ufs = to_exynos_ufs(hba);
int ret;
+ int id;
/* set features, such as caps or quirks */
exynos_ufs_set_features(hba, ufs->hw_rev);
if (ret)
return ret;
- ret = exynos_ufs_smu_get_dev(ufs);
- if (ret == -EPROBE_DEFER) {
- dev_err(ufs->dev, "%s: SMU device not probed yet (%d)\n",
- __func__, ret);
- return ret;
- } else if (ret) {
- dev_err(ufs->dev, "%s, Fail to get SMU device (%d)\n",
- __func__, ret);
- return ret;
- }
+ /* get fmp & smu id */
+ ret = of_property_read_u32(ufs->dev->of_node, "fmp-id", &id);
+ if (ret)
+ ufs->fmp = SMU_ID_MAX;
+ else
+ ufs->fmp = id;
+
+ ret = of_property_read_u32(ufs->dev->of_node, "smu-id", &id);
+ if (ret)
+ ufs->smu = SMU_ID_MAX;
+ else
+ ufs->smu = id;
/* FMPSECURITY & SMU */
- exynos_ufs_smu_sec_cfg(ufs);
+ exynos_ufs_fmp_sec_cfg(ufs);
exynos_ufs_smu_init(ufs);
/* Enable log */
exynos_ufs_ctrl_auto_hci_clk(ufs, false);
/* FMPSECURITY & SMU resume */
- exynos_ufs_smu_sec_cfg(ufs);
+ exynos_ufs_fmp_sec_cfg(ufs);
exynos_ufs_smu_resume(ufs);
/* secure log */
#else
#include "ufs-cal-9810.h"
#endif
+#include <crypto/smu.h>
#define UFS_VER_0004 4
#define UFS_VER_0005 5
struct exynos_ufs_misc_log misc;
};
-struct exynos_smu_data {
- struct exynos_smu_variant_ops *vops;
- struct platform_device *pdev;
-};
-
-struct exynos_fmp_data {
- struct exynos_fmp_variant_ops *vops;
- struct platform_device *pdev;
-};
-
struct exynos_access_cxt {
u32 offset;
u32 mask;
struct uic_pwr_mode req_pmd_parm;
struct uic_pwr_mode act_pmd_parm;
- struct exynos_smu_data smu;
+ enum smu_id fmp;
+ enum smu_id smu;
u32 rx_min_actv_time_cap;
u32 rx_hibern8_time_cap;