Skip to content

Commit

Permalink
Merge patch series "Enable HS-G5 support on SM8550"
Browse files Browse the repository at this point in the history
Can Guo <quic_cang@quicinc.com> says:

This series enables HS-G5 support on SM8550.

This series is rebased on below changes from Mani -

https://patchwork.kernel.org/project/linux-scsi/patch/20230908145329.154024-1-manivannan.sadhasivam@linaro.org/
https://patchwork.kernel.org/project/linux-scsi/patch/20230908145329.154024-2-manivannan.sadhasivam@linaro.org/

This series is tested on below HW combinations -

SM8550 MTP + UFS4.0
SM8550 QRD + UFS3.1
SM8450 MTP + UFS3.1 (for regression test)
SM8350 MTP + UFS3.1 (for regression test)

Note that during reboot test on above platforms, I occasinally hit
PA (PHY) error during the 2nd init, this is not related with this
series. A fix for this is mentioned in below patchwork -

https://patchwork.kernel.org/project/linux-scsi/patch/1698145815-17396-1-git-send-email-quic_ziqichen@quicinc.com/

Also note that on platforms, which have two sets of UFS PHY settings
are provided (say G4 and no-G4, G5 and no-G5). The two sets of PHY
settings are basically programming different values to different
registers, mixing the two sets and/or overwriting one set with another
set is definitely not blessed by UFS PHY designers. For SM8550, this
series will make sure we honor the rule. However, for old targets Mani
and I will fix them in another series in future.

Link: https://lore.kernel.org/r/1701520577-31163-1-git-send-email-quic_cang@quicinc.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
martinkpetersen committed Dec 14, 2023
2 parents edc22a7 + dc7c948 commit 90b74d5
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 80 deletions.
7 changes: 3 additions & 4 deletions drivers/ufs/host/ufs-exynos.c
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ static int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba,
{
struct exynos_ufs *ufs = ufshcd_get_variant(hba);
struct phy *generic_phy = ufs->phy;
struct ufs_dev_params ufs_exynos_cap;
struct ufs_host_params host_params;
int ret;

if (!dev_req_params) {
Expand All @@ -774,10 +774,9 @@ static int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba,
goto out;
}

ufshcd_init_pwr_dev_param(&ufs_exynos_cap);
ufshcd_init_host_params(&host_params);

ret = ufshcd_get_pwr_dev_param(&ufs_exynos_cap,
dev_max_params, dev_req_params);
ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params);
if (ret) {
pr_err("%s: failed to determine capabilities\n", __func__);
goto out;
Expand Down
11 changes: 5 additions & 6 deletions drivers/ufs/host/ufs-hisi.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ static int ufs_hisi_link_startup_notify(struct ufs_hba *hba,
return err;
}

static void ufs_hisi_set_dev_cap(struct ufs_dev_params *hisi_param)
static void ufs_hisi_set_dev_cap(struct ufs_host_params *host_params)
{
ufshcd_init_pwr_dev_param(hisi_param);
ufshcd_init_host_params(host_params);
}

static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba)
Expand Down Expand Up @@ -365,7 +365,7 @@ static int ufs_hisi_pwr_change_notify(struct ufs_hba *hba,
struct ufs_pa_layer_attr *dev_max_params,
struct ufs_pa_layer_attr *dev_req_params)
{
struct ufs_dev_params ufs_hisi_cap;
struct ufs_host_params host_params;
int ret = 0;

if (!dev_req_params) {
Expand All @@ -377,9 +377,8 @@ static int ufs_hisi_pwr_change_notify(struct ufs_hba *hba,

switch (status) {
case PRE_CHANGE:
ufs_hisi_set_dev_cap(&ufs_hisi_cap);
ret = ufshcd_get_pwr_dev_param(&ufs_hisi_cap,
dev_max_params, dev_req_params);
ufs_hisi_set_dev_cap(&host_params);
ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params);
if (ret) {
dev_err(hba->dev,
"%s: failed to determine capabilities\n", __func__);
Expand Down
12 changes: 5 additions & 7 deletions drivers/ufs/host/ufs-mediatek.c
Original file line number Diff line number Diff line change
Expand Up @@ -996,16 +996,14 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
struct ufs_pa_layer_attr *dev_req_params)
{
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
struct ufs_dev_params host_cap;
struct ufs_host_params host_params;
int ret;

ufshcd_init_pwr_dev_param(&host_cap);
host_cap.hs_rx_gear = UFS_HS_G5;
host_cap.hs_tx_gear = UFS_HS_G5;
ufshcd_init_host_params(&host_params);
host_params.hs_rx_gear = UFS_HS_G5;
host_params.hs_tx_gear = UFS_HS_G5;

ret = ufshcd_get_pwr_dev_param(&host_cap,
dev_max_params,
dev_req_params);
ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params);
if (ret) {
pr_info("%s: failed to determine capabilities\n",
__func__);
Expand Down
97 changes: 76 additions & 21 deletions drivers/ufs/host/ufs-qcom.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,9 +442,25 @@ static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba)
static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
struct ufs_host_params *host_params = &host->host_params;
struct phy *phy = host->generic_phy;
enum phy_mode mode;
int ret;

/*
* HW ver 5 can only support up to HS-G5 Rate-A due to HW limitations.
* If the HS-G5 PHY gear is used, update host_params->hs_rate to Rate-A,
* so that the subsequent power mode change shall stick to Rate-A.
*/
if (host->hw_ver.major == 0x5) {
if (host->phy_gear == UFS_HS_G5)
host_params->hs_rate = PA_HS_MODE_A;
else
host_params->hs_rate = PA_HS_MODE_B;
}

mode = host_params->hs_rate == PA_HS_MODE_B ? PHY_MODE_UFS_HS_B : PHY_MODE_UFS_HS_A;

/* Reset UFS Host Controller and PHY */
ret = ufs_qcom_host_reset(hba);
if (ret)
Expand All @@ -459,7 +475,9 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
return ret;
}

phy_set_mode_ext(phy, PHY_MODE_UFS_HS_B, host->phy_gear);
ret = phy_set_mode_ext(phy, mode, host->phy_gear);
if (ret)
goto out_disable_phy;

/* power on phy - start serdes and phy's power and clocks */
ret = phy_power_on(phy);
Expand Down Expand Up @@ -898,7 +916,7 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
struct ufs_pa_layer_attr *dev_req_params)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
struct ufs_dev_params ufs_qcom_cap;
struct ufs_host_params *host_params = &host->host_params;
int ret = 0;

if (!dev_req_params) {
Expand All @@ -908,27 +926,20 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,

switch (status) {
case PRE_CHANGE:
ufshcd_init_pwr_dev_param(&ufs_qcom_cap);
ufs_qcom_cap.hs_rate = UFS_QCOM_LIMIT_HS_RATE;

/* This driver only supports symmetic gear setting i.e., hs_tx_gear == hs_rx_gear */
ufs_qcom_cap.hs_tx_gear = ufs_qcom_cap.hs_rx_gear = ufs_qcom_get_hs_gear(hba);

ret = ufshcd_get_pwr_dev_param(&ufs_qcom_cap,
dev_max_params,
dev_req_params);
ret = ufshcd_negotiate_pwr_params(host_params, dev_max_params, dev_req_params);
if (ret) {
dev_err(hba->dev, "%s: failed to determine capabilities\n",
__func__);
return ret;
}

/*
* Update phy_gear only when the gears are scaled to a higher value. This is
* because, the PHY gear settings are backwards compatible and we only need to
* change the PHY gear settings while scaling to higher gears.
* During UFS driver probe, always update the PHY gear to match the negotiated
* gear, so that, if quirk UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH is enabled,
* the second init can program the optimal PHY settings. This allows one to start
* the first init with either the minimum or the maximum support gear.
*/
if (dev_req_params->gear_tx > host->phy_gear)
if (hba->ufshcd_state == UFSHCD_STATE_RESET)
host->phy_gear = dev_req_params->gear_tx;

/* enable the device ref clock before changing to HS mode */
Expand Down Expand Up @@ -1051,6 +1062,54 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
hba->quirks |= UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH;
}

static void ufs_qcom_set_phy_gear(struct ufs_qcom_host *host)
{
struct ufs_host_params *host_params = &host->host_params;
u32 val, dev_major;

host->phy_gear = host_params->hs_tx_gear;

if (host->hw_ver.major < 0x4) {
/*
* For controllers whose major HW version is < 4, power up the
* PHY using minimum supported gear (UFS_HS_G2). Switching to
* max gear will be performed during reinit if supported.
* For newer controllers, whose major HW version is >= 4, power
* up the PHY using max supported gear.
*/
host->phy_gear = UFS_HS_G2;
} else if (host->hw_ver.major >= 0x5) {
val = ufshcd_readl(host->hba, REG_UFS_DEBUG_SPARE_CFG);
dev_major = FIELD_GET(UFS_DEV_VER_MAJOR_MASK, val);

/*
* Since the UFS device version is populated, let's remove the
* REINIT quirk as the negotiated gear won't change during boot.
* So there is no need to do reinit.
*/
if (dev_major != 0x0)
host->hba->quirks &= ~UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH;

/*
* For UFS 3.1 device and older, power up the PHY using HS-G4
* PHY gear to save power.
*/
if (dev_major > 0x0 && dev_major < 0x4)
host->phy_gear = UFS_HS_G4;
}
}

static void ufs_qcom_set_host_params(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
struct ufs_host_params *host_params = &host->host_params;

ufshcd_init_host_params(host_params);

/* This driver only supports symmetic gear setting i.e., hs_tx_gear == hs_rx_gear */
host_params->hs_tx_gear = host_params->hs_rx_gear = ufs_qcom_get_hs_gear(hba);
}

static void ufs_qcom_set_caps(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
Expand Down Expand Up @@ -1275,6 +1334,8 @@ static int ufs_qcom_init(struct ufs_hba *hba)

ufs_qcom_set_caps(hba);
ufs_qcom_advertise_quirks(hba);
ufs_qcom_set_host_params(hba);
ufs_qcom_set_phy_gear(host);

err = ufs_qcom_ice_init(host);
if (err)
Expand All @@ -1292,12 +1353,6 @@ static int ufs_qcom_init(struct ufs_hba *hba)
dev_warn(dev, "%s: failed to configure the testbus %d\n",
__func__, err);

/*
* Power up the PHY using the minimum supported gear (UFS_HS_G2).
* Switching to max gear will be performed during reinit if supported.
*/
host->phy_gear = UFS_HS_G2;

return 0;

out_variant_clear:
Expand Down
7 changes: 5 additions & 2 deletions drivers/ufs/host/ufs-qcom.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
#define UFS_HW_VER_MINOR_MASK GENMASK(27, 16)
#define UFS_HW_VER_STEP_MASK GENMASK(15, 0)

#define UFS_DEV_VER_MAJOR_MASK GENMASK(7, 4)

/* vendor specific pre-defined parameters */
#define SLOW 1
#define FAST 2

#define UFS_QCOM_LIMIT_HS_RATE PA_HS_MODE_B

/* QCOM UFS host controller vendor specific registers */
enum {
REG_UFS_SYS1CLK_1US = 0xC0,
Expand Down Expand Up @@ -56,6 +56,8 @@ enum {
UFS_AH8_CFG = 0xFC,

REG_UFS_CFG3 = 0x271C,

REG_UFS_DEBUG_SPARE_CFG = 0x284C,
};

/* QCOM UFS host controller vendor specific debug registers */
Expand Down Expand Up @@ -240,6 +242,7 @@ struct ufs_qcom_host {

struct gpio_desc *device_reset;

struct ufs_host_params host_params;
u32 phy_gear;

bool esi_enabled;
Expand Down
Loading

0 comments on commit 90b74d5

Please sign in to comment.