Skip to contentSkip to conversation

scsi: ufs: make UFS Tx lane1 clock optional for QCOM platforms

PATCH Superseded Can Guo 2 participants 3 messages Oct 11, 2018 – Oct 12, 2018

Subsystems (2)

v2 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

Can Guo

author
#0 lore raw

PATCH v2 1/1 scsi: ufs: make UFS Tx lane1 clock optional for QCOM platforms

From: Venkat Gopalakrishnan <[email protected]>

Per Qcom's UFS host controller HW design, the UFS Tx lane1 clock could be muxed with Tx lane0 clock, hence keep Tx lane1 clock optional by ignoring it if it is not provided in device tree. This change also performs some cleanup to lanes per direction checks when enable/disable lane clocks just for symmetry.

---

Changes since v1:
- Incorporated review comments from Doug.
- Update the commit title and commit message.

 drivers/scsi/ufs/ufs-qcom.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)
drivers/scsi/ufs/ufs-qcom.c +20 -13
index 2b38db2..a7ec959 100644
@@ -79,13 +79,19 @@ static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes)
 }
 
 static int ufs_qcom_host_clk_get(struct device *dev,
-		const char *name, struct clk **clk_out)
+		const char *name, struct clk **clk_out, bool optional)
 {
 	struct clk *clk;
 	int err = 0;
 
 	clk = devm_clk_get(dev, name);
-	if (IS_ERR(clk)) {
+	if (clk == ERR_PTR(-EPROBE_DEFER)) {
+		err = -EPROBE_DEFER;
+		dev_warn(dev, "required clock %s hasn't probed yet, err %d\n",
+				name, err);
+	} else if (IS_ERR(clk)) {
+		if (optional)
+			return 0;
 		err = PTR_ERR(clk);
 		dev_err(dev, "%s: failed to get %s err %d",
 				__func__, name, err);
@@ -113,10 +119,10 @@ static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host)
 	if (!host->is_lane_clks_enabled)
 		return;
 
-	if (host->hba->lanes_per_direction > 1)
+	if (host->tx_l1_sync_clk)
 		clk_disable_unprepare(host->tx_l1_sync_clk);
 	clk_disable_unprepare(host->tx_l0_sync_clk);
-	if (host->hba->lanes_per_direction > 1)
+	if (host->rx_l1_sync_clk)
 		clk_disable_unprepare(host->rx_l1_sync_clk);
 	clk_disable_unprepare(host->rx_l0_sync_clk);
 
@@ -141,12 +147,14 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host)
 	if (err)
 		goto disable_rx_l0;
 
-	if (host->hba->lanes_per_direction > 1) {
+	if (host->rx_l1_sync_clk) {
 		err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
 			host->rx_l1_sync_clk);
 		if (err)
 			goto disable_tx_l0;
+	}
 
+	if (host->tx_l1_sync_clk) {
 		err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
 			host->tx_l1_sync_clk);
 		if (err)
@@ -157,8 +165,7 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host)
 	goto out;
 
 disable_rx_l1:
-	if (host->hba->lanes_per_direction > 1)
-		clk_disable_unprepare(host->rx_l1_sync_clk);
+	clk_disable_unprepare(host->rx_l1_sync_clk);
 disable_tx_l0:
 	clk_disable_unprepare(host->tx_l0_sync_clk);
 disable_rx_l0:
@@ -172,25 +179,25 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host)
 	int err = 0;
 	struct device *dev = host->hba->dev;
 
-	err = ufs_qcom_host_clk_get(dev,
-			"rx_lane0_sync_clk", &host->rx_l0_sync_clk);
+	err = ufs_qcom_host_clk_get(dev, "rx_lane0_sync_clk",
+					&host->rx_l0_sync_clk, false);
 	if (err)
 		goto out;
 
-	err = ufs_qcom_host_clk_get(dev,
-			"tx_lane0_sync_clk", &host->tx_l0_sync_clk);
+	err = ufs_qcom_host_clk_get(dev, "tx_lane0_sync_clk",
+					&host->tx_l0_sync_clk, false);
 	if (err)
 		goto out;
 
 	/* In case of single lane per direction, don't read lane1 clocks */
 	if (host->hba->lanes_per_direction > 1) {
 		err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
-			&host->rx_l1_sync_clk);
+			&host->rx_l1_sync_clk, false);
 		if (err)
 			goto out;
 
 		err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
-			&host->tx_l1_sync_clk);
+			&host->tx_l1_sync_clk, true);
 	}
 out:
 	return err;
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#1 lore raw

Hi,

Quoted 6 lines · Can Guo

static int ufs_qcom_host_clk_get(struct device *dev,

drivers/scsi/ufs/ufs-qcom.c +1 -1
-               const char *name, struct clk **clk_out)
+               const char *name, struct clk **clk_out, bool optional)
 {
        struct clk *clk;
        int err = 0;

drivers/scsi/ufs/ufs-qcom.c static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) · line 94

Quoted 10 lines
drivers/scsi/ufs/ufs-qcom.c +7 -1
 
        clk = devm_clk_get(dev, name);
-       if (IS_ERR(clk)) {
+       if (clk == ERR_PTR(-EPROBE_DEFER)) {
+               err = -EPROBE_DEFER;
+               dev_warn(dev, "required clock %s hasn't probed yet, err %d\n",
+                               name, err);
+       } else if (IS_ERR(clk)) {
+               if (optional)
+                       return 0;

Change this function to:

static int ufs_qcom_host_clk_get(struct device *dev,
const char *name, struct clk **clk_out, bool optional)
{
struct clk *clk;
int err;

  clk = devm_clk_get(dev, name);
  if (!IS_ERR(clk)) {
    *clk_out = clk;
    return 0;
  }

  err = PTR_ERR(clk);

  if (optional && err == -ENOENT) {
    *clk_out = NULL;
    return 0;
  }

if (err != -EPROBE_DEFER)
dev_err(dev, "failed to get %s err %d",
name, err);

return err;
}

Specifically:

* Typically it just spams the log to print something when you see an -EPROBE_DEFER.

* If a clock is optional you should only consider things a success only if "-ENOENT" was returned. All other errors should be considered fatal.

* If a clock is optional it's slightly cleaner to set *clk_out to NULL. I know you're initting global data that happened to already be NULL by default, but it still feels nice for the abstraction of the function to do this.

* Please don't pass __func__ to your error messages.

drivers/scsi/ufs/ufs-qcom.c static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host) · line 122

Quoted 9 lines
                err = PTR_ERR(clk);
                dev_err(dev, "%s: failed to get %s err %d",
                                __func__, name, err);
drivers/scsi/ufs/ufs-qcom.c +1 -1
@@ -113,10 +119,10 @@ static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host)
        if (!host->is_lane_clks_enabled)
                return;
 
-       if (host->hba->lanes_per_direction > 1)
+       if (host->tx_l1_sync_clk)

Remove this "if". Always call clk_disable_unprepare() which will be a no-op if "host->tx_l1_sync_clk" is NULL. clk_disable_unprepare() is a no-op for NULL clocks by design specifically to make code like this cleaner.

drivers/scsi/ufs/ufs-qcom.c static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host) · line 125

Quoted 4 lines
drivers/scsi/ufs/ufs-qcom.c +1 -1
                clk_disable_unprepare(host->tx_l1_sync_clk);
        clk_disable_unprepare(host->tx_l0_sync_clk);
-       if (host->hba->lanes_per_direction > 1)
+       if (host->rx_l1_sync_clk)

Remove this "if". Always call clk_disable_unprepare() which will be a no-op if "host->rx_l1_sync_clk" is NULL. clk_disable_unprepare() is a no-op for NULL clocks by design specifically to make code like this cleaner.

drivers/scsi/ufs/ufs-qcom.c static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host) · line 150

Quoted 9 lines
drivers/scsi/ufs/ufs-qcom.c +1 -1
                clk_disable_unprepare(host->rx_l1_sync_clk);
        clk_disable_unprepare(host->rx_l0_sync_clk);
 
@@ -141,12 +147,14 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host)
        if (err)
                goto disable_rx_l0;
 
-       if (host->hba->lanes_per_direction > 1) {
+       if (host->rx_l1_sync_clk) {

Remove this "if". Always call ufs_qcom_host_clk_enable(). The clk_prepare_enable() inside ufs_qcom_host_clk_enable() will be a no-op if "host->rx_l1_sync_clk" is NULL and will return 0 (no error).

drivers/scsi/ufs/ufs-qcom.c static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host) · line 157

Quoted 7 lines
drivers/scsi/ufs/ufs-qcom.c +2 -0
                err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
                        host->rx_l1_sync_clk);
                if (err)
                        goto disable_tx_l0;
+       }
 
+       if (host->tx_l1_sync_clk) {

Remove this "if". Always call ufs_qcom_host_clk_enable(). The clk_prepare_enable() inside ufs_qcom_host_clk_enable() will be a no-op if "host->tx_l1_sync_clk" is NULL and will return 0 (no error).

-Doug

#2 lore raw

Hi Doug,

Quoted 58 lines · Doug Anderson

Hi,

Quoted 13 lines · Can Guo

static int ufs_qcom_host_clk_get(struct device *dev,

drivers/scsi/ufs/ufs-qcom.c +4 -2
-               const char *name, struct clk **clk_out)
+               const char *name, struct clk **clk_out, bool optional)
 {
        struct clk *clk;
        int err = 0;
 
        clk = devm_clk_get(dev, name);
-       if (IS_ERR(clk)) {
+       if (clk == ERR_PTR(-EPROBE_DEFER)) {
+               err = -EPROBE_DEFER;
+               dev_warn(dev, "required clock %s hasn't probed yet, 

err %d\n",

drivers/scsi/ufs/ufs-qcom.c static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) · line 94

Quoted 4 lines
drivers/scsi/ufs/ufs-qcom.c +4 -0
+                               name, err);
+       } else if (IS_ERR(clk)) {
+               if (optional)
+                       return 0;

Change this function to:

static int ufs_qcom_host_clk_get(struct device *dev,
const char *name, struct clk **clk_out, bool optional)
{
struct clk *clk;
int err;

  clk = devm_clk_get(dev, name);
  if (!IS_ERR(clk)) {
    *clk_out = clk;
    return 0;
  }

  err = PTR_ERR(clk);

  if (optional && err == -ENOENT) {
    *clk_out = NULL;
    return 0;
  }

if (err != -EPROBE_DEFER)
dev_err(dev, "failed to get %s err %d",
name, err);

return err;
}

Specifically:

* Typically it just spams the log to print something when you see an -EPROBE_DEFER.

* If a clock is optional you should only consider things a success only if "-ENOENT" was returned. All other errors should be considered fatal.

Quoted 8 lines

* If a clock is optional it's slightly cleaner to set *clk_out to NULL. I know you're initting global data that happened to already be NULL by default, but it still feels nice for the abstraction of the function to do this.

* Please don't pass __func__ to your error messages.

Thank you for the detailed instructions. Shall make the change like so.

Quoted 8 lines
Quoted 8 lines
                err = PTR_ERR(clk);
                dev_err(dev, "%s: failed to get %s err %d",
                                __func__, name, err);
drivers/scsi/ufs/ufs-qcom.c +0 -0
@@ -113,10 +119,10 @@ static void ufs_qcom_disable_lane_clks(struct 
 ufs_qcom_host *host)
        if (!host->is_lane_clks_enabled)
                return;
 
Quoted 9 lines

drivers/scsi/ufs/ufs-qcom.c static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host) · line 122

Quoted 2 lines
drivers/scsi/ufs/ufs-qcom.c +1 -1
-       if (host->hba->lanes_per_direction > 1)
+       if (host->tx_l1_sync_clk)

Remove this "if". Always call clk_disable_unprepare() which will be a no-op if "host->tx_l1_sync_clk" is NULL. clk_disable_unprepare() is a no-op for NULL clocks by design specifically to make code like this cleaner.

Shall do.

Quoted 11 lines

drivers/scsi/ufs/ufs-qcom.c static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host) · line 125

Quoted 4 lines
drivers/scsi/ufs/ufs-qcom.c +1 -1
                clk_disable_unprepare(host->tx_l1_sync_clk);
        clk_disable_unprepare(host->tx_l0_sync_clk);
-       if (host->hba->lanes_per_direction > 1)
+       if (host->rx_l1_sync_clk)

Remove this "if". Always call clk_disable_unprepare() which will be a no-op if "host->rx_l1_sync_clk" is NULL. clk_disable_unprepare() is a no-op for NULL clocks by design specifically to make code like this cleaner.

Shall do.

Quoted 8 lines
Quoted 8 lines
drivers/scsi/ufs/ufs-qcom.c +0 -0
                clk_disable_unprepare(host->rx_l1_sync_clk);
        clk_disable_unprepare(host->rx_l0_sync_clk);
@@ -141,12 +147,14 @@ static int ufs_qcom_enable_lane_clks(struct 
 ufs_qcom_host *host)
        if (err)
                goto disable_rx_l0;
 
Quoted 8 lines

drivers/scsi/ufs/ufs-qcom.c static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host) · line 150

Quoted 2 lines
drivers/scsi/ufs/ufs-qcom.c +1 -1
-       if (host->hba->lanes_per_direction > 1) {
+       if (host->rx_l1_sync_clk) {

Remove this "if". Always call ufs_qcom_host_clk_enable(). The clk_prepare_enable() inside ufs_qcom_host_clk_enable() will be a no-op if "host->rx_l1_sync_clk" is NULL and will return 0 (no error).

Shall do so.

Quoted 7 lines
Quoted 7 lines
drivers/scsi/ufs/ufs-qcom.c +1 -0
                err = ufs_qcom_host_clk_enable(dev, 
 "rx_lane1_sync_clk",
                        host->rx_l1_sync_clk);
                if (err)
                        goto disable_tx_l0;
+       }
 
Quoted 8 lines

drivers/scsi/ufs/ufs-qcom.c static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host) · line 157

Quoted 1 line
drivers/scsi/ufs/ufs-qcom.c +1 -0
+       if (host->tx_l1_sync_clk) {

Remove this "if". Always call ufs_qcom_host_clk_enable(). The clk_prepare_enable() inside ufs_qcom_host_clk_enable() will be a no-op if "host->tx_l1_sync_clk" is NULL and will return 0 (no error).

-Doug

Shall do so. Thank you.

-Can Guo