diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c index b4fc834a4edb..d600e457ad2e 100644 --- a/drivers/firmware/qcom_scm-smc.c +++ b/drivers/firmware/qcom_scm-smc.c @@ -735,6 +735,24 @@ int __qcom_scm_sec_wdog_trigger(struct device *dev) return ret ? : desc.res[0]; } +void __qcom_scm_disable_sdi(struct device *dev) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_WDOG_DEBUG_PART, + .owner = ARM_SMCCC_OWNER_SIP + }; + + desc.args[0] = 1; + desc.args[1] = 0; + desc.arginfo = QCOM_SCM_ARGS(2); + + ret = qcom_scm_call_atomic(dev, &desc); + if (ret) + pr_err("Failed to disable secure wdog debug: %d\n", ret); +} + int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) { struct qcom_scm_desc desc = { @@ -1039,6 +1057,34 @@ int __qcom_scm_get_feat_version(struct device *dev, u64 feat_id, u64 *version) return ret; } +void __qcom_scm_halt_spmi_pmic_arbiter(struct device *dev) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PWR, + .cmd = QCOM_SCM_PWR_IO_DISABLE_PMIC_ARBITER, + .owner = ARM_SMCCC_OWNER_SIP + }; + + desc.args[0] = 0; + desc.arginfo = QCOM_SCM_ARGS(1); + + qcom_scm_call_atomic(dev, &desc); +} + +void __qcom_scm_deassert_ps_hold(struct device *dev) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PWR, + .cmd = QCOM_SCM_PWR_IO_DEASSERT_PS_HOLD, + .owner = ARM_SMCCC_OWNER_SIP + }; + + desc.args[0] = 0; + desc.arginfo = QCOM_SCM_ARGS(1); + + qcom_scm_call_atomic(dev, &desc); +} + void __qcom_scm_mmu_sync(struct device *dev, bool sync) { int ret; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 52b11cda2673..ac7249b671e7 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -128,6 +128,15 @@ int qcom_scm_sec_wdog_trigger(void) } EXPORT_SYMBOL(qcom_scm_sec_wdog_trigger); +/** + * qcom_scm_disable_sdi() - Disable SDI + */ +void qcom_scm_disable_sdi(void) +{ + __qcom_scm_disable_sdi(__scm ? __scm->dev : NULL); +} +EXPORT_SYMBOL(qcom_scm_disable_sdi); + int qcom_scm_set_remote_state(u32 state, u32 id) { return __qcom_scm_set_remote_state(__scm->dev, state, id); @@ -140,26 +149,29 @@ int qcom_scm_spin_cpu(void) } EXPORT_SYMBOL(qcom_scm_spin_cpu); -void qcom_scm_set_download_mode(enum qcom_download_mode mode) +void qcom_scm_set_download_mode(enum qcom_download_mode mode, + phys_addr_t tcsr_boot_misc) { bool avail; int ret = 0; + struct device *dev = __scm ? __scm->dev : NULL; - avail = __qcom_scm_is_call_available(__scm->dev, + avail = __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_SET_DLOAD_MODE); if (avail) { - ret = __qcom_scm_set_dload_mode(__scm->dev, mode); - } else if (__scm->dload_mode_addr) { - ret = __qcom_scm_io_writel(__scm->dev, __scm->dload_mode_addr, - mode); + ret = __qcom_scm_set_dload_mode(dev, mode); + } else if (tcsr_boot_misc || (__scm && __scm->dload_mode_addr)) { + ret = __qcom_scm_io_writel(dev, + tcsr_boot_misc ? : __scm->dload_mode_addr, + mode); } else { - dev_err(__scm->dev, + dev_err(dev, "No available mechanism for setting download mode\n"); } if (ret) - dev_err(__scm->dev, "failed to set download mode: %d\n", ret); + dev_err(dev, "failed to set download mode: %d\n", ret); } EXPORT_SYMBOL(qcom_scm_set_download_mode); @@ -380,6 +392,52 @@ int qcom_scm_get_jtag_etm_feat_id(u64 *version) } EXPORT_SYMBOL(qcom_scm_get_jtag_etm_feat_id); +/** + * qcom_halt_spmi_pmic_arbiter() - Halt SPMI PMIC arbiter + * + * Force the SPMI PMIC arbiter to shutdown so that no more SPMI transactions + * are sent from the MSM to the PMIC. This is required in order to avoid an + * SPMI lockup on certain PMIC chips if PS_HOLD is lowered in the middle of + * an SPMI transaction. + */ +void qcom_scm_halt_spmi_pmic_arbiter(void) +{ + bool avail; + struct device *dev = __scm ? __scm->dev : NULL; + + avail = __qcom_scm_is_call_available(dev, + QCOM_SCM_SVC_PWR, + QCOM_SCM_PWR_IO_DISABLE_PMIC_ARBITER); + + if (!avail) + return; + + pr_crit("Calling SCM to disable SPMI PMIC arbiter\n"); + return __qcom_scm_halt_spmi_pmic_arbiter(dev); +} +EXPORT_SYMBOL(qcom_scm_halt_spmi_pmic_arbiter); + +/** + * qcom_deassert_ps_hold() - Deassert PS_HOLD + * + * Deassert PS_HOLD to signal the PMIC that we are ready to power down or reset. + * + * This function should never return if the SCM call is available. + */ +void qcom_scm_deassert_ps_hold(void) +{ + bool avail; + struct device *dev = __scm ? __scm->dev : NULL; + + avail = __qcom_scm_is_call_available(dev, + QCOM_SCM_SVC_PWR, + QCOM_SCM_PWR_IO_DEASSERT_PS_HOLD); + + if (avail) + __qcom_scm_deassert_ps_hold(dev); +} +EXPORT_SYMBOL(qcom_scm_deassert_ps_hold); + void qcom_scm_mmu_sync(bool sync) { __qcom_scm_mmu_sync(__scm ? __scm->dev : NULL, sync); diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 99251c1518ea..32e84fff6f98 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -10,6 +10,7 @@ #define QCOM_SCM_BOOT_TERMINATE_PC 0x02 #define QCOM_SCM_BOOT_SEC_WDOG_DIS 0x07 #define QCOM_SCM_BOOT_SEC_WDOG_TRIGGER 0x08 +#define QCOM_SCM_BOOT_WDOG_DEBUG_PART 0x09 #define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a #define QCOM_SCM_BOOT_SPIN_CPU 0x0d #define QCOM_SCM_BOOT_SWITCH_MODE 0x0f @@ -22,6 +23,7 @@ extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, extern void __qcom_scm_cpu_power_down(struct device *dev, u32 flags); extern int __qcom_scm_sec_wdog_deactivate(struct device *dev); extern int __qcom_scm_sec_wdog_trigger(struct device *dev); +extern void __qcom_scm_disable_sdi(struct device *dev); extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id); extern int __qcom_scm_spin_cpu(struct device *dev); extern int __qcom_scm_set_dload_mode(struct device *dev, @@ -73,7 +75,11 @@ extern int __qcom_scm_get_feat_version(struct device *dev, u64 feat_id, #define QCOM_SCM_MP_CP_FEAT_ID 0x0c #define QCOM_SCM_SVC_PWR 0x09 +#define QCOM_SCM_PWR_IO_DISABLE_PMIC_ARBITER 0x01 +#define QCOM_SCM_PWR_IO_DEASSERT_PS_HOLD 0x02 #define QCOM_SCM_PWR_MMU_SYNC 0x08 +extern void __qcom_scm_halt_spmi_pmic_arbiter(struct device *dev); +extern void __qcom_scm_deassert_ps_hold(struct device *dev); extern void __qcom_scm_mmu_sync(struct device *dev, bool sync); #define QCOM_SCM_SVC_MP 0x0c diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 067951d0f8af..c403f7a3ca16 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -83,9 +83,11 @@ extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); extern void qcom_scm_cpu_power_down(u32 flags); extern int qcom_scm_sec_wdog_deactivate(void); extern int qcom_scm_sec_wdog_trigger(void); +extern void qcom_scm_disable_sdi(void); extern int qcom_scm_set_remote_state(u32 state, u32 id); extern int qcom_scm_spin_cpu(void); -extern void qcom_scm_set_download_mode(enum qcom_download_mode mode); +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 bool qcom_scm_pas_supported(u32 peripheral); extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, @@ -102,6 +104,8 @@ extern int qcom_scm_io_reset(void); extern bool qcom_scm_is_secure_wdog_trigger_available(void); extern bool qcom_scm_is_mode_switch_available(void); extern int qcom_scm_get_jtag_etm_feat_id(u64 *version); +extern void qcom_scm_halt_spmi_pmic_arbiter(void); +extern void qcom_scm_deassert_ps_hold(void); extern void qcom_scm_mmu_sync(bool sync); extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); @@ -198,10 +202,12 @@ int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) static inline void qcom_scm_cpu_power_down(u32 flags) {} static inline int qcom_scm_sec_wdog_deactivate(void) { return -ENODEV; } static inline int qcom_scm_sec_wdog_trigger(void) { return -ENODEV; } +static inline void qcom_scm_disable_sdi(void) {} static inline u32 qcom_scm_set_remote_state(u32 state, u32 id) { return -ENODEV; } static inline int qcom_scm_spin_cpu(void) { return -ENODEV; } -static inline void qcom_scm_set_download_mode(enum qcom_download_mode mode) {} +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 bool qcom_scm_pas_supported(u32 peripheral) { return false; } @@ -228,6 +234,8 @@ static inline bool qcom_scm_is_mode_switch_available(void) { return -ENODEV; } static inline int qcom_scm_get_jtag_etm_feat_id(u64 *version) { return -ENODEV; } +static inline void qcom_scm_halt_spmi_pmic_arbiter(void) {} +static inline void qcom_scm_deassert_ps_hold(void) {} static inline void qcom_scm_mmu_sync(bool sync) {} static inline int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) { return -ENODEV; }