diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c index 6140b04f079d..27cce454c86a 100644 --- a/drivers/firmware/qcom_scm-smc.c +++ b/drivers/firmware/qcom_scm-smc.c @@ -816,6 +816,23 @@ int __qcom_scm_config_cpu_errata(struct device *dev) return qcom_scm_call(dev, &desc); } +void __qcom_scm_phy_update_scm_level_shifter(struct device *dev, u32 val) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_QUSB2PHY_LVL_SHIFTER_CMD_ID, + .owner = ARM_SMCCC_OWNER_SIP + }; + + desc.args[0] = val; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, &desc); + if (ret) + pr_err("Failed to update scm level shifter=0x%x\n", ret); +} + bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) { int ret; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 5c4c4e8288a6..0b20d4af4c09 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -174,6 +174,14 @@ int qcom_scm_config_cpu_errata(void) } EXPORT_SYMBOL(qcom_scm_config_cpu_errata); +void qcom_scm_phy_update_scm_level_shifter(u32 val) +{ + struct device *dev = __scm ? __scm->dev : NULL; + + __qcom_scm_phy_update_scm_level_shifter(dev, val); +} +EXPORT_SYMBOL(qcom_scm_phy_update_scm_level_shifter); + /** * qcom_scm_pas_supported() - Check if the peripheral authentication service is * available for the given peripherial diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 5f38a4f99661..60ba9b8a67a9 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -16,6 +16,7 @@ #define QCOM_SCM_BOOT_SWITCH_MODE 0x0f #define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10 #define QCOM_SCM_BOOT_CONFIG_CPU_ERRATA 0x12 +#define QCOM_SCM_QUSB2PHY_LVL_SHIFTER_CMD_ID 0x1B extern int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry, const cpumask_t *cpus); extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, @@ -29,6 +30,7 @@ extern int __qcom_scm_spin_cpu(struct device *dev); extern int __qcom_scm_set_dload_mode(struct device *dev, enum qcom_download_mode mode); extern int __qcom_scm_config_cpu_errata(struct device *dev); +extern void __qcom_scm_phy_update_scm_level_shifter(struct device *dev, u32 val); #define QCOM_SCM_FLUSH_FLAG_MASK 0x3 #define QCOM_SCM_SVC_PIL 0x02 diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index 605448d72239..cda987f2b366 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved. */ #include @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -129,6 +131,7 @@ struct qusb_phy { bool ulpi_mode; bool dpdm_enable; bool is_se_clk; + bool scm_lvl_shifter; struct regulator_desc dpdm_rdesc; struct regulator_dev *dpdm_rdev; @@ -145,6 +148,18 @@ struct qusb_phy { u8 tune5; }; +static void qusb_phy_update_tcsr_level_shifter(struct qusb_phy *qphy, + u32 val) +{ + if (qphy->tcsr_clamp_dig_n) { + writel_relaxed(val, qphy->tcsr_clamp_dig_n); + dev_dbg(qphy->phy.dev, "update tcsr level shifter: %d\n", val); + } else if (qphy->scm_lvl_shifter) { + dev_dbg(qphy->phy.dev, "update scm level shifter: %d\n", val); + qcom_scm_phy_update_scm_level_shifter(val); + } +} + static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) { dev_dbg(qphy->phy.dev, "%s(): on:%d\n", __func__, on); @@ -662,9 +677,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) /* Make sure that above write is completed */ wmb(); - if (qphy->tcsr_clamp_dig_n) - writel_relaxed(0x0, - qphy->tcsr_clamp_dig_n); + qusb_phy_update_tcsr_level_shifter(qphy, 0); } qusb_phy_enable_clocks(qphy, false); @@ -690,9 +703,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) qphy->base + QUSB2PHY_PORT_INTR_CTRL); } else { qusb_phy_enable_power(qphy, true); - if (qphy->tcsr_clamp_dig_n) - writel_relaxed(0x1, - qphy->tcsr_clamp_dig_n); + qusb_phy_update_tcsr_level_shifter(qphy, 1); qusb_phy_enable_clocks(qphy, true); } qphy->suspended = false; @@ -749,9 +760,7 @@ static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev) } qphy->dpdm_enable = true; if (qphy->put_into_high_z_state) { - if (qphy->tcsr_clamp_dig_n) - writel_relaxed(0x1, - qphy->tcsr_clamp_dig_n); + qusb_phy_update_tcsr_level_shifter(qphy, 1); qusb_phy_gdsc(qphy, true); qusb_phy_enable_clocks(qphy, true); @@ -809,9 +818,7 @@ static int qusb_phy_dpdm_regulator_disable(struct regulator_dev *rdev) if (qphy->dpdm_enable) { /* If usb core is active, rely on set_suspend to clamp phy */ if (!qphy->cable_connected) { - if (qphy->tcsr_clamp_dig_n) - writel_relaxed(0x0, - qphy->tcsr_clamp_dig_n); + qusb_phy_update_tcsr_level_shifter(qphy, 0); } ret = qusb_phy_enable_power(qphy, false); if (ret < 0) { @@ -972,6 +979,9 @@ static int qusb_phy_probe(struct platform_device *pdev) } } + qphy->scm_lvl_shifter = of_property_read_bool(dev->of_node, + "qcom,secure-level-shifter"); + ret = of_property_read_u32(dev->of_node, "qcom,usb-hs-ac-bitmask", &qphy->usb_hs_ac_bitmask); if (!ret) { diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index d87a296c9f40..2f73d491dc71 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -90,6 +90,7 @@ extern int qcom_scm_spin_cpu(void); extern void qcom_scm_set_download_mode(enum qcom_download_mode mode, phys_addr_t tcsr_boot_misc); extern int qcom_scm_config_cpu_errata(void); +extern void qcom_scm_phy_update_scm_level_shifter(u32 val); extern bool qcom_scm_pas_supported(u32 peripheral); extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size); @@ -219,6 +220,7 @@ static inline void qcom_scm_set_download_mode(enum qcom_download_mode mode, phys_addr_t tcsr_boot_misc) {} static inline int qcom_scm_config_cpu_errata(void) { return -ENODEV; } +static inline void qcom_scm_phy_update_scm_level_shifter(u32 val) {} static inline bool qcom_scm_pas_supported(u32 peripheral) { return false; } static inline int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size) { return -ENODEV; }