Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25b2d8ab authored by Subhash Jadavani's avatar Subhash Jadavani
Browse files

scsi: ufs-qcom: add support to control the device ref_clk



On Qualcomm platforms, there will be many consumers of the source clock
which also supply ref_clk to UFS Device. So even if generic UFS
driver (ufshcd) vote to turn off the source ref_clk, it's very likely that
device ref_clk is still running. Hence some of the qualcomm chipsets have
separate control bit to gate & ungate the UFS ref_clk to device. This
control bit is part of the TLMM register adddress space so it can't be
simulated at clock control bit which means UFS qcom driver has to manually
control this bit to gate or ungate the device ref_clk. This change adds
support for the same.

Change-Id: I3ee1187292eaadfdb552d33c2bb6f58922c9e501
Signed-off-by: default avatarSubhash Jadavani <subhashj@codeaurora.org>
parent 6d1dfe17
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -10,7 +10,10 @@ Required properties:
- compatible        : compatible list, contains "qcom,ufs-phy-qmp-28nm"
                      or "qcom,ufs-phy-qmp-20nm" according to the relevant
                      phy in use
- reg               : <registers mapping>
- reg               : should contain PHY register address space (mandatory),
                      device PHY control register map (optional).
- reg-names         : indicates various resources passed to driver (via reg proptery) by name.
                      Required "reg-names" is "phy_mem" and "dev_ref_clk_ctrl_mem" is optional.
- #phy-cells        : This property shall be set to 0
- vdda-phy-supply   : phandle to main PHY supply for analog domain
- vdda-pll-supply   : phandle to PHY PLL and Power-Gen block power supply
@@ -28,7 +31,8 @@ Example:

	ufsphy1: ufsphy@0xfc597000 {
		compatible = "qcom,ufs-phy-qmp-28nm";
		reg = <0xfc597000 0x800>;
		reg = <0xfc597000 0x800>, <0xfd512074 0x4>;
		reg-names = "phy_mem", "dev_ref_clk_ctrl_mem";
		#phy-cells = <0>;
		vdda-phy-supply = <&pma8084_l4>;
		vdda-pll-supply = <&pma8084_l12>;
+1 −0
Original line number Diff line number Diff line
@@ -1048,6 +1048,7 @@
	ufsphy1: ufsphy@fc597000 {
			compatible = "qcom,ufs-phy-qmp-20nm";
			reg = <0xfc597000 0xda8>;
			reg-names = "phy_mem";
			#phy-cells = <0>;
			vdda-phy-supply = <&pm8994_l28>;
			vdda-pll-supply = <&pm8994_l12>;
+55 −4
Original line number Diff line number Diff line
@@ -124,10 +124,9 @@ int ufs_qcom_phy_base_init(struct platform_device *pdev,
	struct resource *res;
	int err = 0;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
	if (!res) {
		dev_err(dev, "%s: platform_get_resource() failed. returned NULL\n",
			__func__);
		dev_err(dev, "%s: phy_mem resource not found\n", __func__);
		err = -ENOMEM;
		goto out;
	}
@@ -135,7 +134,27 @@ int ufs_qcom_phy_base_init(struct platform_device *pdev,
	phy_common->mmio = devm_ioremap_resource(dev, res);
	if (IS_ERR(phy_common->mmio)) {
		err = PTR_ERR(phy_common->mmio);
		dev_err(dev, "ioremap resource failed %d\n", err);
		phy_common->mmio = NULL;
		dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n",
			__func__, err);
		goto out;
	}

	/* "dev_ref_clk_ctrl_mem" is optional resource */
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
					   "dev_ref_clk_ctrl_mem");
	if (!res) {
		dev_dbg(dev, "%s: dev_ref_clk_ctrl_mem resource not found\n",
			__func__);
		goto out;
	}

	phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
	if (IS_ERR(phy_common->dev_ref_clk_ctrl_mmio)) {
		err = PTR_ERR(phy_common->dev_ref_clk_ctrl_mmio);
		phy_common->dev_ref_clk_ctrl_mmio = NULL;
		dev_err(dev, "%s: ioremap for dev_ref_clk_ctrl_mem resource failed %d\n",
			__func__, err);
	}

out:
@@ -434,6 +453,38 @@ void ufs_qcom_phy_disable_ref_clk(struct phy *generic_phy)
	}
}

#define UFS_REF_CLK_EN	(1 << 5)

static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable)
{
	struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);

	if (phy->dev_ref_clk_ctrl_mmio &&
	    (enable ^ phy->is_dev_ref_clk_enabled)) {
		u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio);

		if (enable)
			temp |= UFS_REF_CLK_EN;
		else
			temp &= ~UFS_REF_CLK_EN;

		writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio);
		/* ensure that ref_clk is enabled/disabled before we return */
		wmb();
		phy->is_dev_ref_clk_enabled = enable;
	}
}

void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy)
{
	ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true);
}

void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy)
{
	ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false);
}

void ufs_qcom_phy_restore_swi_regs(struct phy *generic_phy)
{
	int i;
+7 −3
Original line number Diff line number Diff line
@@ -871,16 +871,20 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on)
			ufs_qcom_phy_disable_iface_clk(host->generic_phy);
			goto out;
		}

		/* enable the device ref clock */
		ufs_qcom_phy_enable_dev_ref_clk(host->generic_phy);
		vote = host->bus_vote.saved_vote;
		if (vote == host->bus_vote.min_bw_vote)
			ufs_qcom_update_bus_bw_vote(host);
	} else {
		/* M-PHY RMMI interface clocks can be turned off */
		ufs_qcom_phy_disable_iface_clk(host->generic_phy);
		/* If link is not active, turn off UFS local PHY ref_clk */
		if (!ufs_qcom_is_link_active(hba))
		if (!ufs_qcom_is_link_active(hba)) {
			/* turn off UFS local PHY ref_clk */
			ufs_qcom_phy_disable_ref_clk(host->generic_phy);
			/* disable device ref_clk */
			ufs_qcom_phy_disable_dev_ref_clk(host->generic_phy);
		}
		vote = host->bus_vote.min_bw_vote;
	}

+4 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ struct ufs_qcom_phy {
	struct list_head list;
	struct device *dev;
	void __iomem *mmio;
	void __iomem *dev_ref_clk_ctrl_mmio;
	struct clk *tx_iface_clk;
	struct clk *rx_iface_clk;
	bool is_iface_clk_enabled;
@@ -68,6 +69,7 @@ struct ufs_qcom_phy {
	struct clk *ref_clk_parent;
	struct clk *ref_clk;
	bool is_ref_clk_enabled;
	bool is_dev_ref_clk_enabled;
	struct ufs_qcom_phy_vreg vdda_pll;
	struct ufs_qcom_phy_vreg vdda_phy;
	unsigned int quirks;
@@ -167,6 +169,8 @@ int ufs_qcom_phy_disable_vreg(struct phy *phy,
			struct ufs_qcom_phy_vreg *vreg);
int ufs_qcom_phy_enable_ref_clk(struct phy *phy);
void ufs_qcom_phy_disable_ref_clk(struct phy *phy);
void ufs_qcom_phy_enable_dev_ref_clk(struct phy *);
void ufs_qcom_phy_disable_dev_ref_clk(struct phy *);
int ufs_qcom_phy_enable_iface_clk(struct phy *phy);
void ufs_qcom_phy_disable_iface_clk(struct phy *phy);
void ufs_qcom_phy_restore_swi_regs(struct phy *phy);