1414#include <linux/of.h>
1515#include <linux/phy/phy.h>
1616#include <linux/platform_device.h>
17+ #include <linux/pm_domain.h>
1718#include <linux/reset-controller.h>
1819#include <linux/time.h>
1920#include <linux/unaligned.h>
@@ -619,6 +620,27 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
619620 return err ;
620621}
621622
623+ static int ufs_qcom_fw_managed_hce_enable_notify (struct ufs_hba * hba ,
624+ enum ufs_notify_change_status status )
625+ {
626+ struct ufs_qcom_host * host = ufshcd_get_variant (hba );
627+
628+ switch (status ) {
629+ case PRE_CHANGE :
630+ ufs_qcom_select_unipro_mode (host );
631+ break ;
632+ case POST_CHANGE :
633+ ufs_qcom_enable_hw_clk_gating (hba );
634+ ufs_qcom_ice_enable (host );
635+ break ;
636+ default :
637+ dev_err (hba -> dev , "Invalid status %d\n" , status );
638+ return - EINVAL ;
639+ }
640+
641+ return 0 ;
642+ }
643+
622644/**
623645 * ufs_qcom_cfg_timers - Configure ufs qcom cfg timers
624646 *
@@ -789,6 +811,33 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
789811 return ufs_qcom_ice_resume (host );
790812}
791813
814+ static int ufs_qcom_fw_managed_suspend (struct ufs_hba * hba , enum ufs_pm_op pm_op ,
815+ enum ufs_notify_change_status status )
816+ {
817+ struct ufs_qcom_host * host = ufshcd_get_variant (hba );
818+
819+ if (status == PRE_CHANGE )
820+ return 0 ;
821+
822+ pm_runtime_put_sync (hba -> dev );
823+
824+ return ufs_qcom_ice_suspend (host );
825+ }
826+
827+ static int ufs_qcom_fw_managed_resume (struct ufs_hba * hba , enum ufs_pm_op pm_op )
828+ {
829+ struct ufs_qcom_host * host = ufshcd_get_variant (hba );
830+ int err ;
831+
832+ err = pm_runtime_resume_and_get (hba -> dev );
833+ if (err ) {
834+ dev_err (hba -> dev , "PM runtime resume failed: %d\n" , err );
835+ return err ;
836+ }
837+
838+ return ufs_qcom_ice_resume (host );
839+ }
840+
792841static void ufs_qcom_dev_ref_clk_ctrl (struct ufs_qcom_host * host , bool enable )
793842{
794843 if (host -> dev_ref_clk_ctrl_mmio &&
@@ -1421,6 +1470,54 @@ static void ufs_qcom_exit(struct ufs_hba *hba)
14211470 phy_exit (host -> generic_phy );
14221471}
14231472
1473+ static int ufs_qcom_fw_managed_init (struct ufs_hba * hba )
1474+ {
1475+ struct device * dev = hba -> dev ;
1476+ struct ufs_qcom_host * host ;
1477+ int err ;
1478+
1479+ host = devm_kzalloc (dev , sizeof (* host ), GFP_KERNEL );
1480+ if (!host )
1481+ return - ENOMEM ;
1482+
1483+ host -> hba = hba ;
1484+ ufshcd_set_variant (hba , host );
1485+
1486+ ufs_qcom_get_controller_revision (hba , & host -> hw_ver .major ,
1487+ & host -> hw_ver .minor , & host -> hw_ver .step );
1488+
1489+ err = ufs_qcom_ice_init (host );
1490+ if (err )
1491+ goto out_variant_clear ;
1492+
1493+ ufs_qcom_get_default_testbus_cfg (host );
1494+ err = ufs_qcom_testbus_config (host );
1495+ if (err )
1496+ /* Failure is non-fatal */
1497+ dev_warn (dev , "Failed to configure the testbus %d\n" , err );
1498+
1499+ hba -> caps |= UFSHCD_CAP_WB_EN ;
1500+
1501+ ufs_qcom_advertise_quirks (hba );
1502+ host -> hba -> quirks &= ~UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH ;
1503+
1504+ hba -> spm_lvl = hba -> rpm_lvl = hba -> pm_lvl_min = UFS_PM_LVL_5 ;
1505+
1506+ ufs_qcom_set_host_params (hba );
1507+ ufs_qcom_parse_gear_limits (hba );
1508+
1509+ return 0 ;
1510+
1511+ out_variant_clear :
1512+ ufshcd_set_variant (hba , NULL );
1513+ return err ;
1514+ }
1515+
1516+ static void ufs_qcom_fw_managed_exit (struct ufs_hba * hba )
1517+ {
1518+ pm_runtime_put_sync (hba -> dev );
1519+ }
1520+
14241521/**
14251522 * ufs_qcom_set_clk_40ns_cycles - Configure 40ns clk cycles
14261523 *
@@ -1950,6 +2047,37 @@ static int ufs_qcom_device_reset(struct ufs_hba *hba)
19502047 return 0 ;
19512048}
19522049
2050+ /**
2051+ * ufs_qcom_fw_managed_device_reset - Reset UFS device under FW-managed design
2052+ * @hba: pointer to UFS host bus adapter
2053+ *
2054+ * In the firmware-managed reset model, the power domain is powered on by genpd
2055+ * before the UFS controller driver probes. For subsequent resets (such as
2056+ * suspend/resume or recovery), the UFS driver must explicitly invoke PM runtime
2057+ *
2058+ * Return: 0 on success or a negative error code on failure.
2059+ */
2060+ static int ufs_qcom_fw_managed_device_reset (struct ufs_hba * hba )
2061+ {
2062+ static bool is_boot = true;
2063+ int err ;
2064+
2065+ /* Skip reset on cold boot; perform it on subsequent calls */
2066+ if (is_boot ) {
2067+ is_boot = false;
2068+ return 0 ;
2069+ }
2070+
2071+ pm_runtime_put_sync (hba -> dev );
2072+ err = pm_runtime_resume_and_get (hba -> dev );
2073+ if (err < 0 ) {
2074+ dev_err (hba -> dev , "PM runtime resume failed: %d\n" , err );
2075+ return err ;
2076+ }
2077+
2078+ return 0 ;
2079+ }
2080+
19532081static void ufs_qcom_config_scaling_param (struct ufs_hba * hba ,
19542082 struct devfreq_dev_profile * p ,
19552083 struct devfreq_simple_ondemand_data * d )
@@ -2229,6 +2357,20 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
22292357 .freq_to_gear_speed = ufs_qcom_freq_to_gear_speed ,
22302358};
22312359
2360+ static const struct ufs_hba_variant_ops ufs_hba_qcom_sa8255p_vops = {
2361+ .name = "qcom-sa8255p" ,
2362+ .init = ufs_qcom_fw_managed_init ,
2363+ .exit = ufs_qcom_fw_managed_exit ,
2364+ .hce_enable_notify = ufs_qcom_fw_managed_hce_enable_notify ,
2365+ .pwr_change_notify = ufs_qcom_pwr_change_notify ,
2366+ .apply_dev_quirks = ufs_qcom_apply_dev_quirks ,
2367+ .fixup_dev_quirks = ufs_qcom_fixup_dev_quirks ,
2368+ .suspend = ufs_qcom_fw_managed_suspend ,
2369+ .resume = ufs_qcom_fw_managed_resume ,
2370+ .dbg_register_dump = ufs_qcom_dump_dbg_regs ,
2371+ .device_reset = ufs_qcom_fw_managed_device_reset ,
2372+ };
2373+
22322374/**
22332375 * ufs_qcom_probe - probe routine of the driver
22342376 * @pdev: pointer to Platform device handle
@@ -2239,9 +2381,16 @@ static int ufs_qcom_probe(struct platform_device *pdev)
22392381{
22402382 int err ;
22412383 struct device * dev = & pdev -> dev ;
2384+ const struct ufs_hba_variant_ops * vops ;
2385+ const struct ufs_qcom_drvdata * drvdata = device_get_match_data (dev );
2386+
2387+ if (drvdata && drvdata -> vops )
2388+ vops = drvdata -> vops ;
2389+ else
2390+ vops = & ufs_hba_qcom_vops ;
22422391
22432392 /* Perform generic probe */
2244- err = ufshcd_pltfrm_init (pdev , & ufs_hba_qcom_vops );
2393+ err = ufshcd_pltfrm_init (pdev , vops );
22452394 if (err )
22462395 return dev_err_probe (dev , err , "ufshcd_pltfrm_init() failed\n" );
22472396
@@ -2269,10 +2418,15 @@ static const struct ufs_qcom_drvdata ufs_qcom_sm8550_drvdata = {
22692418 .no_phy_retention = true,
22702419};
22712420
2421+ static const struct ufs_qcom_drvdata ufs_qcom_sa8255p_drvdata = {
2422+ .vops = & ufs_hba_qcom_sa8255p_vops
2423+ };
2424+
22722425static const struct of_device_id ufs_qcom_of_match [] __maybe_unused = {
22732426 { .compatible = "qcom,ufshc" },
22742427 { .compatible = "qcom,sm8550-ufshc" , .data = & ufs_qcom_sm8550_drvdata },
22752428 { .compatible = "qcom,sm8650-ufshc" , .data = & ufs_qcom_sm8550_drvdata },
2429+ { .compatible = "qcom,sa8255p-ufshc" , .data = & ufs_qcom_sa8255p_drvdata },
22762430 {},
22772431};
22782432MODULE_DEVICE_TABLE (of , ufs_qcom_of_match );
0 commit comments