diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 21aaf2f76e53..634ac07fd1f3 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -606,6 +606,41 @@ int phy_validate(struct phy *phy, enum phy_mode mode, int submode,
 }
 EXPORT_SYMBOL_GPL(phy_validate);
 
+/**
+ * phy_request_bus_width() - request PHY to change its bus width
+ * @phy: the phy returned by phy_get()
+ * @bus_width: new bus width
+ *
+ * Consumers can use this method to request the PHY to update itself to a new
+ * bus width (typically meaning lane count). Can be called from any init state
+ * and power state. PHY is expected to use the new lane count as soon as this
+ * method returns.
+ *
+ * Returns: 0 if successful or if operating on an optional and absent PHY,
+ *	-EOPNOTSUPP if the operation is not implemented, -EINVAL if the
+ *	requested bus width is not supported, other negative error codes for
+ *	driver-specific failures.
+ */
+int phy_request_bus_width(struct phy *phy, int bus_width)
+{
+	int ret;
+
+	if (!phy)
+		return 0;
+
+	if (!phy->ops->request_bus_width)
+		return -EOPNOTSUPP;
+
+	mutex_lock(&phy->mutex);
+	ret = phy->ops->request_bus_width(phy, bus_width);
+	if (!ret)
+		phy_set_bus_width(phy, bus_width);
+	mutex_unlock(&phy->mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(phy_request_bus_width);
+
 /**
  * _of_phy_get() - lookup and obtain a reference to a phy by phandle
  * @np: device_node for which to get the phy
diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c
index ee665f26c236..b09a35ab6acd 100644
--- a/drivers/phy/samsung/phy-samsung-ufs.c
+++ b/drivers/phy/samsung/phy-samsung-ufs.c
@@ -165,7 +165,6 @@ static int samsung_ufs_phy_init(struct phy *phy)
 {
 	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
 
-	ss_phy->lane_cnt = phy->attrs.bus_width;
 	ss_phy->ufs_phy_state = CFG_PRE_INIT;
 
 	return 0;
@@ -204,6 +203,31 @@ static int samsung_ufs_phy_power_off(struct phy *phy)
 	return 0;
 }
 
+static int samsung_ufs_phy_request_bus_width(struct phy *phy, int bus_width)
+{
+	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+	u8 old_lane_cnt = ss_phy->lane_cnt;
+	int err = 0;
+
+	if (bus_width != 1 && bus_width != 2)
+		return -EINVAL;
+
+	ss_phy->lane_cnt = bus_width;
+
+	if (phy->init_count)
+		samsung_ufs_phy_init(phy);
+
+	/* If the init_count is 0, the power_count should also be 0 */
+	if (phy->power_count) {
+		samsung_ufs_phy_power_off(phy);
+		err = samsung_ufs_phy_power_on(phy);
+		if (err)
+			ss_phy->lane_cnt = old_lane_cnt;
+	}
+
+	return err;
+}
+
 static int samsung_ufs_phy_set_mode(struct phy *generic_phy,
 				    enum phy_mode mode, int submode)
 {
@@ -272,6 +296,7 @@ static const struct phy_ops samsung_ufs_phy_ops = {
 	.calibrate	= samsung_ufs_phy_calibrate,
 	.set_mode	= samsung_ufs_phy_set_mode,
 	.notify_phystate = samsung_ufs_phy_notify_state,
+	.request_bus_width = samsung_ufs_phy_request_bus_width,
 	.owner          = THIS_MODULE,
 };
 
diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c
index 77a6c8e44485..b90876b268db 100644
--- a/drivers/ufs/host/ufs-exynos.c
+++ b/drivers/ufs/host/ufs-exynos.c
@@ -931,11 +931,73 @@ static void exynos_ufs_specify_nexus_t_tm_req(struct ufs_hba *hba,
 	}
 }
 
-static int exynos_ufs_phy_init(struct exynos_ufs *ufs)
+static int exynos_ufs_phy_init(struct device *dev, struct exynos_ufs *ufs)
+{
+	struct phy *generic_phy;
+	int ret;
+
+	generic_phy = devm_phy_get(dev, "ufs-phy");
+	if (IS_ERR(generic_phy)) {
+		ret = PTR_ERR(generic_phy);
+		dev_err(dev, "failed to get ufs-phy: %pe\n", ERR_PTR(ret));
+		return ret;
+	}
+
+	ret = phy_init(generic_phy);
+	if (ret) {
+		dev_err(dev, "phy init failed: %pe\n", ERR_PTR(ret));
+		return ret;
+	}
+
+	ufs->phy = generic_phy;
+
+	return ret;
+}
+
+static void exynos_ufs_phy_exit(struct exynos_ufs *ufs)
+{
+	phy_exit(ufs->phy);
+}
+
+static int exynos_ufs_phy_power_on(struct exynos_ufs *ufs)
+{
+	int ret;
+
+	if (ufs->phy_powered_on)
+		return 0;
+
+	ret = phy_power_on(ufs->phy);
+	if (ret) {
+		dev_err(ufs->hba->dev, "Failed to power on PHY: %pe\n",
+			ERR_PTR(ret));
+		return ret;
+	}
+
+	ufs->phy_powered_on = true;
+
+	return 0;
+}
+
+static void exynos_ufs_phy_power_off(struct exynos_ufs *ufs)
+{
+	int ret;
+
+	if (!ufs->phy_powered_on)
+		return;
+
+	ret = phy_power_off(ufs->phy);
+	if (ret)
+		dev_warn(ufs->hba->dev, "Failed to power off PHY: %pe\n",
+			 ERR_PTR(ret));
+
+	ufs->phy_powered_on = false;
+}
+
+static int exynos_ufs_phy_update_bus_width(struct exynos_ufs *ufs)
 {
 	struct ufs_hba *hba = ufs->hba;
 	struct phy *generic_phy = ufs->phy;
-	int ret = 0;
+	int ret;
 
 	if (ufs->avail_ln_rx == 0 || ufs->avail_ln_tx == 0) {
 		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILRXDATALANES),
@@ -947,30 +1009,11 @@ static int exynos_ufs_phy_init(struct exynos_ufs *ufs)
 			ufs->avail_ln_rx, ufs->avail_ln_tx);
 	}
 
-	phy_set_bus_width(generic_phy, ufs->avail_ln_rx);
-
-	if (generic_phy->power_count) {
-		phy_power_off(generic_phy);
-		phy_exit(generic_phy);
-	}
-
-	ret = phy_init(generic_phy);
-	if (ret) {
-		dev_err(hba->dev, "%s: phy init failed, ret = %d\n",
-			__func__, ret);
-		return ret;
-	}
-
-	ret = phy_power_on(generic_phy);
+	ret = phy_request_bus_width(generic_phy, ufs->avail_ln_rx);
 	if (ret)
-		goto out_exit_phy;
-
-	return 0;
-
-out_exit_phy:
-	phy_exit(generic_phy);
+		return ret;
 
-	return ret;
+	return exynos_ufs_phy_power_on(ufs);
 }
 
 static void exynos_ufs_config_unipro(struct exynos_ufs *ufs)
@@ -1055,7 +1098,7 @@ static int exynos_ufs_pre_link(struct ufs_hba *hba)
 		ufs->drv_data->pre_link(ufs);
 
 	/* m-phy */
-	exynos_ufs_phy_init(ufs);
+	exynos_ufs_phy_update_bus_width(ufs);
 	if (!(ufs->opts & EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR)) {
 		exynos_ufs_config_phy_time_attr(ufs);
 		exynos_ufs_config_phy_cap_attr(ufs);
@@ -1475,12 +1518,9 @@ static int exynos_ufs_init(struct ufs_hba *hba)
 		goto out;
 	}
 
-	ufs->phy = devm_phy_get(dev, "ufs-phy");
-	if (IS_ERR(ufs->phy)) {
-		ret = PTR_ERR(ufs->phy);
-		dev_err(dev, "failed to get ufs-phy\n");
+	ret = exynos_ufs_phy_init(dev, ufs);
+	if (ret)
 		goto out;
-	}
 
 	exynos_ufs_priv_init(hba, ufs);
 
@@ -1490,13 +1530,13 @@ static int exynos_ufs_init(struct ufs_hba *hba)
 		ret = ufs->drv_data->drv_init(ufs);
 		if (ret) {
 			dev_err(dev, "failed to init drv-data\n");
-			goto out;
+			goto out_phy_exit;
 		}
 	}
 
 	ret = exynos_ufs_get_clk_info(ufs);
 	if (ret)
-		goto out;
+		goto out_phy_exit;
 	exynos_ufs_specify_phy_time_attr(ufs);
 
 	exynos_ufs_config_smu(ufs);
@@ -1504,6 +1544,8 @@ static int exynos_ufs_init(struct ufs_hba *hba)
 	hba->host->dma_alignment = DATA_UNIT_SIZE - 1;
 	return 0;
 
+out_phy_exit:
+	exynos_ufs_phy_exit(ufs);
 out:
 	hba->priv = NULL;
 	return ret;
@@ -1513,8 +1555,8 @@ static void exynos_ufs_exit(struct ufs_hba *hba)
 {
 	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
 
-	phy_power_off(ufs->phy);
-	phy_exit(ufs->phy);
+	exynos_ufs_phy_power_off(ufs);
+	exynos_ufs_phy_exit(ufs);
 }
 
 static int exynos_ufs_host_reset(struct ufs_hba *hba)
@@ -1728,7 +1770,7 @@ static int exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
 		ufs->drv_data->suspend(ufs);
 
 	if (!ufshcd_is_link_active(hba))
-		phy_power_off(ufs->phy);
+		exynos_ufs_phy_power_off(ufs);
 
 	return 0;
 }
@@ -1738,7 +1780,7 @@ static int exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
 
 	if (!ufshcd_is_link_active(hba))
-		phy_power_on(ufs->phy);
+		exynos_ufs_phy_power_on(ufs);
 
 	exynos_ufs_config_smu(ufs);
 	exynos_ufs_fmp_resume(hba);
diff --git a/drivers/ufs/host/ufs-exynos.h b/drivers/ufs/host/ufs-exynos.h
index abe7e472759e..683b9150e2ba 100644
--- a/drivers/ufs/host/ufs-exynos.h
+++ b/drivers/ufs/host/ufs-exynos.h
@@ -227,6 +227,7 @@ struct exynos_ufs {
 	int avail_ln_rx;
 	int avail_ln_tx;
 	int rx_sel_idx;
+	bool phy_powered_on;
 	struct ufs_pa_layer_attr dev_req_params;
 	struct ufs_phy_time_cfg t_cfg;
 	ktime_t entry_hibern8_t;
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index ea47975e288a..01af84f97608 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -93,6 +93,7 @@ union phy_configure_opts {
  * @reset: resetting the phy
  * @calibrate: calibrate the phy
  * @notify_phystate: notify and configure the phy for a particular state
+ * @request_bus_width: request a different bus width for the phy
  * @release: ops to be performed while the consumer relinquishes the PHY
  * @owner: the module owner containing the ops
  */
@@ -143,6 +144,7 @@ struct phy_ops {
 	int	(*disconnect)(struct phy *phy, int port);
 
 	int	(*notify_phystate)(struct phy *phy, union phy_notify state);
+	int	(*request_bus_width)(struct phy *phy, int bus_width);
 	void	(*release)(struct phy *phy);
 	struct module *owner;
 };
@@ -275,6 +277,7 @@ static inline void phy_set_bus_width(struct phy *phy, int bus_width)
 {
 	phy->attrs.bus_width = bus_width;
 }
+int phy_request_bus_width(struct phy *phy, int bus_width);
 struct phy *phy_get(struct device *dev, const char *string);
 struct phy *devm_phy_get(struct device *dev, const char *string);
 struct phy *devm_phy_optional_get(struct device *dev, const char *string);
@@ -456,6 +459,14 @@ static inline void phy_set_bus_width(struct phy *phy, int bus_width)
 	return;
 }
 
+static inline int phy_request_bus_width(struct phy *phy, int bus_width)
+{
+	if (!phy)
+		return 0;
+
+	return -ENOSYS;
+}
+
 static inline struct phy *phy_get(struct device *dev, const char *string)
 {
 	return ERR_PTR(-ENOSYS);
