Skip to content

Commit 15df721

Browse files
Merge patch series "ufs: ufs-qcom: Add support firmware managed platforms"
Ram Kumar Dwivedi <ram.dwivedi@oss.qualcomm.com> says: On Qualcomm automotive SoC SA8255P, platform resource like clocks, interconnect, resets, regulators and PHY are configured remotely by firmware. Logical power domain is used to abstract these resources in firmware and SCMI power protocol is used to request resource operations by using runtime PM framework APIs such as pm_runtime_get/put_sync to invoke power_on/_off calls from kernel respectively. Link: https://patch.msgid.link/20260113080046.284089-1-ram.dwivedi@oss.qualcomm.com Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2 parents 92da381 + ad44cf1 commit 15df721

6 files changed

Lines changed: 216 additions & 3 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
2+
%YAML 1.2
3+
---
4+
$id: http://devicetree.org/schemas/ufs/qcom,sa8255p-ufshc.yaml#
5+
$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+
title: Qualcomm SA8255P UFS Host Controller
8+
9+
maintainers:
10+
- Ram Kumar Dwivedi <ram.dwivedi@oss.qualcomm.com>
11+
12+
properties:
13+
compatible:
14+
const: qcom,sa8255p-ufshc
15+
16+
reg:
17+
maxItems: 1
18+
19+
interrupts:
20+
maxItems: 1
21+
22+
iommus:
23+
maxItems: 1
24+
25+
dma-coherent: true
26+
27+
power-domains:
28+
maxItems: 1
29+
30+
required:
31+
- compatible
32+
- reg
33+
- interrupts
34+
- power-domains
35+
- iommus
36+
- dma-coherent
37+
38+
allOf:
39+
- $ref: ufs-common.yaml
40+
41+
unevaluatedProperties: false
42+
43+
examples:
44+
- |
45+
#include <dt-bindings/interrupt-controller/arm-gic.h>
46+
47+
ufshc@1d84000 {
48+
compatible = "qcom,sa8255p-ufshc";
49+
reg = <0x01d84000 0x3000>;
50+
interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>;
51+
lanes-per-direction = <2>;
52+
53+
iommus = <&apps_smmu 0x100 0x0>;
54+
power-domains = <&scmi3_pd 0>;
55+
dma-coherent;
56+
};

MAINTAINERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26803,7 +26803,7 @@ M: Manivannan Sadhasivam <mani@kernel.org>
2680326803
L: linux-arm-msm@vger.kernel.org
2680426804
L: linux-scsi@vger.kernel.org
2680526805
S: Maintained
26806-
F: Documentation/devicetree/bindings/ufs/qcom,ufs.yaml
26806+
F: Documentation/devicetree/bindings/ufs/qcom*
2680726807
F: drivers/ufs/host/ufs-qcom*
2680826808

2680926809
UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER RENESAS HOOKS

drivers/ufs/core/ufs-sysfs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ static inline ssize_t ufs_sysfs_pm_lvl_store(struct device *dev,
141141
if (kstrtoul(buf, 0, &value))
142142
return -EINVAL;
143143

144-
if (value >= UFS_PM_LVL_MAX)
144+
if (value >= UFS_PM_LVL_MAX || value < hba->pm_lvl_min)
145145
return -EINVAL;
146146

147147
if (ufs_pm_lvl_states[value].dev_state == UFS_DEEPSLEEP_PWR_MODE &&

drivers/ufs/host/ufs-qcom.c

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
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+
792841
static 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+
19532081
static 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+
22722425
static 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
};
22782432
MODULE_DEVICE_TABLE(of, ufs_qcom_of_match);

drivers/ufs/host/ufs-qcom.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ struct ufs_qcom_host {
313313
struct ufs_qcom_drvdata {
314314
enum ufshcd_quirks quirks;
315315
bool no_phy_retention;
316+
const struct ufs_hba_variant_ops *vops;
316317
};
317318

318319
static inline u32

include/ufs/ufshcd.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ enum ufshcd_mcq_opr {
834834
* @uic_link_state: active state of the link to the UFS device.
835835
* @rpm_lvl: desired UFS power management level during runtime PM.
836836
* @spm_lvl: desired UFS power management level during system PM.
837+
* @pm_lvl_min: minimum supported power management level.
837838
* @pm_op_in_progress: whether or not a PM operation is in progress.
838839
* @ahit: value of Auto-Hibernate Idle Timer register.
839840
* @outstanding_tasks: Bits representing outstanding task requests
@@ -972,6 +973,7 @@ struct ufs_hba {
972973
enum ufs_pm_level rpm_lvl;
973974
/* Desired UFS power management level during system PM */
974975
enum ufs_pm_level spm_lvl;
976+
enum ufs_pm_level pm_lvl_min;
975977
int pm_op_in_progress;
976978

977979
/* Auto-Hibernate Idle Timer register value */

0 commit comments

Comments
 (0)