summaryrefslogtreecommitdiff
path: root/drivers/ufs/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ufs/host')
-rw-r--r--drivers/ufs/host/cdns-pltfrm.c8
-rw-r--r--drivers/ufs/host/tc-dwc-g210-pci.c8
-rw-r--r--drivers/ufs/host/tc-dwc-g210-pltfrm.c7
-rw-r--r--drivers/ufs/host/ti-j721e-ufs.c2
-rw-r--r--drivers/ufs/host/ufs-exynos.c535
-rw-r--r--drivers/ufs/host/ufs-exynos.h26
-rw-r--r--drivers/ufs/host/ufs-hisi.c6
-rw-r--r--drivers/ufs/host/ufs-mediatek-sip.h94
-rw-r--r--drivers/ufs/host/ufs-mediatek.c238
-rw-r--r--drivers/ufs/host/ufs-mediatek.h96
-rw-r--r--drivers/ufs/host/ufs-qcom.c233
-rw-r--r--drivers/ufs/host/ufs-qcom.h17
-rw-r--r--drivers/ufs/host/ufs-renesas.c15
-rw-r--r--drivers/ufs/host/ufs-sprd.c7
-rw-r--r--drivers/ufs/host/ufshcd-pci.c59
-rw-r--r--drivers/ufs/host/ufshcd-pltfrm.c56
-rw-r--r--drivers/ufs/host/ufshcd-pltfrm.h1
17 files changed, 1080 insertions, 328 deletions
diff --git a/drivers/ufs/host/cdns-pltfrm.c b/drivers/ufs/host/cdns-pltfrm.c
index bb30267da471..e793e3538c48 100644
--- a/drivers/ufs/host/cdns-pltfrm.c
+++ b/drivers/ufs/host/cdns-pltfrm.c
@@ -136,7 +136,7 @@ static int cdns_ufs_set_hclkdiv(struct ufs_hba *hba)
* Make sure the register was updated,
* UniPro layer will not work with an incorrect value.
*/
- mb();
+ ufshcd_readl(hba, CDNS_UFS_REG_HCLKDIV);
return 0;
}
@@ -307,9 +307,7 @@ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev)
*/
static void cdns_ufs_pltfrm_remove(struct platform_device *pdev)
{
- struct ufs_hba *hba = platform_get_drvdata(pdev);
-
- ufshcd_remove(hba);
+ ufshcd_pltfrm_remove(pdev);
}
static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
@@ -321,7 +319,7 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
static struct platform_driver cdns_ufs_pltfrm_driver = {
.probe = cdns_ufs_pltfrm_probe,
- .remove_new = cdns_ufs_pltfrm_remove,
+ .remove = cdns_ufs_pltfrm_remove,
.driver = {
.name = "cdns-ufshcd",
.pm = &cdns_ufs_dev_pm_ops,
diff --git a/drivers/ufs/host/tc-dwc-g210-pci.c b/drivers/ufs/host/tc-dwc-g210-pci.c
index 876781fd6861..0167d8bef71a 100644
--- a/drivers/ufs/host/tc-dwc-g210-pci.c
+++ b/drivers/ufs/host/tc-dwc-g210-pci.c
@@ -80,14 +80,12 @@ tc_dwc_g210_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_master(pdev);
- err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD);
- if (err < 0) {
+ mmio_base = pcim_iomap_region(pdev, 0, UFSHCD);
+ if (IS_ERR(mmio_base)) {
dev_err(&pdev->dev, "request and iomap failed\n");
- return err;
+ return PTR_ERR(mmio_base);
}
- mmio_base = pcim_iomap_table(pdev)[0];
-
err = ufshcd_alloc_host(&pdev->dev, &hba);
if (err) {
dev_err(&pdev->dev, "Allocation failed\n");
diff --git a/drivers/ufs/host/tc-dwc-g210-pltfrm.c b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
index a3877592604d..454ac88c357d 100644
--- a/drivers/ufs/host/tc-dwc-g210-pltfrm.c
+++ b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
@@ -76,10 +76,7 @@ static int tc_dwc_g210_pltfm_probe(struct platform_device *pdev)
*/
static void tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
{
- struct ufs_hba *hba = platform_get_drvdata(pdev);
-
- pm_runtime_get_sync(&(pdev)->dev);
- ufshcd_remove(hba);
+ ufshcd_pltfrm_remove(pdev);
}
static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
@@ -89,7 +86,7 @@ static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
static struct platform_driver tc_dwc_g210_pltfm_driver = {
.probe = tc_dwc_g210_pltfm_probe,
- .remove_new = tc_dwc_g210_pltfm_remove,
+ .remove = tc_dwc_g210_pltfm_remove,
.driver = {
.name = "tc-dwc-g210-pltfm",
.pm = &tc_dwc_g210_pltfm_pm_ops,
diff --git a/drivers/ufs/host/ti-j721e-ufs.c b/drivers/ufs/host/ti-j721e-ufs.c
index 250c22df000d..21214e5d5896 100644
--- a/drivers/ufs/host/ti-j721e-ufs.c
+++ b/drivers/ufs/host/ti-j721e-ufs.c
@@ -83,7 +83,7 @@ MODULE_DEVICE_TABLE(of, ti_j721e_ufs_of_match);
static struct platform_driver ti_j721e_ufs_driver = {
.probe = ti_j721e_ufs_probe,
- .remove_new = ti_j721e_ufs_remove,
+ .remove = ti_j721e_ufs_remove,
.driver = {
.name = "ti-j721e-ufs",
.of_match_table = ti_j721e_ufs_of_match,
diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c
index 734d40f99e31..13dd5dfc03eb 100644
--- a/drivers/ufs/host/ufs-exynos.c
+++ b/drivers/ufs/host/ufs-exynos.c
@@ -8,6 +8,9 @@
*
*/
+#include <linux/unaligned.h>
+#include <crypto/aes.h>
+#include <linux/arm-smccc.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
@@ -25,12 +28,13 @@
#include "ufs-exynos.h"
+#define DATA_UNIT_SIZE 4096
+
/*
* Exynos's Vendor specific registers for UFSHCI
*/
#define HCI_TXPRDT_ENTRY_SIZE 0x00
#define PRDT_PREFECT_EN BIT(31)
-#define PRDT_SET_SIZE(x) ((x) & 0x1F)
#define HCI_RXPRDT_ENTRY_SIZE 0x04
#define HCI_1US_TO_CNT_VAL 0x0C
#define CNT_VAL_1US_MASK 0x3FF
@@ -44,12 +48,16 @@
#define HCI_UNIPRO_APB_CLK_CTRL 0x68
#define UNIPRO_APB_CLK(v, x) (((v) & ~0xF) | ((x) & 0xF))
#define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C
+#define WLU_EN BIT(31)
+#define WLU_BURST_LEN(x) ((x) << 27 | ((x) & 0xF))
#define HCI_GPIO_OUT 0x70
#define HCI_ERR_EN_PA_LAYER 0x78
#define HCI_ERR_EN_DL_LAYER 0x7C
#define HCI_ERR_EN_N_LAYER 0x80
#define HCI_ERR_EN_T_LAYER 0x84
#define HCI_ERR_EN_DME_LAYER 0x88
+#define HCI_V2P1_CTRL 0x8C
+#define IA_TICK_SEL BIT(16)
#define HCI_CLKSTOP_CTRL 0xB0
#define REFCLKOUT_STOP BIT(4)
#define MPHY_APBCLK_STOP BIT(3)
@@ -59,6 +67,7 @@
#define CLK_STOP_MASK (REFCLKOUT_STOP | REFCLK_STOP |\
UNIPRO_MCLK_STOP | MPHY_APBCLK_STOP|\
UNIPRO_PCLK_STOP)
+/* HCI_MISC is also known as HCI_FORCE_HCS */
#define HCI_MISC 0xB4
#define REFCLK_CTRL_EN BIT(7)
#define UNIPRO_PCLK_CTRL_EN BIT(6)
@@ -67,6 +76,10 @@
#define CLK_CTRL_EN_MASK (REFCLK_CTRL_EN |\
UNIPRO_PCLK_CTRL_EN |\
UNIPRO_MCLK_CTRL_EN)
+
+#define HCI_IOP_ACG_DISABLE 0x100
+#define HCI_IOP_ACG_DISABLE_EN BIT(0)
+
/* Device fatal error */
#define DFES_ERR_EN BIT(31)
#define DFES_DEF_L2_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
@@ -136,6 +149,9 @@ enum {
/*
* UNIPRO registers
*/
+#define UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER0 0x7888
+#define UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER1 0x788c
+#define UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER2 0x7890
#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0 0x78B8
#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1 0x78BC
#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2 0x78C0
@@ -188,15 +204,8 @@ static inline void exynos_ufs_ungate_clks(struct exynos_ufs *ufs)
exynos_ufs_ctrl_clkstop(ufs, false);
}
-static int exynos7_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs)
-{
- return 0;
-}
-
-static int exynosauto_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs)
+static int exynos_ufs_shareability(struct exynos_ufs *ufs)
{
- struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
-
/* IO Coherency setting */
if (ufs->sysreg) {
return regmap_update_bits(ufs->sysreg,
@@ -204,11 +213,32 @@ static int exynosauto_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs)
UFS_SHARABLE, UFS_SHARABLE);
}
- attr->tx_dif_p_nsec = 3200000;
-
return 0;
}
+static int gs101_ufs_drv_init(struct exynos_ufs *ufs)
+{
+ struct ufs_hba *hba = ufs->hba;
+ u32 reg;
+
+ /* Enable WriteBooster */
+ hba->caps |= UFSHCD_CAP_WB_EN;
+
+ /* Enable clock gating and hibern8 */
+ hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
+
+ /* set ACG to be controlled by UFS_ACG_DISABLE */
+ reg = hci_readl(ufs, HCI_IOP_ACG_DISABLE);
+ hci_writel(ufs, reg & (~HCI_IOP_ACG_DISABLE_EN), HCI_IOP_ACG_DISABLE);
+
+ return exynos_ufs_shareability(ufs);
+}
+
+static int exynosauto_ufs_drv_init(struct exynos_ufs *ufs)
+{
+ return exynos_ufs_shareability(ufs);
+}
+
static int exynosauto_ufs_post_hce_enable(struct exynos_ufs *ufs)
{
struct ufs_hba *hba = ufs->hba;
@@ -306,8 +336,9 @@ static int exynosauto_ufs_post_pwr_change(struct exynos_ufs *ufs,
static int exynos7_ufs_pre_link(struct exynos_ufs *ufs)
{
+ struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
+ u32 val = attr->pa_dbg_opt_suite1_val;
struct ufs_hba *hba = ufs->hba;
- u32 val = ufs->drv_data->uic_attr->pa_dbg_option_suite;
int i;
exynos_ufs_enable_ov_tm(hba);
@@ -324,12 +355,13 @@ static int exynos7_ufs_pre_link(struct exynos_ufs *ufs)
UIC_ARG_MIB_SEL(TX_HIBERN8_CONTROL, i), 0x0);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_TXPHY_CFGUPDT), 0x1);
udelay(1);
- ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE), val | (1 << 12));
+ ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_opt_suite1_off),
+ val | (1 << 12));
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_SKIP_RESET_PHY), 0x1);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_SKIP_LINE_RESET), 0x1);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_LINE_RESET_REQ), 0x1);
udelay(1600);
- ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE), val);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_opt_suite1_off), val);
return 0;
}
@@ -534,6 +566,9 @@ static void exynos_ufs_specify_phy_time_attr(struct exynos_ufs *ufs)
struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
struct ufs_phy_time_cfg *t_cfg = &ufs->t_cfg;
+ if (ufs->opts & EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR)
+ return;
+
t_cfg->tx_linereset_p =
exynos_ufs_calc_time_cntr(ufs, attr->tx_dif_p_nsec);
t_cfg->tx_linereset_n =
@@ -712,6 +747,9 @@ static void exynos_ufs_config_smu(struct exynos_ufs *ufs)
{
u32 reg, val;
+ if (ufs->opts & EXYNOS_UFS_OPT_UFSPR_SECURE)
+ return;
+
exynos_ufs_disable_auto_ctrl_hcc_save(ufs, &val);
/* make encryption disabled by default */
@@ -759,6 +797,21 @@ static void exynos_ufs_config_sync_pattern_mask(struct exynos_ufs *ufs,
exynos_ufs_disable_ov_tm(hba);
}
+#define UFS_HW_VER_MAJOR_MASK GENMASK(15, 8)
+
+static u32 exynos_ufs_get_hs_gear(struct ufs_hba *hba)
+{
+ u8 major;
+
+ major = FIELD_GET(UFS_HW_VER_MAJOR_MASK, hba->ufs_version);
+
+ if (major >= 3)
+ return UFS_HS_G4;
+
+ /* Default is HS-G3 */
+ return UFS_HS_G3;
+}
+
static int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba,
struct ufs_pa_layer_attr *dev_max_params,
struct ufs_pa_layer_attr *dev_req_params)
@@ -776,6 +829,10 @@ static int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba,
ufshcd_init_host_params(&host_params);
+ /* This driver only support symmetric gear setting e.g. hs_tx_gear == hs_rx_gear */
+ host_params.hs_tx_gear = exynos_ufs_get_hs_gear(hba);
+ host_params.hs_rx_gear = exynos_ufs_get_hs_gear(hba);
+
ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params);
if (ret) {
pr_err("%s: failed to determine capabilities\n", __func__);
@@ -921,14 +978,23 @@ out_exit_phy:
static void exynos_ufs_config_unipro(struct exynos_ufs *ufs)
{
+ struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
struct ufs_hba *hba = ufs->hba;
- ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_CLK_PERIOD),
- DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+ if (attr->pa_dbg_clk_period_off)
+ ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_clk_period_off),
+ DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTRAILINGCLOCKS),
ufs->drv_data->uic_attr->tx_trailingclks);
- ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE),
- ufs->drv_data->uic_attr->pa_dbg_option_suite);
+
+ if (attr->pa_dbg_opt_suite1_off)
+ ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_opt_suite1_off),
+ attr->pa_dbg_opt_suite1_val);
+
+ if (attr->pa_dbg_opt_suite2_off)
+ ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_opt_suite2_off),
+ attr->pa_dbg_opt_suite2_val);
}
static void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32 errs, u8 index)
@@ -1005,6 +1071,13 @@ static void exynos_ufs_fit_aggr_timeout(struct exynos_ufs *ufs)
{
u32 val;
+ /* Select function clock (mclk) for timer tick */
+ if (ufs->opts & EXYNOS_UFS_OPT_TIMER_TICK_SELECT) {
+ val = hci_readl(ufs, HCI_V2P1_CTRL);
+ val |= IA_TICK_SEL;
+ hci_writel(ufs, val, HCI_V2P1_CTRL);
+ }
+
val = exynos_ufs_calc_time_cntr(ufs, IATOVAL_NSEC / CNTR_DIV_VAL);
hci_writel(ufs, val & CNT_VAL_1US_MASK, HCI_1US_TO_CNT_VAL);
}
@@ -1019,8 +1092,8 @@ static int exynos_ufs_post_link(struct ufs_hba *hba)
exynos_ufs_fit_aggr_timeout(ufs);
hci_writel(ufs, 0xa, HCI_DATA_REORDER);
- hci_writel(ufs, PRDT_SET_SIZE(12), HCI_TXPRDT_ENTRY_SIZE);
- hci_writel(ufs, PRDT_SET_SIZE(12), HCI_RXPRDT_ENTRY_SIZE);
+ hci_writel(ufs, ilog2(DATA_UNIT_SIZE), HCI_TXPRDT_ENTRY_SIZE);
+ hci_writel(ufs, ilog2(DATA_UNIT_SIZE), HCI_RXPRDT_ENTRY_SIZE);
hci_writel(ufs, (1 << hba->nutrs) - 1, HCI_UTRL_NEXUS_TYPE);
hci_writel(ufs, (1 << hba->nutmrs) - 1, HCI_UTMRL_NEXUS_TYPE);
hci_writel(ufs, 0xf, HCI_AXIDMA_RWDATA_BURST_LEN);
@@ -1127,6 +1200,230 @@ static inline void exynos_ufs_priv_init(struct ufs_hba *hba,
hba->quirks = ufs->drv_data->quirks;
}
+#ifdef CONFIG_SCSI_UFS_CRYPTO
+
+/*
+ * Support for Flash Memory Protector (FMP), which is the inline encryption
+ * hardware on Exynos and Exynos-based SoCs. The interface to this hardware is
+ * not compatible with the standard UFS crypto. It requires that encryption be
+ * configured in the PRDT using a nonstandard extension.
+ */
+
+enum fmp_crypto_algo_mode {
+ FMP_BYPASS_MODE = 0,
+ FMP_ALGO_MODE_AES_CBC = 1,
+ FMP_ALGO_MODE_AES_XTS = 2,
+};
+enum fmp_crypto_key_length {
+ FMP_KEYLEN_256BIT = 1,
+};
+
+/**
+ * struct fmp_sg_entry - nonstandard format of PRDT entries when FMP is enabled
+ *
+ * @base: The standard PRDT entry, but with nonstandard bitfields in the high
+ * bits of the 'size' field, i.e. the last 32-bit word. When these
+ * nonstandard bitfields are zero, the data segment won't be encrypted or
+ * decrypted. Otherwise they specify the algorithm and key length with
+ * which the data segment will be encrypted or decrypted.
+ * @file_iv: The initialization vector (IV) with all bytes reversed
+ * @file_enckey: The first half of the AES-XTS key with all bytes reserved
+ * @file_twkey: The second half of the AES-XTS key with all bytes reserved
+ * @disk_iv: Unused
+ * @reserved: Unused
+ */
+struct fmp_sg_entry {
+ struct ufshcd_sg_entry base;
+ __be64 file_iv[2];
+ __be64 file_enckey[4];
+ __be64 file_twkey[4];
+ __be64 disk_iv[2];
+ __be64 reserved[2];
+};
+
+#define SMC_CMD_FMP_SECURITY \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, 0x1810)
+#define SMC_CMD_SMU \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, 0x1850)
+#define SMC_CMD_FMP_SMU_RESUME \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, 0x1860)
+#define SMU_EMBEDDED 0
+#define SMU_INIT 0
+#define CFG_DESCTYPE_3 3
+
+static void exynos_ufs_fmp_init(struct ufs_hba *hba, struct exynos_ufs *ufs)
+{
+ struct blk_crypto_profile *profile = &hba->crypto_profile;
+ struct arm_smccc_res res;
+ int err;
+
+ /*
+ * Check for the standard crypto support bit, since it's available even
+ * though the rest of the interface to FMP is nonstandard.
+ *
+ * This check should have the effect of preventing the driver from
+ * trying to use FMP on old Exynos SoCs that don't have FMP.
+ */
+ if (!(ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES) &
+ MASK_CRYPTO_SUPPORT))
+ return;
+
+ /*
+ * The below sequence of SMC calls to enable FMP can be found in the
+ * downstream driver source for gs101 and other Exynos-based SoCs. It
+ * is the only way to enable FMP that works on SoCs such as gs101 that
+ * don't make the FMP registers accessible to Linux. It probably works
+ * on other Exynos-based SoCs too, and might even still be the only way
+ * that works. But this hasn't been properly tested, and this code is
+ * mutually exclusive with exynos_ufs_config_smu(). So for now only
+ * enable FMP support on SoCs with EXYNOS_UFS_OPT_UFSPR_SECURE.
+ */
+ if (!(ufs->opts & EXYNOS_UFS_OPT_UFSPR_SECURE))
+ return;
+
+ /*
+ * This call (which sets DESCTYPE to 0x3 in the FMPSECURITY0 register)
+ * is needed to make the hardware use the larger PRDT entry size.
+ */
+ BUILD_BUG_ON(sizeof(struct fmp_sg_entry) != 128);
+ arm_smccc_smc(SMC_CMD_FMP_SECURITY, 0, SMU_EMBEDDED, CFG_DESCTYPE_3,
+ 0, 0, 0, 0, &res);
+ if (res.a0) {
+ dev_warn(hba->dev,
+ "SMC_CMD_FMP_SECURITY failed on init: %ld. Disabling FMP support.\n",
+ res.a0);
+ return;
+ }
+ ufshcd_set_sg_entry_size(hba, sizeof(struct fmp_sg_entry));
+
+ /*
+ * This is needed to initialize FMP. Without it, errors occur when
+ * inline encryption is used.
+ */
+ arm_smccc_smc(SMC_CMD_SMU, SMU_INIT, SMU_EMBEDDED, 0, 0, 0, 0, 0, &res);
+ if (res.a0) {
+ dev_err(hba->dev,
+ "SMC_CMD_SMU(SMU_INIT) failed: %ld. Disabling FMP support.\n",
+ res.a0);
+ return;
+ }
+
+ /* Advertise crypto capabilities to the block layer. */
+ err = devm_blk_crypto_profile_init(hba->dev, profile, 0);
+ if (err) {
+ /* Only ENOMEM should be possible here. */
+ dev_err(hba->dev, "Failed to initialize crypto profile: %d\n",
+ err);
+ return;
+ }
+ profile->max_dun_bytes_supported = AES_BLOCK_SIZE;
+ profile->dev = hba->dev;
+ profile->modes_supported[BLK_ENCRYPTION_MODE_AES_256_XTS] =
+ DATA_UNIT_SIZE;
+
+ /* Advertise crypto support to ufshcd-core. */
+ hba->caps |= UFSHCD_CAP_CRYPTO;
+
+ /* Advertise crypto quirks to ufshcd-core. */
+ hba->quirks |= UFSHCD_QUIRK_CUSTOM_CRYPTO_PROFILE |
+ UFSHCD_QUIRK_BROKEN_CRYPTO_ENABLE |
+ UFSHCD_QUIRK_KEYS_IN_PRDT;
+
+}
+
+static void exynos_ufs_fmp_resume(struct ufs_hba *hba)
+{
+ struct arm_smccc_res res;
+
+ if (!(hba->caps & UFSHCD_CAP_CRYPTO))
+ return;
+
+ arm_smccc_smc(SMC_CMD_FMP_SECURITY, 0, SMU_EMBEDDED, CFG_DESCTYPE_3,
+ 0, 0, 0, 0, &res);
+ if (res.a0)
+ dev_err(hba->dev,
+ "SMC_CMD_FMP_SECURITY failed on resume: %ld\n", res.a0);
+
+ arm_smccc_smc(SMC_CMD_FMP_SMU_RESUME, 0, SMU_EMBEDDED, 0, 0, 0, 0, 0,
+ &res);
+ if (res.a0)
+ dev_err(hba->dev,
+ "SMC_CMD_FMP_SMU_RESUME failed: %ld\n", res.a0);
+}
+
+static inline __be64 fmp_key_word(const u8 *key, int j)
+{
+ return cpu_to_be64(get_unaligned_le64(
+ key + AES_KEYSIZE_256 - (j + 1) * sizeof(u64)));
+}
+
+/* Fill the PRDT for a request according to the given encryption context. */
+static int exynos_ufs_fmp_fill_prdt(struct ufs_hba *hba,
+ const struct bio_crypt_ctx *crypt_ctx,
+ void *prdt, unsigned int num_segments)
+{
+ struct fmp_sg_entry *fmp_prdt = prdt;
+ const u8 *enckey = crypt_ctx->bc_key->raw;
+ const u8 *twkey = enckey + AES_KEYSIZE_256;
+ u64 dun_lo = crypt_ctx->bc_dun[0];
+ u64 dun_hi = crypt_ctx->bc_dun[1];
+ unsigned int i;
+
+ /* If FMP wasn't enabled, we shouldn't get any encrypted requests. */
+ if (WARN_ON_ONCE(!(hba->caps & UFSHCD_CAP_CRYPTO)))
+ return -EIO;
+
+ /* Configure FMP on each segment of the request. */
+ for (i = 0; i < num_segments; i++) {
+ struct fmp_sg_entry *prd = &fmp_prdt[i];
+ int j;
+
+ /* Each segment must be exactly one data unit. */
+ if (prd->base.size != cpu_to_le32(DATA_UNIT_SIZE - 1)) {
+ dev_err(hba->dev,
+ "data segment is misaligned for FMP\n");
+ return -EIO;
+ }
+
+ /* Set the algorithm and key length. */
+ prd->base.size |= cpu_to_le32((FMP_ALGO_MODE_AES_XTS << 28) |
+ (FMP_KEYLEN_256BIT << 26));
+
+ /* Set the IV. */
+ prd->file_iv[0] = cpu_to_be64(dun_hi);
+ prd->file_iv[1] = cpu_to_be64(dun_lo);
+
+ /* Set the key. */
+ for (j = 0; j < AES_KEYSIZE_256 / sizeof(u64); j++) {
+ prd->file_enckey[j] = fmp_key_word(enckey, j);
+ prd->file_twkey[j] = fmp_key_word(twkey, j);
+ }
+
+ /* Increment the data unit number. */
+ dun_lo++;
+ if (dun_lo == 0)
+ dun_hi++;
+ }
+ return 0;
+}
+
+#else /* CONFIG_SCSI_UFS_CRYPTO */
+
+static void exynos_ufs_fmp_init(struct ufs_hba *hba, struct exynos_ufs *ufs)
+{
+}
+
+static void exynos_ufs_fmp_resume(struct ufs_hba *hba)
+{
+}
+
+#define exynos_ufs_fmp_fill_prdt NULL
+
+#endif /* !CONFIG_SCSI_UFS_CRYPTO */
+
static int exynos_ufs_init(struct ufs_hba *hba)
{
struct device *dev = hba->dev;
@@ -1174,8 +1471,10 @@ static int exynos_ufs_init(struct ufs_hba *hba)
exynos_ufs_priv_init(hba, ufs);
+ exynos_ufs_fmp_init(hba, ufs);
+
if (ufs->drv_data->drv_init) {
- ret = ufs->drv_data->drv_init(dev, ufs);
+ ret = ufs->drv_data->drv_init(ufs);
if (ret) {
dev_err(dev, "failed to init drv-data\n");
goto out;
@@ -1186,7 +1485,10 @@ static int exynos_ufs_init(struct ufs_hba *hba)
if (ret)
goto out;
exynos_ufs_specify_phy_time_attr(ufs);
+
exynos_ufs_config_smu(ufs);
+
+ hba->host->dma_alignment = DATA_UNIT_SIZE - 1;
return 0;
out:
@@ -1227,12 +1529,12 @@ static void exynos_ufs_dev_hw_reset(struct ufs_hba *hba)
hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
}
-static void exynos_ufs_pre_hibern8(struct ufs_hba *hba, u8 enter)
+static void exynos_ufs_pre_hibern8(struct ufs_hba *hba, enum uic_cmd_dme cmd)
{
struct exynos_ufs *ufs = ufshcd_get_variant(hba);
struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
- if (!enter) {
+ if (cmd == UIC_CMD_DME_HIBER_EXIT) {
if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL)
exynos_ufs_disable_auto_ctrl_hcc(ufs);
exynos_ufs_ungate_clks(ufs);
@@ -1260,30 +1562,11 @@ static void exynos_ufs_pre_hibern8(struct ufs_hba *hba, u8 enter)
}
}
-static void exynos_ufs_post_hibern8(struct ufs_hba *hba, u8 enter)
+static void exynos_ufs_post_hibern8(struct ufs_hba *hba, enum uic_cmd_dme cmd)
{
struct exynos_ufs *ufs = ufshcd_get_variant(hba);
- if (!enter) {
- u32 cur_mode = 0;
- u32 pwrmode;
-
- if (ufshcd_is_hs_mode(&ufs->dev_req_params))
- pwrmode = FAST_MODE;
- else
- pwrmode = SLOW_MODE;
-
- ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PWRMODE), &cur_mode);
- if (cur_mode != (pwrmode << 4 | pwrmode)) {
- dev_warn(hba->dev, "%s: power mode change\n", __func__);
- hba->pwr_info.pwr_rx = (cur_mode >> 4) & 0xf;
- hba->pwr_info.pwr_tx = cur_mode & 0xf;
- ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info);
- }
-
- if (!(ufs->opts & EXYNOS_UFS_OPT_SKIP_CONNECTION_ESTAB))
- exynos_ufs_establish_connt(ufs);
- } else {
+ if (cmd == UIC_CMD_DME_HIBER_ENTER) {
ufs->entry_hibern8_t = ktime_get();
exynos_ufs_gate_clks(ufs);
if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL)
@@ -1305,7 +1588,7 @@ static int exynos_ufs_hce_enable_notify(struct ufs_hba *hba,
* (ufshcd_async_scan()). Note: this callback may also be called
* from other functions than ufshcd_init().
*/
- hba->host->max_segment_size = SZ_4K;
+ hba->host->max_segment_size = DATA_UNIT_SIZE;
if (ufs->drv_data->pre_hce_enable) {
ret = ufs->drv_data->pre_hce_enable(ufs);
@@ -1370,15 +1653,15 @@ static int exynos_ufs_pwr_change_notify(struct ufs_hba *hba,
}
static void exynos_ufs_hibern8_notify(struct ufs_hba *hba,
- enum uic_cmd_dme enter,
+ enum uic_cmd_dme cmd,
enum ufs_notify_change_status notify)
{
switch ((u8)notify) {
case PRE_CHANGE:
- exynos_ufs_pre_hibern8(hba, enter);
+ exynos_ufs_pre_hibern8(hba, cmd);
break;
case POST_CHANGE:
- exynos_ufs_post_hibern8(hba, enter);
+ exynos_ufs_post_hibern8(hba, cmd);
break;
}
}
@@ -1405,7 +1688,7 @@ static int exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
phy_power_on(ufs->phy);
exynos_ufs_config_smu(ufs);
-
+ exynos_ufs_fmp_resume(hba);
return 0;
}
@@ -1475,10 +1758,11 @@ static int exynosauto_ufs_vh_init(struct ufs_hba *hba)
static int fsd_ufs_pre_link(struct exynos_ufs *ufs)
{
- int i;
+ struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
struct ufs_hba *hba = ufs->hba;
+ int i;
- ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_CLK_PERIOD),
+ ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_clk_period_off),
DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
ufshcd_dme_set(hba, UIC_ARG_MIB(0x201), 0x12);
ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40);
@@ -1502,7 +1786,9 @@ static int fsd_ufs_pre_link(struct exynos_ufs *ufs)
ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_AUTOMODE_THLD), 0x4E20);
- ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE), 0x2e820183);
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_opt_suite1_off),
+ 0x2e820183);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0);
exynos_ufs_establish_connt(ufs);
@@ -1510,11 +1796,6 @@ static int fsd_ufs_pre_link(struct exynos_ufs *ufs)
return 0;
}
-static void exynos_ufs_config_scsi_dev(struct scsi_device *sdev)
-{
- blk_queue_update_dma_alignment(sdev->request_queue, SZ_4K - 1);
-}
-
static int fsd_ufs_post_link(struct exynos_ufs *ufs)
{
int i;
@@ -1571,6 +1852,102 @@ static int fsd_ufs_pre_pwr_change(struct exynos_ufs *ufs,
return 0;
}
+static inline u32 get_mclk_period_unipro_18(struct exynos_ufs *ufs)
+{
+ return (16 * 1000 * 1000000UL / ufs->mclk_rate);
+}
+
+static int gs101_ufs_pre_link(struct exynos_ufs *ufs)
+{
+ struct ufs_hba *hba = ufs->hba;
+ int i;
+ u32 tx_line_reset_period, rx_line_reset_period;
+
+ rx_line_reset_period = (RX_LINE_RESET_TIME * ufs->mclk_rate)
+ / NSEC_PER_MSEC;
+ tx_line_reset_period = (TX_LINE_RESET_TIME * ufs->mclk_rate)
+ / NSEC_PER_MSEC;
+
+ unipro_writel(ufs, get_mclk_period_unipro_18(ufs), COMP_CLK_PERIOD);
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40);
+
+ for_each_ufs_rx_lane(ufs, i) {
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, i),
+ DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, i), 0x0);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE2, i),
+ (rx_line_reset_period >> 16) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE1, i),
+ (rx_line_reset_period >> 8) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE0, i),
+ (rx_line_reset_period) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x2f, i), 0x69);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x84, i), 0x1);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x25, i), 0xf6);
+ }
+
+ for_each_ufs_tx_lane(ufs, i) {
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, i),
+ DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, i),
+ 0x02);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, i),
+ (tx_line_reset_period >> 16) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, i),
+ (tx_line_reset_period >> 8) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE0, i),
+ (tx_line_reset_period) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x04, i), 1);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x7F, i), 0);
+ }
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(N_DEVICEID), 0x0);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(N_DEVICEID_VALID), 0x1);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(T_PEERDEVICEID), 0x1);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(T_CONNECTIONSTATE), CPORT_CONNECTED);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0xA006), 0x8000);
+
+ return 0;
+}
+
+static int gs101_ufs_post_link(struct exynos_ufs *ufs)
+{
+ struct ufs_hba *hba = ufs->hba;
+
+ /*
+ * Enable Write Line Unique. This field has to be 0x3
+ * to support Write Line Unique transaction on gs101.
+ */
+ hci_writel(ufs, WLU_EN | WLU_BURST_LEN(3), HCI_AXIDMA_RWDATA_BURST_LEN);
+
+ exynos_ufs_enable_dbg_mode(hba);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_SAVECONFIGTIME), 0x3e8);
+ exynos_ufs_disable_dbg_mode(hba);
+
+ return 0;
+}
+
+static int gs101_ufs_pre_pwr_change(struct exynos_ufs *ufs,
+ struct ufs_pa_layer_attr *pwr)
+{
+ struct ufs_hba *hba = ufs->hba;
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000);
+ unipro_writel(ufs, 8064, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER0);
+ unipro_writel(ufs, 28224, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER1);
+ unipro_writel(ufs, 20160, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER2);
+ unipro_writel(ufs, 12000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0);
+ unipro_writel(ufs, 32000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1);
+ unipro_writel(ufs, 16000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2);
+
+ return 0;
+}
+
static const struct ufs_hba_variant_ops ufs_hba_exynos_ops = {
.name = "exynos_ufs",
.init = exynos_ufs_init,
@@ -1583,7 +1960,7 @@ static const struct ufs_hba_variant_ops ufs_hba_exynos_ops = {
.hibern8_notify = exynos_ufs_hibern8_notify,
.suspend = exynos_ufs_suspend,
.resume = exynos_ufs_resume,
- .config_scsi_dev = exynos_ufs_config_scsi_dev,
+ .fill_crypto_prdt = exynos_ufs_fmp_fill_prdt,
};
static struct ufs_hba_variant_ops ufs_hba_exynosauto_vh_ops = {
@@ -1615,8 +1992,7 @@ static void exynos_ufs_remove(struct platform_device *pdev)
struct ufs_hba *hba = platform_get_drvdata(pdev);
struct exynos_ufs *ufs = ufshcd_get_variant(hba);
- pm_runtime_get_sync(&(pdev)->dev);
- ufshcd_remove(hba);
+ ufshcd_pltfrm_remove(pdev);
phy_power_off(ufs->phy);
phy_exit(ufs->phy);
@@ -1644,7 +2020,9 @@ static struct exynos_ufs_uic_attr exynos7_uic_attr = {
.rx_hs_g1_prep_sync_len_cap = PREP_LEN(0xf),
.rx_hs_g2_prep_sync_len_cap = PREP_LEN(0xf),
.rx_hs_g3_prep_sync_len_cap = PREP_LEN(0xf),
- .pa_dbg_option_suite = 0x30103,
+ .pa_dbg_clk_period_off = PA_DBG_CLK_PERIOD,
+ .pa_dbg_opt_suite1_val = 0x30103,
+ .pa_dbg_opt_suite1_off = PA_DBG_OPTION_SUITE,
};
static const struct exynos_ufs_drv_data exynosauto_ufs_drvs = {
@@ -1689,13 +2067,20 @@ static const struct exynos_ufs_drv_data exynos_ufs_drvs = {
EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX |
EXYNOS_UFS_OPT_SKIP_CONNECTION_ESTAB |
EXYNOS_UFS_OPT_USE_SW_HIBERN8_TIMER,
- .drv_init = exynos7_ufs_drv_init,
.pre_link = exynos7_ufs_pre_link,
.post_link = exynos7_ufs_post_link,
.pre_pwr_change = exynos7_ufs_pre_pwr_change,
.post_pwr_change = exynos7_ufs_post_pwr_change,
};
+static struct exynos_ufs_uic_attr gs101_uic_attr = {
+ .tx_trailingclks = 0xff,
+ .pa_dbg_opt_suite1_val = 0x90913C1C,
+ .pa_dbg_opt_suite1_off = PA_GS101_DBG_OPTION_SUITE1,
+ .pa_dbg_opt_suite2_val = 0xE01C115F,
+ .pa_dbg_opt_suite2_off = PA_GS101_DBG_OPTION_SUITE2,
+};
+
static struct exynos_ufs_uic_attr fsd_uic_attr = {
.tx_trailingclks = 0x10,
.tx_dif_p_nsec = 3000000, /* unit: ns */
@@ -1718,7 +2103,9 @@ static struct exynos_ufs_uic_attr fsd_uic_attr = {
.rx_hs_g1_prep_sync_len_cap = PREP_LEN(0xf),
.rx_hs_g2_prep_sync_len_cap = PREP_LEN(0xf),
.rx_hs_g3_prep_sync_len_cap = PREP_LEN(0xf),
- .pa_dbg_option_suite = 0x2E820183,
+ .pa_dbg_clk_period_off = PA_DBG_CLK_PERIOD,
+ .pa_dbg_opt_suite1_val = 0x2E820183,
+ .pa_dbg_opt_suite1_off = PA_DBG_OPTION_SUITE,
};
static const struct exynos_ufs_drv_data fsd_ufs_drvs = {
@@ -1737,7 +2124,26 @@ static const struct exynos_ufs_drv_data fsd_ufs_drvs = {
.pre_pwr_change = fsd_ufs_pre_pwr_change,
};
+static const struct exynos_ufs_drv_data gs101_ufs_drvs = {
+ .uic_attr = &gs101_uic_attr,
+ .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN |
+ UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR |
+ UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR |
+ UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
+ UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL |
+ UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING,
+ .opts = EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR |
+ EXYNOS_UFS_OPT_UFSPR_SECURE |
+ EXYNOS_UFS_OPT_TIMER_TICK_SELECT,
+ .drv_init = gs101_ufs_drv_init,
+ .pre_link = gs101_ufs_pre_link,
+ .post_link = gs101_ufs_post_link,
+ .pre_pwr_change = gs101_ufs_pre_pwr_change,
+};
+
static const struct of_device_id exynos_ufs_of_match[] = {
+ { .compatible = "google,gs101-ufs",
+ .data = &gs101_ufs_drvs },
{ .compatible = "samsung,exynos7-ufs",
.data = &exynos_ufs_drvs },
{ .compatible = "samsung,exynosautov9-ufs",
@@ -1748,6 +2154,7 @@ static const struct of_device_id exynos_ufs_of_match[] = {
.data = &fsd_ufs_drvs },
{},
};
+MODULE_DEVICE_TABLE(of, exynos_ufs_of_match);
static const struct dev_pm_ops exynos_ufs_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume)
@@ -1758,7 +2165,7 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = {
static struct platform_driver exynos_ufs_pltform = {
.probe = exynos_ufs_probe,
- .remove_new = exynos_ufs_remove,
+ .remove = exynos_ufs_remove,
.driver = {
.name = "exynos-ufshc",
.pm = &exynos_ufs_pm_ops,
diff --git a/drivers/ufs/host/ufs-exynos.h b/drivers/ufs/host/ufs-exynos.h
index a4bd6646d7f1..9670dc138d1e 100644
--- a/drivers/ufs/host/ufs-exynos.h
+++ b/drivers/ufs/host/ufs-exynos.h
@@ -10,6 +10,12 @@
#define _UFS_EXYNOS_H_
/*
+ * Component registers
+ */
+
+#define COMP_CLK_PERIOD 0x44
+
+/*
* UNIPRO registers
*/
#define UNIPRO_DBG_FORCE_DME_CTRL_STATE 0x150
@@ -30,6 +36,14 @@
#define PA_DBG_OPTION_SUITE_DYN 0x9565
/*
+ * Note: GS101_DBG_OPTION offsets below differ from the TRM
+ * but match the downstream driver. Following the TRM
+ * results in non-functioning UFS.
+ */
+#define PA_GS101_DBG_OPTION_SUITE1 0x956a
+#define PA_GS101_DBG_OPTION_SUITE2 0x956d
+
+/*
* MIBs for Transport Layer debug registers
*/
#define T_DBG_SKIP_INIT_HIBERN8_EXIT 0xc001
@@ -116,7 +130,7 @@ struct exynos_ufs;
#define PA_HIBERN8TIME_VAL 0x20
#define PCLK_AVAIL_MIN 70000000
-#define PCLK_AVAIL_MAX 167000000
+#define PCLK_AVAIL_MAX 267000000
struct exynos_ufs_uic_attr {
/* TX Attributes */
@@ -145,7 +159,11 @@ struct exynos_ufs_uic_attr {
/* Common Attributes */
unsigned int cmn_pwm_clk_ctrl;
/* Internal Attributes */
- unsigned int pa_dbg_option_suite;
+ unsigned int pa_dbg_clk_period_off;
+ unsigned int pa_dbg_opt_suite1_val;
+ unsigned int pa_dbg_opt_suite1_off;
+ unsigned int pa_dbg_opt_suite2_val;
+ unsigned int pa_dbg_opt_suite2_off;
/* Changeable Attributes */
unsigned int rx_adv_fine_gran_sup_en;
unsigned int rx_adv_fine_gran_step;
@@ -164,7 +182,7 @@ struct exynos_ufs_drv_data {
unsigned int quirks;
unsigned int opts;
/* SoC's specific operations */
- int (*drv_init)(struct device *dev, struct exynos_ufs *ufs);
+ int (*drv_init)(struct exynos_ufs *ufs);
int (*pre_link)(struct exynos_ufs *ufs);
int (*post_link)(struct exynos_ufs *ufs);
int (*pre_pwr_change)(struct exynos_ufs *ufs,
@@ -221,6 +239,8 @@ struct exynos_ufs {
#define EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX BIT(3)
#define EXYNOS_UFS_OPT_USE_SW_HIBERN8_TIMER BIT(4)
#define EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR BIT(5)
+#define EXYNOS_UFS_OPT_UFSPR_SECURE BIT(6)
+#define EXYNOS_UFS_OPT_TIMER_TICK_SELECT BIT(7)
};
#define for_each_ufs_rx_lane(ufs, i) \
diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
index 5ee73ff05251..6e6569de74d8 100644
--- a/drivers/ufs/host/ufs-hisi.c
+++ b/drivers/ufs/host/ufs-hisi.c
@@ -576,9 +576,7 @@ static int ufs_hisi_probe(struct platform_device *pdev)
static void ufs_hisi_remove(struct platform_device *pdev)
{
- struct ufs_hba *hba = platform_get_drvdata(pdev);
-
- ufshcd_remove(hba);
+ ufshcd_pltfrm_remove(pdev);
}
static const struct dev_pm_ops ufs_hisi_pm_ops = {
@@ -590,7 +588,7 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = {
static struct platform_driver ufs_hisi_pltform = {
.probe = ufs_hisi_probe,
- .remove_new = ufs_hisi_remove,
+ .remove = ufs_hisi_remove,
.driver = {
.name = "ufshcd-hisi",
.pm = &ufs_hisi_pm_ops,
diff --git a/drivers/ufs/host/ufs-mediatek-sip.h b/drivers/ufs/host/ufs-mediatek-sip.h
new file mode 100644
index 000000000000..7d17aedf6fb8
--- /dev/null
+++ b/drivers/ufs/host/ufs-mediatek-sip.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#ifndef _UFS_MEDIATEK_SIP_H
+#define _UFS_MEDIATEK_SIP_H
+
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+
+/*
+ * SiP (Slicon Partner) commands
+ */
+#define MTK_SIP_UFS_CONTROL MTK_SIP_SMC_CMD(0x276)
+#define UFS_MTK_SIP_VA09_PWR_CTRL BIT(0)
+#define UFS_MTK_SIP_DEVICE_RESET BIT(1)
+#define UFS_MTK_SIP_CRYPTO_CTRL BIT(2)
+#define UFS_MTK_SIP_REF_CLK_NOTIFICATION BIT(3)
+#define UFS_MTK_SIP_SRAM_PWR_CTRL BIT(5)
+#define UFS_MTK_SIP_GET_VCC_NUM BIT(6)
+#define UFS_MTK_SIP_DEVICE_PWR_CTRL BIT(7)
+#define UFS_MTK_SIP_MPHY_CTRL BIT(8)
+#define UFS_MTK_SIP_MTCMOS_CTRL BIT(9)
+
+/*
+ * Multi-VCC by Numbering
+ */
+enum ufs_mtk_vcc_num {
+ UFS_VCC_NONE = 0,
+ UFS_VCC_1,
+ UFS_VCC_2,
+ UFS_VCC_MAX
+};
+
+enum ufs_mtk_mphy_op {
+ UFS_MPHY_BACKUP = 0,
+ UFS_MPHY_RESTORE
+};
+
+/*
+ * SMC call wrapper function
+ */
+struct ufs_mtk_smc_arg {
+ unsigned long cmd;
+ struct arm_smccc_res *res;
+ unsigned long v1;
+ unsigned long v2;
+ unsigned long v3;
+ unsigned long v4;
+ unsigned long v5;
+ unsigned long v6;
+ unsigned long v7;
+};
+
+
+static inline void _ufs_mtk_smc(struct ufs_mtk_smc_arg s)
+{
+ arm_smccc_smc(MTK_SIP_UFS_CONTROL,
+ s.cmd,
+ s.v1, s.v2, s.v3, s.v4, s.v5, s.v6, s.res);
+}
+
+#define ufs_mtk_smc(...) \
+ _ufs_mtk_smc((struct ufs_mtk_smc_arg) {__VA_ARGS__})
+
+/* Sip kernel interface */
+#define ufs_mtk_va09_pwr_ctrl(res, on) \
+ ufs_mtk_smc(UFS_MTK_SIP_VA09_PWR_CTRL, &(res), on)
+
+#define ufs_mtk_crypto_ctrl(res, enable) \
+ ufs_mtk_smc(UFS_MTK_SIP_CRYPTO_CTRL, &(res), enable)
+
+#define ufs_mtk_ref_clk_notify(on, stage, res) \
+ ufs_mtk_smc(UFS_MTK_SIP_REF_CLK_NOTIFICATION, &(res), on, stage)
+
+#define ufs_mtk_device_reset_ctrl(high, res) \
+ ufs_mtk_smc(UFS_MTK_SIP_DEVICE_RESET, &(res), high)
+
+#define ufs_mtk_sram_pwr_ctrl(on, res) \
+ ufs_mtk_smc(UFS_MTK_SIP_SRAM_PWR_CTRL, &(res), on)
+
+#define ufs_mtk_get_vcc_num(res) \
+ ufs_mtk_smc(UFS_MTK_SIP_GET_VCC_NUM, &(res))
+
+#define ufs_mtk_device_pwr_ctrl(on, ufs_version, res) \
+ ufs_mtk_smc(UFS_MTK_SIP_DEVICE_PWR_CTRL, &(res), on, ufs_version)
+
+#define ufs_mtk_mphy_ctrl(op, res) \
+ ufs_mtk_smc(UFS_MTK_SIP_MPHY_CTRL, &(res), op)
+
+#define ufs_mtk_mtcmos_ctrl(op, res) \
+ ufs_mtk_smc(UFS_MTK_SIP_MTCMOS_CTRL, &(res), op)
+
+#endif /* !_UFS_MEDIATEK_SIP_H */
diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
index 776bca4f70c8..135cd78109e2 100644
--- a/drivers/ufs/host/ufs-mediatek.c
+++ b/drivers/ufs/host/ufs-mediatek.c
@@ -17,16 +17,16 @@
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
-#include <linux/pm_qos.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
-#include <linux/soc/mediatek/mtk_sip_svc.h>
#include <ufs/ufshcd.h>
#include "ufshcd-pltfrm.h"
#include <ufs/ufs_quirks.h>
#include <ufs/unipro.h>
+
#include "ufs-mediatek.h"
+#include "ufs-mediatek-sip.h"
static int ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq);
@@ -52,6 +52,7 @@ static const struct of_device_id ufs_mtk_of_match[] = {
{ .compatible = "mediatek,mt8183-ufshci" },
{},
};
+MODULE_DEVICE_TABLE(of, ufs_mtk_of_match);
/*
* Details of UIC Errors
@@ -119,6 +120,27 @@ static bool ufs_mtk_is_pmc_via_fastauto(struct ufs_hba *hba)
return !!(host->caps & UFS_MTK_CAP_PMC_VIA_FASTAUTO);
}
+static bool ufs_mtk_is_tx_skew_fix(struct ufs_hba *hba)
+{
+ struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+
+ return (host->caps & UFS_MTK_CAP_TX_SKEW_FIX);
+}
+
+static bool ufs_mtk_is_rtff_mtcmos(struct ufs_hba *hba)
+{
+ struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+
+ return (host->caps & UFS_MTK_CAP_RTFF_MTCMOS);
+}
+
+static bool ufs_mtk_is_allow_vccqx_lpm(struct ufs_hba *hba)
+{
+ struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+
+ return (host->caps & UFS_MTK_CAP_ALLOW_VCCQX_LPM);
+}
+
static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable)
{
u32 tmp;
@@ -170,16 +192,23 @@ static void ufs_mtk_crypto_enable(struct ufs_hba *hba)
static void ufs_mtk_host_reset(struct ufs_hba *hba)
{
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+ struct arm_smccc_res res;
reset_control_assert(host->hci_reset);
reset_control_assert(host->crypto_reset);
reset_control_assert(host->unipro_reset);
+ reset_control_assert(host->mphy_reset);
usleep_range(100, 110);
reset_control_deassert(host->unipro_reset);
reset_control_deassert(host->crypto_reset);
reset_control_deassert(host->hci_reset);
+ reset_control_deassert(host->mphy_reset);
+
+ /* restore mphy setting aftre mphy reset */
+ if (host->mphy_reset)
+ ufs_mtk_mphy_ctrl(UFS_MPHY_RESTORE, res);
}
static void ufs_mtk_init_reset_control(struct ufs_hba *hba,
@@ -204,6 +233,8 @@ static void ufs_mtk_init_reset(struct ufs_hba *hba)
"unipro_rst");
ufs_mtk_init_reset_control(hba, &host->crypto_reset,
"crypto_rst");
+ ufs_mtk_init_reset_control(hba, &host->mphy_reset,
+ "mphy_rst");
}
static int ufs_mtk_hce_enable_notify(struct ufs_hba *hba,
@@ -623,24 +654,21 @@ static void ufs_mtk_init_host_caps(struct ufs_hba *hba)
if (of_property_read_bool(np, "mediatek,ufs-pmc-via-fastauto"))
host->caps |= UFS_MTK_CAP_PMC_VIA_FASTAUTO;
- dev_info(hba->dev, "caps: 0x%x", host->caps);
-}
+ if (of_property_read_bool(np, "mediatek,ufs-tx-skew-fix"))
+ host->caps |= UFS_MTK_CAP_TX_SKEW_FIX;
-static void ufs_mtk_boost_pm_qos(struct ufs_hba *hba, bool boost)
-{
- struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+ if (of_property_read_bool(np, "mediatek,ufs-disable-mcq"))
+ host->caps |= UFS_MTK_CAP_DISABLE_MCQ;
- if (!host || !host->pm_qos_init)
- return;
+ if (of_property_read_bool(np, "mediatek,ufs-rtff-mtcmos"))
+ host->caps |= UFS_MTK_CAP_RTFF_MTCMOS;
- cpu_latency_qos_update_request(&host->pm_qos_req,
- boost ? 0 : PM_QOS_DEFAULT_VALUE);
+ dev_info(hba->dev, "caps: 0x%x", host->caps);
}
static void ufs_mtk_scale_perf(struct ufs_hba *hba, bool scale_up)
{
ufs_mtk_boost_crypt(hba, scale_up);
- ufs_mtk_boost_pm_qos(hba, scale_up);
}
static void ufs_mtk_pwr_ctrl(struct ufs_hba *hba, bool on)
@@ -660,6 +688,45 @@ static void ufs_mtk_pwr_ctrl(struct ufs_hba *hba, bool on)
}
}
+static void ufs_mtk_mcq_disable_irq(struct ufs_hba *hba)
+{
+ struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+ u32 irq, i;
+
+ if (!hba->mcq_enabled)
+ return;
+
+ if (host->mcq_nr_intr == 0)
+ return;
+
+ for (i = 0; i < host->mcq_nr_intr; i++) {
+ irq = host->mcq_intr_info[i].irq;
+ disable_irq(irq);
+ }
+ host->is_mcq_intr_enabled = false;
+}
+
+static void ufs_mtk_mcq_enable_irq(struct ufs_hba *hba)
+{
+ struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+ u32 irq, i;
+
+ if (!hba->mcq_enabled)
+ return;
+
+ if (host->mcq_nr_intr == 0)
+ return;
+
+ if (host->is_mcq_intr_enabled == true)
+ return;
+
+ for (i = 0; i < host->mcq_nr_intr; i++) {
+ irq = host->mcq_intr_info[i].irq;
+ enable_irq(irq);
+ }
+ host->is_mcq_intr_enabled = true;
+}
+
/**
* ufs_mtk_setup_clocks - enables/disable clocks
* @hba: host controller instance
@@ -703,8 +770,10 @@ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on,
if (clk_pwr_off)
ufs_mtk_pwr_ctrl(hba, false);
+ ufs_mtk_mcq_disable_irq(hba);
} else if (on && status == POST_CHANGE) {
ufs_mtk_pwr_ctrl(hba, true);
+ ufs_mtk_mcq_enable_irq(hba);
}
return ret;
@@ -857,6 +926,9 @@ static void ufs_mtk_init_mcq_irq(struct ufs_hba *hba)
host->mcq_nr_intr = UFSHCD_MAX_Q_NR;
pdev = container_of(hba->dev, struct platform_device, dev);
+ if (host->caps & UFS_MTK_CAP_DISABLE_MCQ)
+ goto failed;
+
for (i = 0; i < host->mcq_nr_intr; i++) {
/* irq index 0 is legacy irq, sq/cq irq start from index 1 */
irq = platform_get_irq(pdev, i + 1);
@@ -893,7 +965,9 @@ static int ufs_mtk_init(struct ufs_hba *hba)
const struct of_device_id *id;
struct device *dev = hba->dev;
struct ufs_mtk_host *host;
+ struct Scsi_Host *shost = hba->host;
int err = 0;
+ struct arm_smccc_res res;
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host) {
@@ -922,6 +996,10 @@ static int ufs_mtk_init(struct ufs_hba *hba)
ufs_mtk_init_reset(hba);
+ /* backup mphy setting if mphy can reset */
+ if (host->mphy_reset)
+ ufs_mtk_mphy_ctrl(UFS_MPHY_BACKUP, res);
+
/* Enable runtime autosuspend */
hba->caps |= UFSHCD_CAP_RPM_AUTOSUSPEND;
@@ -937,6 +1015,9 @@ static int ufs_mtk_init(struct ufs_hba *hba)
/* Enable clk scaling*/
hba->caps |= UFSHCD_CAP_CLK_SCALING;
+ /* Set runtime pm delay to replace default */
+ shost->rpm_autosuspend_delay = MTK_RPM_AUTOSUSPEND_DELAY_MS;
+
hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL;
hba->quirks |= UFSHCD_QUIRK_MCQ_BROKEN_INTR;
hba->quirks |= UFSHCD_QUIRK_MCQ_BROKEN_RTC;
@@ -945,6 +1026,9 @@ static int ufs_mtk_init(struct ufs_hba *hba)
if (host->caps & UFS_MTK_CAP_DISABLE_AH8)
hba->caps |= UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
+ if (host->caps & UFS_MTK_CAP_DISABLE_MCQ)
+ hba->quirks |= UFSHCD_QUIRK_BROKEN_LSDBS_CAP;
+
ufs_mtk_init_clocks(hba);
/*
@@ -955,14 +1039,19 @@ static int ufs_mtk_init(struct ufs_hba *hba)
* Enable phy clocks specifically here.
*/
ufs_mtk_mphy_power_on(hba, true);
+
+ if (ufs_mtk_is_rtff_mtcmos(hba)) {
+ /* First Restore here, to avoid backup unexpected value */
+ ufs_mtk_mtcmos_ctrl(false, res);
+
+ /* Power on to init */
+ ufs_mtk_mtcmos_ctrl(true, res);
+ }
+
ufs_mtk_setup_clocks(hba, true, POST_CHANGE);
host->ip_ver = ufshcd_readl(hba, REG_UFS_MTK_IP_VER);
- /* Initialize pm-qos request */
- cpu_latency_qos_add_request(&host->pm_qos_req, PM_QOS_DEFAULT_VALUE);
- host->pm_qos_init = true;
-
goto out;
out_variant_clear:
@@ -1206,25 +1295,29 @@ static int ufs_mtk_link_set_hpm(struct ufs_hba *hba)
return err;
err = ufshcd_uic_hibern8_exit(hba);
- if (!err)
- ufshcd_set_link_active(hba);
- else
+ if (err)
return err;
- if (!hba->mcq_enabled) {
- err = ufshcd_make_hba_operational(hba);
- } else {
- ufs_mtk_config_mcq(hba, false);
- ufshcd_mcq_make_queues_operational(hba);
- ufshcd_mcq_config_mac(hba, hba->nutrs);
- /* Enable MCQ mode */
- ufshcd_writel(hba, ufshcd_readl(hba, REG_UFS_MEM_CFG) | 0x1,
- REG_UFS_MEM_CFG);
+ /* Check link state to make sure exit h8 success */
+ ufs_mtk_wait_idle_state(hba, 5);
+ err = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
+ if (err) {
+ dev_warn(hba->dev, "exit h8 state fail, err=%d\n", err);
+ return err;
}
+ ufshcd_set_link_active(hba);
+ err = ufshcd_make_hba_operational(hba);
if (err)
return err;
+ if (hba->mcq_enabled) {
+ ufs_mtk_config_mcq(hba, false);
+ ufshcd_mcq_make_queues_operational(hba);
+ ufshcd_mcq_config_mac(hba, hba->nutrs);
+ ufshcd_mcq_enable(hba);
+ }
+
return 0;
}
@@ -1271,27 +1364,37 @@ static void ufs_mtk_vsx_set_lpm(struct ufs_hba *hba, bool lpm)
static void ufs_mtk_dev_vreg_set_lpm(struct ufs_hba *hba, bool lpm)
{
- if (!hba->vreg_info.vccq && !hba->vreg_info.vccq2)
- return;
+ bool skip_vccqx = false;
- /* Skip if VCC is assumed always-on */
- if (!hba->vreg_info.vcc)
- return;
-
- /* Bypass LPM when device is still active */
+ /* Prevent entering LPM when device is still active */
if (lpm && ufshcd_is_ufs_dev_active(hba))
return;
- /* Bypass LPM if VCC is enabled */
- if (lpm && hba->vreg_info.vcc->enabled)
- return;
+ /* Skip vccqx lpm control and control vsx only */
+ if (!hba->vreg_info.vccq && !hba->vreg_info.vccq2)
+ skip_vccqx = true;
+
+ /* VCC is always-on, control vsx only */
+ if (!hba->vreg_info.vcc)
+ skip_vccqx = true;
+
+ /* Broken vcc keep vcc always on, most case control vsx only */
+ if (lpm && hba->vreg_info.vcc && hba->vreg_info.vcc->enabled) {
+ /* Some device vccqx/vsx can enter lpm */
+ if (ufs_mtk_is_allow_vccqx_lpm(hba))
+ skip_vccqx = false;
+ else /* control vsx only */
+ skip_vccqx = true;
+ }
if (lpm) {
- ufs_mtk_vccqx_set_lpm(hba, lpm);
+ if (!skip_vccqx)
+ ufs_mtk_vccqx_set_lpm(hba, lpm);
ufs_mtk_vsx_set_lpm(hba, lpm);
} else {
ufs_mtk_vsx_set_lpm(hba, lpm);
- ufs_mtk_vccqx_set_lpm(hba, lpm);
+ if (!skip_vccqx)
+ ufs_mtk_vccqx_set_lpm(hba, lpm);
}
}
@@ -1342,7 +1445,7 @@ static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
if (ufshcd_is_link_off(hba))
ufs_mtk_device_reset_ctrl(0, res);
- ufs_mtk_host_pwr_ctrl(HOST_PWR_HCI, false, res);
+ ufs_mtk_sram_pwr_ctrl(false, res);
return 0;
fail:
@@ -1363,7 +1466,7 @@ static int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL)
ufs_mtk_dev_vreg_set_lpm(hba, false);
- ufs_mtk_host_pwr_ctrl(HOST_PWR_HCI, true, res);
+ ufs_mtk_sram_pwr_ctrl(true, res);
err = ufs_mtk_mphy_power_on(hba, true);
if (err)
@@ -1406,6 +1509,17 @@ static int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba)
if (mid == UFS_VENDOR_SAMSUNG) {
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 6);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HIBERN8TIME), 10);
+ } else if (mid == UFS_VENDOR_MICRON) {
+ /* Only for the host which have TX skew issue */
+ if (ufs_mtk_is_tx_skew_fix(hba) &&
+ (STR_PRFX_EQUAL("MT128GBCAV2U31", dev_info->model) ||
+ STR_PRFX_EQUAL("MT256GBCAV4U31", dev_info->model) ||
+ STR_PRFX_EQUAL("MT512GBCAV8U31", dev_info->model) ||
+ STR_PRFX_EQUAL("MT256GBEAX4U40", dev_info->model) ||
+ STR_PRFX_EQUAL("MT512GAYAX4U40", dev_info->model) ||
+ STR_PRFX_EQUAL("MT001TAYAX8U40", dev_info->model))) {
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 8);
+ }
}
/*
@@ -1547,6 +1661,12 @@ static int ufs_mtk_clk_scale_notify(struct ufs_hba *hba, bool scale_up,
static int ufs_mtk_get_hba_mac(struct ufs_hba *hba)
{
+ struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+
+ /* MCQ operation not permitted */
+ if (host->caps & UFS_MTK_CAP_DISABLE_MCQ)
+ return -EPERM;
+
return MAX_SUPP_MAC;
}
@@ -1660,6 +1780,15 @@ static int ufs_mtk_config_esi(struct ufs_hba *hba)
return ufs_mtk_config_mcq(hba, true);
}
+static void ufs_mtk_config_scsi_dev(struct scsi_device *sdev)
+{
+ struct ufs_hba *hba = shost_priv(sdev->host);
+
+ dev_dbg(hba->dev, "lu %llu scsi device configured", sdev->lun);
+ if (sdev->lun == 2)
+ blk_queue_flag_set(QUEUE_FLAG_SAME_FORCE, sdev->request_queue);
+}
+
/*
* struct ufs_hba_mtk_vops - UFS MTK specific variant operations
*
@@ -1668,6 +1797,7 @@ static int ufs_mtk_config_esi(struct ufs_hba *hba)
*/
static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
.name = "mediatek.ufshci",
+ .max_num_rtt = MTK_MAX_NUM_RTT,
.init = ufs_mtk_init,
.get_ufs_hci_version = ufs_mtk_get_ufs_hci_version,
.setup_clocks = ufs_mtk_setup_clocks,
@@ -1688,6 +1818,7 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
.op_runtime_config = ufs_mtk_op_runtime_config,
.mcq_config_resource = ufs_mtk_mcq_config_resource,
.config_esi = ufs_mtk_config_esi,
+ .config_scsi_dev = ufs_mtk_config_scsi_dev,
};
/**
@@ -1748,16 +1879,14 @@ out:
*/
static void ufs_mtk_remove(struct platform_device *pdev)
{
- struct ufs_hba *hba = platform_get_drvdata(pdev);
-
- pm_runtime_get_sync(&(pdev)->dev);
- ufshcd_remove(hba);
+ ufshcd_pltfrm_remove(pdev);
}
#ifdef CONFIG_PM_SLEEP
static int ufs_mtk_system_suspend(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
+ struct arm_smccc_res res;
int ret;
ret = ufshcd_system_suspend(dev);
@@ -1766,15 +1895,22 @@ static int ufs_mtk_system_suspend(struct device *dev)
ufs_mtk_dev_vreg_set_lpm(hba, true);
+ if (ufs_mtk_is_rtff_mtcmos(hba))
+ ufs_mtk_mtcmos_ctrl(false, res);
+
return 0;
}
static int ufs_mtk_system_resume(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
+ struct arm_smccc_res res;
ufs_mtk_dev_vreg_set_lpm(hba, false);
+ if (ufs_mtk_is_rtff_mtcmos(hba))
+ ufs_mtk_mtcmos_ctrl(true, res);
+
return ufshcd_system_resume(dev);
}
#endif
@@ -1783,6 +1919,7 @@ static int ufs_mtk_system_resume(struct device *dev)
static int ufs_mtk_runtime_suspend(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
+ struct arm_smccc_res res;
int ret = 0;
ret = ufshcd_runtime_suspend(dev);
@@ -1791,12 +1928,19 @@ static int ufs_mtk_runtime_suspend(struct device *dev)
ufs_mtk_dev_vreg_set_lpm(hba, true);
+ if (ufs_mtk_is_rtff_mtcmos(hba))
+ ufs_mtk_mtcmos_ctrl(false, res);
+
return 0;
}
static int ufs_mtk_runtime_resume(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
+ struct arm_smccc_res res;
+
+ if (ufs_mtk_is_rtff_mtcmos(hba))
+ ufs_mtk_mtcmos_ctrl(true, res);
ufs_mtk_dev_vreg_set_lpm(hba, false);
@@ -1815,7 +1959,7 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = {
static struct platform_driver ufs_mtk_pltform = {
.probe = ufs_mtk_probe,
- .remove_new = ufs_mtk_remove,
+ .remove = ufs_mtk_remove,
.driver = {
.name = "ufshcd-mtk",
.pm = &ufs_mtk_pm_ops,
diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h
index f76e80d91729..05d76a6bd772 100644
--- a/drivers/ufs/host/ufs-mediatek.h
+++ b/drivers/ufs/host/ufs-mediatek.h
@@ -7,8 +7,6 @@
#define _UFS_MEDIATEK_H
#include <linux/bitops.h>
-#include <linux/pm_qos.h>
-#include <linux/soc/mediatek/mtk_sip_svc.h>
/*
* MCQ define and struct
@@ -101,18 +99,6 @@ enum {
};
/*
- * SiP commands
- */
-#define MTK_SIP_UFS_CONTROL MTK_SIP_SMC_CMD(0x276)
-#define UFS_MTK_SIP_VA09_PWR_CTRL BIT(0)
-#define UFS_MTK_SIP_DEVICE_RESET BIT(1)
-#define UFS_MTK_SIP_CRYPTO_CTRL BIT(2)
-#define UFS_MTK_SIP_REF_CLK_NOTIFICATION BIT(3)
-#define UFS_MTK_SIP_HOST_PWR_CTRL BIT(5)
-#define UFS_MTK_SIP_GET_VCC_NUM BIT(6)
-#define UFS_MTK_SIP_DEVICE_PWR_CTRL BIT(7)
-
-/*
* VS_DEBUGCLOCKENABLE
*/
enum {
@@ -136,7 +122,17 @@ enum ufs_mtk_host_caps {
UFS_MTK_CAP_VA09_PWR_CTRL = 1 << 1,
UFS_MTK_CAP_DISABLE_AH8 = 1 << 2,
UFS_MTK_CAP_BROKEN_VCC = 1 << 3,
+
+ /*
+ * Override UFS_MTK_CAP_BROKEN_VCC's behavior to
+ * allow vccqx upstream to enter LPM
+ */
+ UFS_MTK_CAP_ALLOW_VCCQX_LPM = 1 << 5,
UFS_MTK_CAP_PMC_VIA_FASTAUTO = 1 << 6,
+ UFS_MTK_CAP_TX_SKEW_FIX = 1 << 7,
+ UFS_MTK_CAP_DISABLE_MCQ = 1 << 8,
+ /* Control MTCMOS with RTFF */
+ UFS_MTK_CAP_RTFF_MTCMOS = 1 << 9,
};
struct ufs_mtk_crypt_cfg {
@@ -167,18 +163,17 @@ struct ufs_mtk_mcq_intr_info {
struct ufs_mtk_host {
struct phy *mphy;
- struct pm_qos_request pm_qos_req;
struct regulator *reg_va09;
struct reset_control *hci_reset;
struct reset_control *unipro_reset;
struct reset_control *crypto_reset;
+ struct reset_control *mphy_reset;
struct ufs_hba *hba;
struct ufs_mtk_crypt_cfg *crypt;
struct ufs_mtk_clk mclk;
struct ufs_mtk_hw_ver hw_ver;
enum ufs_mtk_host_caps caps;
bool mphy_powered_on;
- bool pm_qos_init;
bool unipro_lpm;
bool ref_clk_enabled;
u16 ref_clk_ungating_wait_us;
@@ -186,74 +181,15 @@ struct ufs_mtk_host {
u32 ip_ver;
bool mcq_set_intr;
+ bool is_mcq_intr_enabled;
int mcq_nr_intr;
struct ufs_mtk_mcq_intr_info mcq_intr_info[UFSHCD_MAX_Q_NR];
};
-/*
- * Multi-VCC by Numbering
- */
-enum ufs_mtk_vcc_num {
- UFS_VCC_NONE = 0,
- UFS_VCC_1,
- UFS_VCC_2,
- UFS_VCC_MAX
-};
-
-/*
- * Host Power Control options
- */
-enum {
- HOST_PWR_HCI = 0,
- HOST_PWR_MPHY
-};
-
-/*
- * SMC call wrapper function
- */
-struct ufs_mtk_smc_arg {
- unsigned long cmd;
- struct arm_smccc_res *res;
- unsigned long v1;
- unsigned long v2;
- unsigned long v3;
- unsigned long v4;
- unsigned long v5;
- unsigned long v6;
- unsigned long v7;
-};
-
-static void _ufs_mtk_smc(struct ufs_mtk_smc_arg s)
-{
- arm_smccc_smc(MTK_SIP_UFS_CONTROL,
- s.cmd, s.v1, s.v2, s.v3, s.v4, s.v5, s.v6, s.res);
-}
-
-#define ufs_mtk_smc(...) \
- _ufs_mtk_smc((struct ufs_mtk_smc_arg) {__VA_ARGS__})
-
-/*
- * SMC call interface
- */
-#define ufs_mtk_va09_pwr_ctrl(res, on) \
- ufs_mtk_smc(UFS_MTK_SIP_VA09_PWR_CTRL, &(res), on)
-
-#define ufs_mtk_crypto_ctrl(res, enable) \
- ufs_mtk_smc(UFS_MTK_SIP_CRYPTO_CTRL, &(res), enable)
-
-#define ufs_mtk_ref_clk_notify(on, stage, res) \
- ufs_mtk_smc(UFS_MTK_SIP_REF_CLK_NOTIFICATION, &(res), on, stage)
-
-#define ufs_mtk_device_reset_ctrl(high, res) \
- ufs_mtk_smc(UFS_MTK_SIP_DEVICE_RESET, &(res), high)
-
-#define ufs_mtk_host_pwr_ctrl(opt, on, res) \
- ufs_mtk_smc(UFS_MTK_SIP_HOST_PWR_CTRL, &(res), opt, on)
-
-#define ufs_mtk_get_vcc_num(res) \
- ufs_mtk_smc(UFS_MTK_SIP_GET_VCC_NUM, &(res))
+/* MTK delay of autosuspend: 500 ms */
+#define MTK_RPM_AUTOSUSPEND_DELAY_MS 500
-#define ufs_mtk_device_pwr_ctrl(on, ufs_ver, res) \
- ufs_mtk_smc(UFS_MTK_SIP_DEVICE_PWR_CTRL, &(res), on, ufs_ver)
+/* MTK RTT support number */
+#define MTK_MAX_NUM_RTT 2
#endif /* !_UFS_MEDIATEK_H */
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 39eef470f8fa..23b9f6efa047 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -47,7 +47,7 @@ enum {
TSTBUS_MAX,
};
-#define QCOM_UFS_MAX_GEAR 4
+#define QCOM_UFS_MAX_GEAR 5
#define QCOM_UFS_MAX_LANE 2
enum {
@@ -67,27 +67,33 @@ static const struct __ufs_qcom_bw_table {
[MODE_PWM][UFS_PWM_G2][UFS_LANE_1] = { 1844, 1000 },
[MODE_PWM][UFS_PWM_G3][UFS_LANE_1] = { 3688, 1000 },
[MODE_PWM][UFS_PWM_G4][UFS_LANE_1] = { 7376, 1000 },
+ [MODE_PWM][UFS_PWM_G5][UFS_LANE_1] = { 14752, 1000 },
[MODE_PWM][UFS_PWM_G1][UFS_LANE_2] = { 1844, 1000 },
[MODE_PWM][UFS_PWM_G2][UFS_LANE_2] = { 3688, 1000 },
[MODE_PWM][UFS_PWM_G3][UFS_LANE_2] = { 7376, 1000 },
[MODE_PWM][UFS_PWM_G4][UFS_LANE_2] = { 14752, 1000 },
+ [MODE_PWM][UFS_PWM_G5][UFS_LANE_2] = { 29504, 1000 },
[MODE_HS_RA][UFS_HS_G1][UFS_LANE_1] = { 127796, 1000 },
[MODE_HS_RA][UFS_HS_G2][UFS_LANE_1] = { 255591, 1000 },
[MODE_HS_RA][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 },
[MODE_HS_RA][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 },
+ [MODE_HS_RA][UFS_HS_G5][UFS_LANE_1] = { 5836800, 409600 },
[MODE_HS_RA][UFS_HS_G1][UFS_LANE_2] = { 255591, 1000 },
[MODE_HS_RA][UFS_HS_G2][UFS_LANE_2] = { 511181, 1000 },
[MODE_HS_RA][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 },
[MODE_HS_RA][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 },
+ [MODE_HS_RA][UFS_HS_G5][UFS_LANE_2] = { 5836800, 819200 },
[MODE_HS_RB][UFS_HS_G1][UFS_LANE_1] = { 149422, 1000 },
[MODE_HS_RB][UFS_HS_G2][UFS_LANE_1] = { 298189, 1000 },
[MODE_HS_RB][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 },
[MODE_HS_RB][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 },
+ [MODE_HS_RB][UFS_HS_G5][UFS_LANE_1] = { 5836800, 409600 },
[MODE_HS_RB][UFS_HS_G1][UFS_LANE_2] = { 298189, 1000 },
[MODE_HS_RB][UFS_HS_G2][UFS_LANE_2] = { 596378, 1000 },
[MODE_HS_RB][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 },
[MODE_HS_RB][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 },
- [MODE_MAX][0][0] = { 7643136, 307200 },
+ [MODE_HS_RB][UFS_HS_G5][UFS_LANE_2] = { 5836800, 819200 },
+ [MODE_MAX][0][0] = { 7643136, 819200 },
};
static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
@@ -106,11 +112,18 @@ static inline void ufs_qcom_ice_enable(struct ufs_qcom_host *host)
qcom_ice_enable(host->ice);
}
+static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops; /* forward decl */
+
static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
{
struct ufs_hba *hba = host->hba;
+ struct blk_crypto_profile *profile = &hba->crypto_profile;
struct device *dev = hba->dev;
struct qcom_ice *ice;
+ union ufs_crypto_capabilities caps;
+ union ufs_crypto_cap_entry cap;
+ int err;
+ int i;
ice = of_qcom_ice_get(dev);
if (ice == ERR_PTR(-EOPNOTSUPP)) {
@@ -122,8 +135,38 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
return PTR_ERR_OR_ZERO(ice);
host->ice = ice;
- hba->caps |= UFSHCD_CAP_CRYPTO;
+ /* Initialize the blk_crypto_profile */
+
+ caps.reg_val = cpu_to_le32(ufshcd_readl(hba, REG_UFS_CCAP));
+
+ /* The number of keyslots supported is (CFGC+1) */
+ err = devm_blk_crypto_profile_init(dev, profile, caps.config_count + 1);
+ if (err)
+ return err;
+
+ profile->ll_ops = ufs_qcom_crypto_ops;
+ profile->max_dun_bytes_supported = 8;
+ profile->dev = dev;
+
+ /*
+ * Currently this driver only supports AES-256-XTS. All known versions
+ * of ICE support it, but to be safe make sure it is really declared in
+ * the crypto capability registers. The crypto capability registers
+ * also give the supported data unit size(s).
+ */
+ for (i = 0; i < caps.num_crypto_cap; i++) {
+ cap.reg_val = cpu_to_le32(ufshcd_readl(hba,
+ REG_UFS_CRYPTOCAP +
+ i * sizeof(__le32)));
+ if (cap.algorithm_id == UFS_CRYPTO_ALG_AES_XTS &&
+ cap.key_size == UFS_CRYPTO_KEY_SIZE_256)
+ profile->modes_supported[BLK_ENCRYPTION_MODE_AES_256_XTS] |=
+ cap.sdus_mask * 512;
+ }
+
+ hba->caps |= UFSHCD_CAP_CRYPTO;
+ hba->quirks |= UFSHCD_QUIRK_CUSTOM_CRYPTO_PROFILE;
return 0;
}
@@ -143,34 +186,49 @@ static inline int ufs_qcom_ice_suspend(struct ufs_qcom_host *host)
return 0;
}
-static int ufs_qcom_ice_program_key(struct ufs_hba *hba,
- const union ufs_crypto_cfg_entry *cfg,
- int slot)
+static int ufs_qcom_ice_keyslot_program(struct blk_crypto_profile *profile,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
{
+ struct ufs_hba *hba = ufs_hba_from_crypto_profile(profile);
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
- union ufs_crypto_cap_entry cap;
- bool config_enable =
- cfg->config_enable & UFS_CRYPTO_CONFIGURATION_ENABLE;
+ int err;
/* Only AES-256-XTS has been tested so far. */
- cap = hba->crypto_cap_array[cfg->crypto_cap_idx];
- if (cap.algorithm_id != UFS_CRYPTO_ALG_AES_XTS ||
- cap.key_size != UFS_CRYPTO_KEY_SIZE_256)
+ if (key->crypto_cfg.crypto_mode != BLK_ENCRYPTION_MODE_AES_256_XTS)
return -EOPNOTSUPP;
- if (config_enable)
- return qcom_ice_program_key(host->ice,
- QCOM_ICE_CRYPTO_ALG_AES_XTS,
- QCOM_ICE_CRYPTO_KEY_SIZE_256,
- cfg->crypto_key,
- cfg->data_unit_size, slot);
- else
- return qcom_ice_evict_key(host->ice, slot);
+ ufshcd_hold(hba);
+ err = qcom_ice_program_key(host->ice,
+ QCOM_ICE_CRYPTO_ALG_AES_XTS,
+ QCOM_ICE_CRYPTO_KEY_SIZE_256,
+ key->raw,
+ key->crypto_cfg.data_unit_size / 512,
+ slot);
+ ufshcd_release(hba);
+ return err;
}
-#else
+static int ufs_qcom_ice_keyslot_evict(struct blk_crypto_profile *profile,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+{
+ struct ufs_hba *hba = ufs_hba_from_crypto_profile(profile);
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+ int err;
-#define ufs_qcom_ice_program_key NULL
+ ufshcd_hold(hba);
+ err = qcom_ice_evict_key(host->ice, slot);
+ ufshcd_release(hba);
+ return err;
+}
+
+static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops = {
+ .keyslot_program = ufs_qcom_ice_keyslot_program,
+ .keyslot_evict = ufs_qcom_ice_keyslot_evict,
+};
+
+#else
static inline void ufs_qcom_ice_enable(struct ufs_qcom_host *host)
{
@@ -278,9 +336,6 @@ static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host)
if (host->hw_ver.major >= 0x05)
ufshcd_rmwl(host->hba, QUNIPRO_G4_SEL, 0, REG_UFS_CFG0);
-
- /* make sure above configuration is applied before we return */
- mb();
}
/*
@@ -365,6 +420,11 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
if (ret)
return ret;
+ if (phy->power_count) {
+ phy_power_off(phy);
+ phy_exit(phy);
+ }
+
/* phy initialization - calibrate the phy */
ret = phy_init(phy);
if (ret) {
@@ -409,7 +469,7 @@ static void ufs_qcom_enable_hw_clk_gating(struct ufs_hba *hba)
REG_UFS_CFG2);
/* Ensure that HW clock gating is enabled before next operations */
- mb();
+ ufshcd_readl(hba, REG_UFS_CFG2);
}
static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
@@ -501,7 +561,7 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
* make sure above write gets applied before we return from
* this function.
*/
- mb();
+ ufshcd_readl(hba, REG_UFS_SYS1CLK_1US);
}
return 0;
@@ -531,8 +591,7 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
* and device TX LCC are disabled once link startup is
* completed.
*/
- if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41)
- err = ufshcd_disable_host_tx_lcc(hba);
+ err = ufshcd_disable_host_tx_lcc(hba);
break;
default:
@@ -690,6 +749,16 @@ static struct __ufs_qcom_bw_table ufs_qcom_get_bw_table(struct ufs_qcom_host *ho
int gear = max_t(u32, p->gear_rx, p->gear_tx);
int lane = max_t(u32, p->lane_rx, p->lane_tx);
+ if (WARN_ONCE(gear > QCOM_UFS_MAX_GEAR,
+ "ICC scaling for UFS Gear (%d) not supported. Using Gear (%d) bandwidth\n",
+ gear, QCOM_UFS_MAX_GEAR))
+ gear = QCOM_UFS_MAX_GEAR;
+
+ if (WARN_ONCE(lane > QCOM_UFS_MAX_LANE,
+ "ICC scaling for UFS Lane (%d) not supported. Using Lane (%d) bandwidth\n",
+ lane, QCOM_UFS_MAX_LANE))
+ lane = QCOM_UFS_MAX_LANE;
+
if (ufshcd_is_hs_mode(p)) {
if (p->hs_rate == PA_HS_MODE_B)
return ufs_qcom_bw_table[MODE_HS_RB][gear][lane];
@@ -738,8 +807,17 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
* the second init can program the optimal PHY settings. This allows one to start
* the first init with either the minimum or the maximum support gear.
*/
- if (hba->ufshcd_state == UFSHCD_STATE_RESET)
- host->phy_gear = dev_req_params->gear_tx;
+ if (hba->ufshcd_state == UFSHCD_STATE_RESET) {
+ /*
+ * Skip REINIT if the negotiated gear matches with the
+ * initial phy_gear. Otherwise, update the phy_gear to
+ * program the optimal gear setting during REINIT.
+ */
+ if (host->phy_gear == dev_req_params->gear_tx)
+ hba->quirks &= ~UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH;
+ else
+ host->phy_gear = dev_req_params->gear_tx;
+ }
/* enable the device ref clock before changing to HS mode */
if (!ufshcd_is_hs_mode(&hba->pwr_info) &&
@@ -807,12 +885,28 @@ static int ufs_qcom_apply_dev_quirks(struct ufs_hba *hba)
if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME)
err = ufs_qcom_quirk_host_pa_saveconfigtime(hba);
- if (hba->dev_info.wmanufacturerid == UFS_VENDOR_WDC)
- hba->dev_quirks |= UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE;
-
return err;
}
+/* UFS device-specific quirks */
+static struct ufs_dev_quirk ufs_qcom_dev_fixups[] = {
+ { .wmanufacturerid = UFS_VENDOR_SKHYNIX,
+ .model = UFS_ANY_MODEL,
+ .quirk = UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM },
+ { .wmanufacturerid = UFS_VENDOR_TOSHIBA,
+ .model = UFS_ANY_MODEL,
+ .quirk = UFS_DEVICE_QUIRK_DELAY_AFTER_LPM },
+ { .wmanufacturerid = UFS_VENDOR_WDC,
+ .model = UFS_ANY_MODEL,
+ .quirk = UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE },
+ {}
+};
+
+static void ufs_qcom_fixup_dev_quirks(struct ufs_hba *hba)
+{
+ ufshcd_fixup_dev_quirks(hba, ufs_qcom_dev_fixups);
+}
+
static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba)
{
return ufshci_version(2, 0);
@@ -829,6 +923,7 @@ static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba)
*/
static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
{
+ const struct ufs_qcom_drvdata *drvdata = of_device_get_match_data(hba->dev);
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
if (host->hw_ver.major == 0x2)
@@ -836,6 +931,9 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
if (host->hw_ver.major > 0x3)
hba->quirks |= UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH;
+
+ if (drvdata && drvdata->quirks)
+ hba->quirks |= drvdata->quirks;
}
static void ufs_qcom_set_phy_gear(struct ufs_qcom_host *host)
@@ -843,15 +941,20 @@ static void ufs_qcom_set_phy_gear(struct ufs_qcom_host *host)
struct ufs_host_params *host_params = &host->host_params;
u32 val, dev_major;
+ /*
+ * Default to powering up the PHY to the max gear possible, which is
+ * backwards compatible with lower gears but not optimal from
+ * a power usage point of view. After device negotiation, if the
+ * gear is lower a reinit will be performed to program the PHY
+ * to the ideal gear for this combo of controller and device.
+ */
host->phy_gear = host_params->hs_tx_gear;
if (host->hw_ver.major < 0x4) {
/*
- * For controllers whose major HW version is < 4, power up the
- * PHY using minimum supported gear (UFS_HS_G2). Switching to
- * max gear will be performed during reinit if supported.
- * For newer controllers, whose major HW version is >= 4, power
- * up the PHY using max supported gear.
+ * These controllers only have one PHY init sequence,
+ * let's power up the PHY using that (the minimum supported
+ * gear, UFS_HS_G2).
*/
host->phy_gear = UFS_HS_G2;
} else if (host->hw_ver.major >= 0x5) {
@@ -1018,6 +1121,7 @@ static int ufs_qcom_init(struct ufs_hba *hba)
struct device *dev = hba->dev;
struct ufs_qcom_host *host;
struct ufs_clk_info *clki;
+ const struct ufs_qcom_drvdata *drvdata = of_device_get_match_data(hba->dev);
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host)
@@ -1097,6 +1201,9 @@ static int ufs_qcom_init(struct ufs_hba *hba)
dev_warn(dev, "%s: failed to configure the testbus %d\n",
__func__, err);
+ if (drvdata && drvdata->no_phy_retention)
+ hba->spm_lvl = UFS_PM_LVL_5;
+
return 0;
out_variant_clear:
@@ -1196,8 +1303,10 @@ static int ufs_qcom_set_core_clk_ctrl(struct ufs_hba *hba, bool is_scale_up)
list_for_each_entry(clki, head, list) {
if (!IS_ERR_OR_NULL(clki->clk) &&
- !strcmp(clki->name, "core_clk_unipro")) {
- if (is_scale_up)
+ !strcmp(clki->name, "core_clk_unipro")) {
+ if (!clki->max_freq)
+ cycles_in_1us = 150; /* default for backwards compatibility */
+ else if (is_scale_up)
cycles_in_1us = ceil(clki->max_freq, (1000 * 1000));
else
cycles_in_1us = ceil(clk_get_rate(clki->clk), (1000 * 1000));
@@ -1429,11 +1538,6 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
(u32)host->testbus.select_minor << offset,
reg);
ufs_qcom_enable_test_bus(host);
- /*
- * Make sure the test bus configuration is
- * committed before returning.
- */
- mb();
return 0;
}
@@ -1525,6 +1629,8 @@ static void ufs_qcom_config_scaling_param(struct ufs_hba *hba,
p->timer = DEVFREQ_TIMER_DELAYED;
d->upthreshold = 70;
d->downdifferential = 5;
+
+ hba->clk_scaling.suspend_on_no_request = true;
}
#else
static void ufs_qcom_config_scaling_param(struct ufs_hba *hba,
@@ -1534,13 +1640,6 @@ static void ufs_qcom_config_scaling_param(struct ufs_hba *hba,
}
#endif
-static void ufs_qcom_reinit_notify(struct ufs_hba *hba)
-{
- struct ufs_qcom_host *host = ufshcd_get_variant(hba);
-
- phy_power_off(host->generic_phy);
-}
-
/* Resources */
static const struct ufshcd_res_info ufs_res_info[RES_MAX] = {
{.name = "ufs_mem",},
@@ -1712,8 +1811,8 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
* 2. Poll queues do not need ESI.
*/
nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL];
- ret = platform_msi_domain_alloc_irqs(hba->dev, nr_irqs,
- ufs_qcom_write_msi_msg);
+ ret = platform_device_msi_init_and_alloc_irqs(hba->dev, nr_irqs,
+ ufs_qcom_write_msi_msg);
if (ret) {
dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret);
return ret;
@@ -1742,7 +1841,7 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
devm_free_irq(hba->dev, desc->irq, hba);
}
msi_unlock_descs(hba->dev);
- platform_msi_domain_free_irqs(hba->dev);
+ platform_device_msi_free_irqs_all(hba->dev);
} else {
if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 &&
host->hw_ver.step == 0)
@@ -1773,13 +1872,12 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
.link_startup_notify = ufs_qcom_link_startup_notify,
.pwr_change_notify = ufs_qcom_pwr_change_notify,
.apply_dev_quirks = ufs_qcom_apply_dev_quirks,
+ .fixup_dev_quirks = ufs_qcom_fixup_dev_quirks,
.suspend = ufs_qcom_suspend,
.resume = ufs_qcom_resume,
.dbg_register_dump = ufs_qcom_dump_dbg_regs,
.device_reset = ufs_qcom_device_reset,
.config_scaling_param = ufs_qcom_config_scaling_param,
- .program_key = ufs_qcom_ice_program_key,
- .reinit_notify = ufs_qcom_reinit_notify,
.mcq_config_resource = ufs_qcom_mcq_config_resource,
.get_hba_mac = ufs_qcom_get_hba_mac,
.op_runtime_config = ufs_qcom_op_runtime_config,
@@ -1815,14 +1913,22 @@ static int ufs_qcom_probe(struct platform_device *pdev)
static void ufs_qcom_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
- pm_runtime_get_sync(&(pdev)->dev);
- ufshcd_remove(hba);
- platform_msi_domain_free_irqs(hba->dev);
+ ufshcd_pltfrm_remove(pdev);
+ if (host->esi_enabled)
+ platform_device_msi_free_irqs_all(hba->dev);
}
+static const struct ufs_qcom_drvdata ufs_qcom_sm8550_drvdata = {
+ .quirks = UFSHCD_QUIRK_BROKEN_LSDBS_CAP,
+ .no_phy_retention = true,
+};
+
static const struct of_device_id ufs_qcom_of_match[] __maybe_unused = {
- { .compatible = "qcom,ufshc"},
+ { .compatible = "qcom,ufshc" },
+ { .compatible = "qcom,sm8550-ufshc", .data = &ufs_qcom_sm8550_drvdata },
+ { .compatible = "qcom,sm8650-ufshc", .data = &ufs_qcom_sm8550_drvdata },
{},
};
MODULE_DEVICE_TABLE(of, ufs_qcom_of_match);
@@ -1850,7 +1956,7 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = {
static struct platform_driver ufs_qcom_pltform = {
.probe = ufs_qcom_probe,
- .remove_new = ufs_qcom_remove,
+ .remove = ufs_qcom_remove,
.driver = {
.name = "ufshcd-qcom",
.pm = &ufs_qcom_pm_ops,
@@ -1860,4 +1966,5 @@ static struct platform_driver ufs_qcom_pltform = {
};
module_platform_driver(ufs_qcom_pltform);
+MODULE_DESCRIPTION("Qualcomm UFS host controller driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
index 9dd9a391ebb7..919f53682beb 100644
--- a/drivers/ufs/host/ufs-qcom.h
+++ b/drivers/ufs/host/ufs-qcom.h
@@ -151,10 +151,10 @@ static inline void ufs_qcom_assert_reset(struct ufs_hba *hba)
ufshcd_rmwl(hba, UFS_PHY_SOFT_RESET, UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
/*
- * Make sure assertion of ufs phy reset is written to
- * register before returning
+ * Dummy read to ensure the write takes effect before doing any sort
+ * of delay
*/
- mb();
+ ufshcd_readl(hba, REG_UFS_CFG1);
}
static inline void ufs_qcom_deassert_reset(struct ufs_hba *hba)
@@ -162,10 +162,10 @@ static inline void ufs_qcom_deassert_reset(struct ufs_hba *hba)
ufshcd_rmwl(hba, UFS_PHY_SOFT_RESET, 0, REG_UFS_CFG1);
/*
- * Make sure de-assertion of ufs phy reset is written to
- * register before returning
+ * Dummy read to ensure the write takes effect before doing any sort
+ * of delay
*/
- mb();
+ ufshcd_readl(hba, REG_UFS_CFG1);
}
/* Host controller hardware version: major.minor.step */
@@ -217,6 +217,11 @@ struct ufs_qcom_host {
bool esi_enabled;
};
+struct ufs_qcom_drvdata {
+ enum ufshcd_quirks quirks;
+ bool no_phy_retention;
+};
+
static inline u32
ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg)
{
diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c
index 8711e5cbc968..03cd82db751b 100644
--- a/drivers/ufs/host/ufs-renesas.c
+++ b/drivers/ufs/host/ufs-renesas.c
@@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -364,14 +365,20 @@ static int ufs_renesas_init(struct ufs_hba *hba)
return -ENOMEM;
ufshcd_set_variant(hba, priv);
- hba->quirks |= UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS | UFSHCD_QUIRK_HIBERN_FASTAUTO;
+ hba->quirks |= UFSHCD_QUIRK_HIBERN_FASTAUTO;
return 0;
}
+static int ufs_renesas_set_dma_mask(struct ufs_hba *hba)
+{
+ return dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(32));
+}
+
static const struct ufs_hba_variant_ops ufs_renesas_vops = {
.name = "renesas",
.init = ufs_renesas_init,
+ .set_dma_mask = ufs_renesas_set_dma_mask,
.setup_clocks = ufs_renesas_setup_clocks,
.hce_enable_notify = ufs_renesas_hce_enable_notify,
.dbg_register_dump = ufs_renesas_dbg_register_dump,
@@ -390,14 +397,12 @@ static int ufs_renesas_probe(struct platform_device *pdev)
static void ufs_renesas_remove(struct platform_device *pdev)
{
- struct ufs_hba *hba = platform_get_drvdata(pdev);
-
- ufshcd_remove(hba);
+ ufshcd_pltfrm_remove(pdev);
}
static struct platform_driver ufs_renesas_platform = {
.probe = ufs_renesas_probe,
- .remove_new = ufs_renesas_remove,
+ .remove = ufs_renesas_remove,
.driver = {
.name = "ufshcd-renesas",
.of_match_table = of_match_ptr(ufs_renesas_of_match),
diff --git a/drivers/ufs/host/ufs-sprd.c b/drivers/ufs/host/ufs-sprd.c
index d8b165908809..b1d532363f9d 100644
--- a/drivers/ufs/host/ufs-sprd.c
+++ b/drivers/ufs/host/ufs-sprd.c
@@ -427,10 +427,7 @@ static int ufs_sprd_probe(struct platform_device *pdev)
static void ufs_sprd_remove(struct platform_device *pdev)
{
- struct ufs_hba *hba = platform_get_drvdata(pdev);
-
- pm_runtime_get_sync(&(pdev)->dev);
- ufshcd_remove(hba);
+ ufshcd_pltfrm_remove(pdev);
}
static const struct dev_pm_ops ufs_sprd_pm_ops = {
@@ -442,7 +439,7 @@ static const struct dev_pm_ops ufs_sprd_pm_ops = {
static struct platform_driver ufs_sprd_pltform = {
.probe = ufs_sprd_probe,
- .remove_new = ufs_sprd_remove,
+ .remove = ufs_sprd_remove,
.driver = {
.name = "ufshcd-sprd",
.pm = &ufs_sprd_pm_ops,
diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c
index 0aca666d2199..9cfcaad23cf9 100644
--- a/drivers/ufs/host/ufshcd-pci.c
+++ b/drivers/ufs/host/ufshcd-pci.c
@@ -20,6 +20,8 @@
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
+#define MAX_SUPP_MAC 64
+
struct ufs_host {
void (*late_init)(struct ufs_hba *hba);
};
@@ -446,6 +448,49 @@ static int ufs_intel_mtl_init(struct ufs_hba *hba)
return ufs_intel_common_init(hba);
}
+static int ufs_qemu_get_hba_mac(struct ufs_hba *hba)
+{
+ return MAX_SUPP_MAC;
+}
+
+static int ufs_qemu_mcq_config_resource(struct ufs_hba *hba)
+{
+ hba->mcq_base = hba->mmio_base + ufshcd_mcq_queue_cfg_addr(hba);
+
+ return 0;
+}
+
+static int ufs_qemu_op_runtime_config(struct ufs_hba *hba)
+{
+ struct ufshcd_mcq_opr_info_t *opr;
+ int i;
+
+ u32 sqdao = ufsmcq_readl(hba, ufshcd_mcq_cfg_offset(REG_SQDAO, 0));
+ u32 sqisao = ufsmcq_readl(hba, ufshcd_mcq_cfg_offset(REG_SQISAO, 0));
+ u32 cqdao = ufsmcq_readl(hba, ufshcd_mcq_cfg_offset(REG_CQDAO, 0));
+ u32 cqisao = ufsmcq_readl(hba, ufshcd_mcq_cfg_offset(REG_CQISAO, 0));
+
+ hba->mcq_opr[OPR_SQD].offset = sqdao;
+ hba->mcq_opr[OPR_SQIS].offset = sqisao;
+ hba->mcq_opr[OPR_CQD].offset = cqdao;
+ hba->mcq_opr[OPR_CQIS].offset = cqisao;
+
+ for (i = 0; i < OPR_MAX; i++) {
+ opr = &hba->mcq_opr[i];
+ opr->stride = 48;
+ opr->base = hba->mmio_base + opr->offset;
+ }
+
+ return 0;
+}
+
+static struct ufs_hba_variant_ops ufs_qemu_hba_vops = {
+ .name = "qemu-pci",
+ .get_hba_mac = ufs_qemu_get_hba_mac,
+ .mcq_config_resource = ufs_qemu_mcq_config_resource,
+ .op_runtime_config = ufs_qemu_op_runtime_config,
+};
+
static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = {
.name = "intel-pci",
.init = ufs_intel_common_init,
@@ -517,7 +562,6 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
ufshcd_remove(hba);
- ufshcd_dealloc_host(hba);
}
/**
@@ -543,14 +587,12 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_master(pdev);
- err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD);
- if (err < 0) {
+ mmio_base = pcim_iomap_region(pdev, 0, UFSHCD);
+ if (IS_ERR(mmio_base)) {
dev_err(&pdev->dev, "request and iomap failed\n");
- return err;
+ return PTR_ERR(mmio_base);
}
- mmio_base = pcim_iomap_table(pdev)[0];
-
err = ufshcd_alloc_host(&pdev->dev, &hba);
if (err) {
dev_err(&pdev->dev, "Allocation failed\n");
@@ -562,7 +604,6 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
err = ufshcd_init(hba, mmio_base, pdev->irq);
if (err) {
dev_err(&pdev->dev, "Initialization failed\n");
- ufshcd_dealloc_host(hba);
return err;
}
@@ -591,7 +632,8 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = {
};
static const struct pci_device_id ufshcd_pci_tbl[] = {
- { PCI_VENDOR_ID_REDHAT, 0x0013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { PCI_VENDOR_ID_REDHAT, 0x0013, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ (kernel_ulong_t)&ufs_qemu_hba_vops },
{ PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops },
{ PCI_VDEVICE(INTEL, 0x4B41), (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
@@ -602,6 +644,7 @@ static const struct pci_device_id ufshcd_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x7E47), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
{ PCI_VDEVICE(INTEL, 0xA847), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
{ PCI_VDEVICE(INTEL, 0x7747), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0xE447), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
{ } /* terminate list */
};
diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
index a3e69ecafd27..ffe5d1d2b215 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.c
+++ b/drivers/ufs/host/ufshcd-pltfrm.c
@@ -31,8 +31,7 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba)
const char *name;
u32 *clkfreq = NULL;
struct ufs_clk_info *clki;
- int len = 0;
- size_t sz = 0;
+ ssize_t sz = 0;
if (!np)
goto out;
@@ -50,15 +49,12 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba)
if (cnt <= 0)
goto out;
- if (!of_get_property(np, "freq-table-hz", &len)) {
+ sz = of_property_count_u32_elems(np, "freq-table-hz");
+ if (sz <= 0) {
dev_info(dev, "freq-table-hz property not specified\n");
goto out;
}
- if (len <= 0)
- goto out;
-
- sz = len / sizeof(*clkfreq);
if (sz != 2 * cnt) {
dev_err(dev, "%s len mismatch\n", "freq-table-hz");
ret = -EINVAL;
@@ -272,10 +268,10 @@ static int ufshcd_parse_operating_points(struct ufs_hba *hba)
const char **clk_names;
int cnt, i, ret;
- if (!of_find_property(np, "operating-points-v2", NULL))
+ if (!of_property_present(np, "operating-points-v2"))
return 0;
- if (of_find_property(np, "freq-table-hz", NULL)) {
+ if (of_property_present(np, "freq-table-hz")) {
dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n",
__func__);
return -EINVAL;
@@ -469,21 +465,17 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
struct device *dev = &pdev->dev;
mmio_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(mmio_base)) {
- err = PTR_ERR(mmio_base);
- goto out;
- }
+ if (IS_ERR(mmio_base))
+ return PTR_ERR(mmio_base);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- err = irq;
- goto out;
- }
+ if (irq < 0)
+ return irq;
err = ufshcd_alloc_host(dev, &hba);
if (err) {
dev_err(dev, "Allocation failed\n");
- goto out;
+ return err;
}
hba->vops = vops;
@@ -492,13 +484,13 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
if (err) {
dev_err(dev, "%s: clock parse failed %d\n",
__func__, err);
- goto dealloc_host;
+ return err;
}
err = ufshcd_parse_regulator_info(hba);
if (err) {
dev_err(dev, "%s: regulator init failed %d\n",
__func__, err);
- goto dealloc_host;
+ return err;
}
ufshcd_init_lanes_per_dir(hba);
@@ -506,28 +498,38 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
err = ufshcd_parse_operating_points(hba);
if (err) {
dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
- goto dealloc_host;
+ return err;
}
err = ufshcd_init(hba, mmio_base, irq);
if (err) {
dev_err_probe(dev, err, "Initialization failed with error %d\n",
err);
- goto dealloc_host;
+ return err;
}
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
-
-dealloc_host:
- ufshcd_dealloc_host(hba);
-out:
- return err;
}
EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
+/**
+ * ufshcd_pltfrm_remove - Remove ufshcd platform
+ * @pdev: pointer to Platform device handle
+ */
+void ufshcd_pltfrm_remove(struct platform_device *pdev)
+{
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ ufshcd_remove(hba);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+}
+EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
+
MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h
index df387be5216b..3017f8e8f93c 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.h
+++ b/drivers/ufs/host/ufshcd-pltfrm.h
@@ -31,6 +31,7 @@ int ufshcd_negotiate_pwr_params(const struct ufs_host_params *host_params,
void ufshcd_init_host_params(struct ufs_host_params *host_params);
int ufshcd_pltfrm_init(struct platform_device *pdev,
const struct ufs_hba_variant_ops *vops);
+void ufshcd_pltfrm_remove(struct platform_device *pdev);
int ufshcd_populate_vreg(struct device *dev, const char *name,
struct ufs_vreg **out_vreg, bool skip_current);