Commit 15df721f authored by Martin K. Petersen's avatar Martin K. Petersen
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: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parents 92da3818 ad44cf1b
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/ufs/qcom,sa8255p-ufshc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Qualcomm SA8255P UFS Host Controller

maintainers:
  - Ram Kumar Dwivedi <ram.dwivedi@oss.qualcomm.com>

properties:
  compatible:
    const: qcom,sa8255p-ufshc

  reg:
    maxItems: 1

  interrupts:
    maxItems: 1

  iommus:
    maxItems: 1

  dma-coherent: true

  power-domains:
    maxItems: 1

required:
  - compatible
  - reg
  - interrupts
  - power-domains
  - iommus
  - dma-coherent

allOf:
  - $ref: ufs-common.yaml

unevaluatedProperties: false

examples:
  - |
    #include <dt-bindings/interrupt-controller/arm-gic.h>

    ufshc@1d84000 {
        compatible = "qcom,sa8255p-ufshc";
        reg = <0x01d84000 0x3000>;
        interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>;
        lanes-per-direction = <2>;

        iommus = <&apps_smmu 0x100 0x0>;
        power-domains = <&scmi3_pd 0>;
        dma-coherent;
    };
+1 −1
Original line number Diff line number Diff line
@@ -26803,7 +26803,7 @@ M: Manivannan Sadhasivam <mani@kernel.org>
L:	linux-arm-msm@vger.kernel.org
L:	linux-scsi@vger.kernel.org
S:	Maintained
F:	Documentation/devicetree/bindings/ufs/qcom,ufs.yaml
F:	Documentation/devicetree/bindings/ufs/qcom*
F:	drivers/ufs/host/ufs-qcom*
UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER RENESAS HOOKS
+1 −1
Original line number Diff line number Diff line
@@ -141,7 +141,7 @@ static inline ssize_t ufs_sysfs_pm_lvl_store(struct device *dev,
	if (kstrtoul(buf, 0, &value))
		return -EINVAL;

	if (value >= UFS_PM_LVL_MAX)
	if (value >= UFS_PM_LVL_MAX || value < hba->pm_lvl_min)
		return -EINVAL;

	if (ufs_pm_lvl_states[value].dev_state == UFS_DEEPSLEEP_PWR_MODE &&
+155 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/reset-controller.h>
#include <linux/time.h>
#include <linux/unaligned.h>
@@ -619,6 +620,27 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
	return err;
}

static int ufs_qcom_fw_managed_hce_enable_notify(struct ufs_hba *hba,
						 enum ufs_notify_change_status status)
{
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);

	switch (status) {
	case PRE_CHANGE:
		ufs_qcom_select_unipro_mode(host);
		break;
	case POST_CHANGE:
		ufs_qcom_enable_hw_clk_gating(hba);
		ufs_qcom_ice_enable(host);
		break;
	default:
		dev_err(hba->dev, "Invalid status %d\n", status);
		return -EINVAL;
	}

	return 0;
}

/**
 * ufs_qcom_cfg_timers - Configure ufs qcom cfg timers
 *
@@ -789,6 +811,33 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
	return ufs_qcom_ice_resume(host);
}

static int ufs_qcom_fw_managed_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
				       enum ufs_notify_change_status status)
{
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);

	if (status == PRE_CHANGE)
		return 0;

	pm_runtime_put_sync(hba->dev);

	return ufs_qcom_ice_suspend(host);
}

static int ufs_qcom_fw_managed_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
{
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	int err;

	err = pm_runtime_resume_and_get(hba->dev);
	if (err) {
		dev_err(hba->dev, "PM runtime resume failed: %d\n", err);
		return err;
	}

	return ufs_qcom_ice_resume(host);
}

static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable)
{
	if (host->dev_ref_clk_ctrl_mmio &&
@@ -1421,6 +1470,54 @@ static void ufs_qcom_exit(struct ufs_hba *hba)
	phy_exit(host->generic_phy);
}

static int ufs_qcom_fw_managed_init(struct ufs_hba *hba)
{
	struct device *dev = hba->dev;
	struct ufs_qcom_host *host;
	int err;

	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
	if (!host)
		return -ENOMEM;

	host->hba = hba;
	ufshcd_set_variant(hba, host);

	ufs_qcom_get_controller_revision(hba, &host->hw_ver.major,
					 &host->hw_ver.minor, &host->hw_ver.step);

	err = ufs_qcom_ice_init(host);
	if (err)
		goto out_variant_clear;

	ufs_qcom_get_default_testbus_cfg(host);
	err = ufs_qcom_testbus_config(host);
	if (err)
		/* Failure is non-fatal */
		dev_warn(dev, "Failed to configure the testbus %d\n", err);

	hba->caps |= UFSHCD_CAP_WB_EN;

	ufs_qcom_advertise_quirks(hba);
	host->hba->quirks &= ~UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH;

	hba->spm_lvl = hba->rpm_lvl = hba->pm_lvl_min = UFS_PM_LVL_5;

	ufs_qcom_set_host_params(hba);
	ufs_qcom_parse_gear_limits(hba);

	return 0;

out_variant_clear:
	ufshcd_set_variant(hba, NULL);
	return err;
}

static void ufs_qcom_fw_managed_exit(struct ufs_hba *hba)
{
	pm_runtime_put_sync(hba->dev);
}

/**
 * ufs_qcom_set_clk_40ns_cycles - Configure 40ns clk cycles
 *
@@ -1950,6 +2047,37 @@ static int ufs_qcom_device_reset(struct ufs_hba *hba)
	return 0;
}

/**
 * ufs_qcom_fw_managed_device_reset - Reset UFS device under FW-managed design
 * @hba: pointer to UFS host bus adapter
 *
 * In the firmware-managed reset model, the power domain is powered on by genpd
 * before the UFS controller driver probes. For subsequent resets (such as
 * suspend/resume or recovery), the UFS driver must explicitly invoke PM runtime
 *
 * Return: 0 on success or a negative error code on failure.
 */
static int ufs_qcom_fw_managed_device_reset(struct ufs_hba *hba)
{
	static bool is_boot = true;
	int err;

	/* Skip reset on cold boot; perform it on subsequent calls */
	if (is_boot) {
		is_boot = false;
		return 0;
	}

	pm_runtime_put_sync(hba->dev);
	err = pm_runtime_resume_and_get(hba->dev);
	if (err < 0) {
		dev_err(hba->dev, "PM runtime resume failed: %d\n", err);
		return err;
	}

	return 0;
}

static void ufs_qcom_config_scaling_param(struct ufs_hba *hba,
					struct devfreq_dev_profile *p,
					struct devfreq_simple_ondemand_data *d)
@@ -2229,6 +2357,20 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
	.freq_to_gear_speed	= ufs_qcom_freq_to_gear_speed,
};

static const struct ufs_hba_variant_ops ufs_hba_qcom_sa8255p_vops = {
	.name                   = "qcom-sa8255p",
	.init                   = ufs_qcom_fw_managed_init,
	.exit                   = ufs_qcom_fw_managed_exit,
	.hce_enable_notify      = ufs_qcom_fw_managed_hce_enable_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_fw_managed_suspend,
	.resume                 = ufs_qcom_fw_managed_resume,
	.dbg_register_dump      = ufs_qcom_dump_dbg_regs,
	.device_reset           = ufs_qcom_fw_managed_device_reset,
};

/**
 * ufs_qcom_probe - probe routine of the driver
 * @pdev: pointer to Platform device handle
@@ -2239,9 +2381,16 @@ static int ufs_qcom_probe(struct platform_device *pdev)
{
	int err;
	struct device *dev = &pdev->dev;
	const struct ufs_hba_variant_ops *vops;
	const struct ufs_qcom_drvdata *drvdata = device_get_match_data(dev);

	if (drvdata && drvdata->vops)
		vops = drvdata->vops;
	else
		vops = &ufs_hba_qcom_vops;

	/* Perform generic probe */
	err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_vops);
	err = ufshcd_pltfrm_init(pdev, vops);
	if (err)
		return dev_err_probe(dev, err, "ufshcd_pltfrm_init() failed\n");

@@ -2269,10 +2418,15 @@ static const struct ufs_qcom_drvdata ufs_qcom_sm8550_drvdata = {
	.no_phy_retention = true,
};

static const struct ufs_qcom_drvdata ufs_qcom_sa8255p_drvdata = {
	.vops = &ufs_hba_qcom_sa8255p_vops
};

static const struct of_device_id ufs_qcom_of_match[] __maybe_unused = {
	{ .compatible = "qcom,ufshc" },
	{ .compatible = "qcom,sm8550-ufshc", .data = &ufs_qcom_sm8550_drvdata },
	{ .compatible = "qcom,sm8650-ufshc", .data = &ufs_qcom_sm8550_drvdata },
	{ .compatible = "qcom,sa8255p-ufshc", .data = &ufs_qcom_sa8255p_drvdata },
	{},
};
MODULE_DEVICE_TABLE(of, ufs_qcom_of_match);
+1 −0
Original line number Diff line number Diff line
@@ -313,6 +313,7 @@ struct ufs_qcom_host {
struct ufs_qcom_drvdata {
	enum ufshcd_quirks quirks;
	bool no_phy_retention;
	const struct ufs_hba_variant_ops *vops;
};

static inline u32
Loading