2 * Copyright (C) 2016 Samsung Electronics Co., Ltd.
3 * Author: Boojin Kim <boojin.kim@samsung.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
11 #include <linux/module.h>
13 #include <crypto/diskcipher.h>
14 #include <crypto/fmp.h>
16 #include "ufs-exynos.h"
19 #if defined(CONFIG_SCSI_UFS_EXYNOS_SMU)
20 static void exynos_ufs_smu_entry0_init(struct exynos_ufs
*ufs
)
22 writel(0x0, ufs
->reg_ufsp
+ UFSPSBEGIN0
);
23 writel(0xffffffff, ufs
->reg_ufsp
+ UFSPSEND0
);
24 writel(0xff, ufs
->reg_ufsp
+ UFSPSLUN0
);
25 writel(0xf1, ufs
->reg_ufsp
+ UFSPSCTRL0
);
28 int exynos_ufs_smu_init(struct exynos_ufs
*ufs
)
30 if (!ufs
|| (ufs
->smu
== SMU_ID_MAX
)) {
31 exynos_ufs_smu_entry0_init(ufs
);
35 dev_info(ufs
->dev
, "%s with id:%d\n", __func__
, ufs
->smu
);
36 return exynos_smu_init(ufs
->smu
, SMU_INIT
);
39 int exynos_ufs_smu_resume(struct exynos_ufs
*ufs
)
46 if (ufs
->smu
< SMU_ID_MAX
)
48 else if (ufs
->fmp
< SMU_ID_MAX
)
51 exynos_ufs_smu_entry0_init(ufs
);
55 dev_info(ufs
->dev
, "%s with id:%d\n", __func__
, fmp_id
);
56 return exynos_smu_resume(fmp_id
);
59 int exynos_ufs_smu_abort(struct exynos_ufs
*ufs
)
61 if (!ufs
|| (ufs
->smu
== SMU_ID_MAX
))
64 dev_info(ufs
->dev
, "%s with id:%d\n", __func__
, ufs
->smu
);
65 return exynos_smu_abort(ufs
->smu
, SMU_ABORT
);
69 #ifdef CONFIG_SCSI_UFS_EXYNOS_FMP
70 int exynos_ufs_fmp_sec_cfg(struct exynos_ufs
*ufs
)
72 if (!ufs
|| (ufs
->fmp
== SMU_ID_MAX
))
75 if (ufs
->fmp
!= SMU_EMBEDDED
)
76 dev_err(ufs
->dev
, "%s is fails id:%d\n",
80 dev_info(ufs
->dev
, "%s with id:%d\n", __func__
, ufs
->fmp
);
81 return exynos_fmp_sec_config(ufs
->fmp
);
84 static struct bio
*get_bio(struct ufs_hba
*hba
, struct ufshcd_lrb
*lrbp
)
86 struct exynos_ufs
*ufs
;
89 pr_err("%s: Invalid MMC:%p data:%p\n", __func__
, hba
, lrbp
);
93 ufs
= dev_get_platdata(hba
->dev
);
94 if (ufs
->fmp
== SMU_ID_MAX
)
97 if (!virt_addr_valid(lrbp
->cmd
)) {
98 dev_err(hba
->dev
, "Invalid cmd:%p\n", lrbp
->cmd
);
102 if (!virt_addr_valid(lrbp
->cmd
->request
->bio
)) {
103 if (lrbp
->cmd
->request
->bio
)
104 dev_err(hba
->dev
, "Invalid bio:%p\n", lrbp
->cmd
->request
->bio
);
108 return lrbp
->cmd
->request
->bio
;
111 int exynos_ufs_fmp_cfg(struct ufs_hba
*hba
,
112 struct ufshcd_lrb
*lrbp
,
113 struct scatterlist
*sg
,
114 uint32_t index
, int sector_offset
, int page_index
)
116 struct fmp_request req
;
117 struct crypto_diskcipher
*dtfm
;
119 struct bio
*bio
= get_bio(hba
, lrbp
);
124 dtfm
= crypto_diskcipher_get(bio
);
125 if (unlikely(IS_ERR(dtfm
))) {
126 pr_warn("%s: fails to get crypt\n", __func__
);
129 #ifdef CONFIG_CRYPTO_DISKCIPHER_DUN
131 iv
= bio_dun(bio
) + page_index
;
133 iv
= bio
->bi_iter
.bi_sector
+ (sector_t
) sector_offset
;
135 iv
= bio
->bi_iter
.bi_sector
+ (sector_t
) sector_offset
;
138 req
.table
= (void *)&lrbp
->ucd_prdt_ptr
[index
];
139 req
.cmdq_enabled
= 0;
141 req
.ivsize
= sizeof(iv
);
142 #ifdef CONFIG_EXYNOS_FMP_FIPS
143 /* check fips flag. use fmp without diskcipher */
145 if (exynos_fmp_crypt((void *)dtfm
, &req
))
146 pr_warn("%s: fails to test fips\n", __func__
);
150 if (crypto_diskcipher_set_crypt(dtfm
, &req
)) {
151 pr_warn("%s: fails to set crypt\n", __func__
);
157 exynos_fmp_bypass(&lrbp
->ucd_prdt_ptr
[index
], 0);
161 int exynos_ufs_fmp_clear(struct ufs_hba
*hba
, struct ufshcd_lrb
*lrbp
)
164 int sg_segments
, idx
;
165 struct scatterlist
*sg
;
166 struct ufshcd_sg_entry
*prd_table
;
167 struct crypto_diskcipher
*dtfm
;
168 struct fmp_crypto_info
*ci
;
169 struct fmp_request req
;
170 struct bio
*bio
= get_bio(hba
, lrbp
);
175 sg_segments
= scsi_sg_count(lrbp
->cmd
);
179 dtfm
= crypto_diskcipher_get(bio
);
181 #ifdef CONFIG_EXYNOS_FMP_FIPS
182 /* check fips flag. use fmp without diskcipher */
185 (struct ufshcd_sg_entry
*)lrbp
->ucd_prdt_ptr
;
186 scsi_for_each_sg(lrbp
->cmd
, sg
, sg_segments
, idx
) {
187 req
.table
= (void *)&prd_table
[idx
];
188 ret
= exynos_fmp_clear((void *)dtfm
, &req
);
190 pr_warn("%s: fails to clear fips\n",
198 /* clear key on descrptor */
199 ci
= crypto_tfm_ctx(crypto_diskcipher_tfm(dtfm
));
200 if (ci
&& (ci
->enc_mode
== EXYNOS_FMP_FILE_ENC
)) {
202 (struct ufshcd_sg_entry
*)lrbp
->ucd_prdt_ptr
;
203 scsi_for_each_sg(lrbp
->cmd
, sg
, sg_segments
, idx
) {
204 req
.table
= (void *)&prd_table
[idx
];
205 ret
= crypto_diskcipher_clear_crypt(dtfm
, &req
);
207 pr_err("%s: fail to clear desc (%d)\n",