Commit da810279 authored by Martin K. Petersen's avatar Martin K. Petersen
Browse files

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: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parents c393614e 58636385
Loading
Loading
Loading
Loading
+156 −29
Original line number Diff line number Diff line
@@ -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 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_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;
}

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);
	}
		tmp |= 0x10;
		ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_DEBUGOMC), tmp);
	}

	return ret;
}
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,