Merge patch series "ufs: host: mediatek: Provide features and fixes in MediaTek platforms"

peter.wang@mediatek.com says:

This series fixes some defects and provide features in MediaTek UFS
drivers.

Link: https://lore.kernel.org/r/20250811131423.3444014-1-peter.wang@mediatek.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Martin K. Petersen 2025-08-14 22:56:02 -04:00
commit da81027950

View File

@ -29,6 +29,7 @@
#include "ufs-mediatek-sip.h"
static int ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq);
static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up);
#define CREATE_TRACE_POINTS
#include "ufs-mediatek-trace.h"
@ -798,8 +799,14 @@ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on,
clk_pwr_off = true;
}
if (clk_pwr_off)
if (clk_pwr_off) {
ufs_mtk_pwr_ctrl(hba, false);
} else {
dev_warn(hba->dev, "Clock is not turned off, hba->ahit = 0x%x, AHIT = 0x%x\n",
hba->ahit,
ufshcd_readl(hba,
REG_AUTO_HIBERNATE_IDLE_TIMER));
}
ufs_mtk_mcq_disable_irq(hba);
} else if (on && status == POST_CHANGE) {
ufs_mtk_pwr_ctrl(hba, true);
@ -1018,7 +1025,7 @@ static int ufs_mtk_vreg_fix_vcc(struct ufs_hba *hba)
struct arm_smccc_res res;
int err, ver;
if (hba->vreg_info.vcc)
if (info->vcc)
return 0;
if (of_property_read_bool(np, "mediatek,ufs-vcc-by-num")) {
@ -1075,6 +1082,80 @@ static void ufs_mtk_vreg_fix_vccqx(struct ufs_hba *hba)
}
}
static void ufs_mtk_setup_clk_gating(struct ufs_hba *hba)
{
unsigned long flags;
u32 ah_ms = 10;
u32 ah_scale, ah_timer;
u32 scale_us[] = {1, 10, 100, 1000, 10000, 100000};
if (ufshcd_is_clkgating_allowed(hba)) {
if (ufshcd_is_auto_hibern8_supported(hba) && hba->ahit) {
ah_scale = FIELD_GET(UFSHCI_AHIBERN8_SCALE_MASK,
hba->ahit);
ah_timer = FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK,
hba->ahit);
if (ah_scale <= 5)
ah_ms = ah_timer * scale_us[ah_scale] / 1000;
}
spin_lock_irqsave(hba->host->host_lock, flags);
hba->clk_gating.delay_ms = max(ah_ms, 10U);
spin_unlock_irqrestore(hba->host->host_lock, flags);
}
}
/* Convert microseconds to Auto-Hibernate Idle Timer register value */
static u32 ufs_mtk_us_to_ahit(unsigned int timer)
{
unsigned int scale;
for (scale = 0; timer > UFSHCI_AHIBERN8_TIMER_MASK; ++scale)
timer /= UFSHCI_AHIBERN8_SCALE_FACTOR;
return FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, timer) |
FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, scale);
}
static void ufs_mtk_fix_ahit(struct ufs_hba *hba)
{
unsigned int us;
if (ufshcd_is_auto_hibern8_supported(hba)) {
switch (hba->dev_info.wmanufacturerid) {
case UFS_VENDOR_SAMSUNG:
/* configure auto-hibern8 timer to 3.5 ms */
us = 3500;
break;
case UFS_VENDOR_MICRON:
/* configure auto-hibern8 timer to 2 ms */
us = 2000;
break;
default:
/* configure auto-hibern8 timer to 1 ms */
us = 1000;
break;
}
hba->ahit = ufs_mtk_us_to_ahit(us);
}
ufs_mtk_setup_clk_gating(hba);
}
static void ufs_mtk_fix_clock_scaling(struct ufs_hba *hba)
{
/* UFS version is below 4.0, clock scaling is not necessary */
if ((hba->dev_info.wspecversion < 0x0400) &&
ufs_mtk_is_clk_scale_ready(hba)) {
hba->caps &= ~UFSHCD_CAP_CLK_SCALING;
_ufs_mtk_clk_scale(hba, false);
}
}
static void ufs_mtk_init_mcq_irq(struct ufs_hba *hba)
{
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
@ -1240,6 +1321,10 @@ static bool ufs_mtk_pmc_via_fastauto(struct ufs_hba *hba,
dev_req_params->gear_rx < UFS_HS_G4)
return false;
if (dev_req_params->pwr_tx == SLOW_MODE ||
dev_req_params->pwr_rx == SLOW_MODE)
return false;
return true;
}
@ -1255,6 +1340,10 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
host_params.hs_rx_gear = UFS_HS_G5;
host_params.hs_tx_gear = UFS_HS_G5;
if (dev_max_params->pwr_rx == SLOW_MODE ||
dev_max_params->pwr_tx == SLOW_MODE)
host_params.desired_working_mode = UFS_PWM_MODE;
ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params);
if (ret) {
pr_info("%s: failed to determine capabilities\n",
@ -1278,6 +1367,28 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE),
PA_NO_ADAPT);
if (!(hba->quirks & UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING)) {
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0),
DL_FC0ProtectionTimeOutVal_Default);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1),
DL_TC0ReplayTimeOutVal_Default);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2),
DL_AFC0ReqTimeOutVal_Default);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA3),
DL_FC1ProtectionTimeOutVal_Default);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA4),
DL_TC1ReplayTimeOutVal_Default);
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA5),
DL_AFC1ReqTimeOutVal_Default);
ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalFC0ProtectionTimeOutVal),
DL_FC0ProtectionTimeOutVal_Default);
ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalTC0ReplayTimeOutVal),
DL_TC0ReplayTimeOutVal_Default);
ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalAFC0ReqTimeOutVal),
DL_AFC0ReqTimeOutVal_Default);
}
ret = ufshcd_uic_change_pwr_mode(hba,
FASTAUTO_MODE << 4 | FASTAUTO_MODE);
@ -1287,10 +1398,32 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
}
}
if (host->hw_ver.major >= 3) {
/* if already configured to the requested pwr_mode, skip adapt */
if (dev_req_params->gear_rx == hba->pwr_info.gear_rx &&
dev_req_params->gear_tx == hba->pwr_info.gear_tx &&
dev_req_params->lane_rx == hba->pwr_info.lane_rx &&
dev_req_params->lane_tx == hba->pwr_info.lane_tx &&
dev_req_params->pwr_rx == hba->pwr_info.pwr_rx &&
dev_req_params->pwr_tx == hba->pwr_info.pwr_tx &&
dev_req_params->hs_rate == hba->pwr_info.hs_rate) {
return ret;
}
if (dev_req_params->pwr_rx == FAST_MODE ||
dev_req_params->pwr_rx == FASTAUTO_MODE) {
if (host->hw_ver.major >= 3) {
ret = ufshcd_dme_configure_adapt(hba,
dev_req_params->gear_tx,
PA_INITIAL_ADAPT);
} else {
ret = ufshcd_dme_configure_adapt(hba,
dev_req_params->gear_tx,
PA_NO_ADAPT);
}
} else {
ret = ufshcd_dme_configure_adapt(hba,
dev_req_params->gear_tx,
PA_INITIAL_ADAPT);
dev_req_params->gear_tx,
PA_NO_ADAPT);
}
return ret;
@ -1342,6 +1475,7 @@ static int ufs_mtk_pre_link(struct ufs_hba *hba)
{
int ret;
u32 tmp;
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
ufs_mtk_get_controller_version(hba);
@ -1367,34 +1501,22 @@ static int ufs_mtk_pre_link(struct ufs_hba *hba)
ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
/* Enable the 1144 functions setting */
if (host->ip_ver == IP_VER_MT6989) {
ret = ufshcd_dme_get(hba, UIC_ARG_MIB(VS_DEBUGOMC), &tmp);
if (ret)
return ret;
tmp |= 0x10;
ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_DEBUGOMC), tmp);
}
return ret;
}
static void ufs_mtk_setup_clk_gating(struct ufs_hba *hba)
{
u32 ah_ms;
if (ufshcd_is_clkgating_allowed(hba)) {
if (ufshcd_is_auto_hibern8_supported(hba) && hba->ahit)
ah_ms = FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK,
hba->ahit);
else
ah_ms = 10;
ufshcd_clkgate_delay_set(hba->dev, ah_ms + 5);
}
}
static void ufs_mtk_post_link(struct ufs_hba *hba)
{
/* enable unipro clock gating feature */
ufs_mtk_cfg_unipro_cg(hba, true);
/* will be configured during probe hba */
if (ufshcd_is_auto_hibern8_supported(hba))
hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10) |
FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);
ufs_mtk_setup_clk_gating(hba);
}
static int ufs_mtk_link_startup_notify(struct ufs_hba *hba,
@ -1421,11 +1543,11 @@ static int ufs_mtk_device_reset(struct ufs_hba *hba)
{
struct arm_smccc_res res;
/* disable hba before device reset */
ufshcd_hba_stop(hba);
ufs_mtk_device_reset_ctrl(0, res);
/* disable hba in middle of device reset */
ufshcd_hba_stop(hba);
/*
* The reset signal is active low. UFS devices shall detect
* more than or equal to 1us of positive or negative RST_n
@ -1507,6 +1629,9 @@ static void ufs_mtk_vccqx_set_lpm(struct ufs_hba *hba, bool lpm)
{
struct ufs_vreg *vccqx = NULL;
if (!hba->vreg_info.vccq && !hba->vreg_info.vccq2)
return;
if (hba->vreg_info.vccq)
vccqx = hba->vreg_info.vccq;
else
@ -1726,6 +1851,8 @@ static void ufs_mtk_fixup_dev_quirks(struct ufs_hba *hba)
ufs_mtk_vreg_fix_vcc(hba);
ufs_mtk_vreg_fix_vccqx(hba);
ufs_mtk_fix_ahit(hba);
ufs_mtk_fix_clock_scaling(hba);
}
static void ufs_mtk_event_notify(struct ufs_hba *hba,