[PATCH 3/4] ufs: Add MediaTek UFS driver
Neil Armstrong
neil.armstrong at linaro.org
Tue Oct 28 17:19:06 CET 2025
On 10/11/25 21:10, Igor Belwon wrote:
> Add the UFS driver for MediaTek platforms.
>
> Loosely based on the Linux driver, this UFS driver can successfully get a
> link and R/W access to the UFS chip on the MediaTek MT6878 mobile SoC,
> when U-Boot is running as lk, or as the kernel (Secure world access is
> not tested)
>
> Signed-off-by: Igor Belwon <igor.belwon at mentallysanemainliners.org>
> ---
> drivers/ufs/Kconfig | 14 ++
> drivers/ufs/Makefile | 1 +
> drivers/ufs/ufs-mediatek-sip.h | 56 ++++++
> drivers/ufs/ufs-mediatek.c | 403 +++++++++++++++++++++++++++++++++++++++++
> drivers/ufs/ufs-mediatek.h | 210 +++++++++++++++++++++
> 5 files changed, 684 insertions(+)
>
> diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig
> index b08ca08b07c0ae6c3288a84769203f52ab58b3b0..19d29bfb83dff8fe319ac2f4478db90502e38f76 100644
> --- a/drivers/ufs/Kconfig
> +++ b/drivers/ufs/Kconfig
> @@ -15,6 +15,20 @@ config CADENCE_UFS
> This selects the platform driver for the Cadence UFS host
> controller present on present TI's J721e devices.
>
> +config UFS_MEDIATEK
> + tristate "MediaTek UFS Host Controller Driver"
> + depends on UFS && ARCH_MEDIATEK
> + select PHY_MTK_UFS
> + help
> + This selects the MediaTek specific additions to UFSHCD platform driver.
> + UFS host on Mediatek needs some vendor specific configuration before
> + accessing the hardware which includes PHY configuration and vendor
> + specific registers.
> +
> + Select this if you have UFS controller on MediaTek chipset.
> +
> + If unsure, say N.
> +
> config UFS_PCI
> bool "PCI bus based UFS Controller support"
> depends on PCI && UFS
> diff --git a/drivers/ufs/Makefile b/drivers/ufs/Makefile
> index 2a378e45111968b09b3194094f679f6c2fb9ded5..b32a3576c59e4b4941cd435071b38aad4da14d98 100644
> --- a/drivers/ufs/Makefile
> +++ b/drivers/ufs/Makefile
> @@ -5,6 +5,7 @@
>
> obj-$(CONFIG_UFS) += ufs.o ufs-uclass.o
> obj-$(CONFIG_CADENCE_UFS) += cdns-platform.o
> +obj-$(CONFIG_UFS_MEDIATEK) += ufs-mediatek.o
> obj-$(CONFIG_QCOM_UFS) += ufs-qcom.o
> obj-$(CONFIG_TI_J721E_UFS) += ti-j721e-ufs.o
> obj-$(CONFIG_UFS_PCI) += ufs-pci.o
> diff --git a/drivers/ufs/ufs-mediatek-sip.h b/drivers/ufs/ufs-mediatek-sip.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..6a61f7bac69d65d8b08ca2edce791c38b6406107
> --- /dev/null
> +++ b/drivers/ufs/ufs-mediatek-sip.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 MediaTek Inc.
> + * Copyright (c) 2025, Igor Belwon <igor.belwon at mentallysanemainliners.org>
> + *
> + * Slimmed down header from Linux: drivers/ufs/host/ufs-mediatek-sip.h
> + */
> +
> +#ifndef _UFS_MEDIATEK_SIP_H
> +#define _UFS_MEDIATEK_SIP_H
> +
> +#include <linux/arm-smccc.h>
> +
> +/*
> + * SiP (Slicon Partner) commands
> + */
> +#define MTK_SIP_UFS_CONTROL ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
> + ARM_SMCCC_SMC_64, \
> + ARM_SMCCC_OWNER_SIP, 0x276)
> +#define UFS_MTK_SIP_DEVICE_RESET BIT(1)
> +#define UFS_MTK_SIP_REF_CLK_NOTIFICATION BIT(3)
> +
> +/*
> + * SMC call wrapper function
> + */
> +struct ufs_mtk_smc_arg {
> + unsigned long cmd;
> + struct arm_smccc_res *res;
> + unsigned long v1;
> + unsigned long v2;
> + unsigned long v3;
> + unsigned long v4;
> + unsigned long v5;
> + unsigned long v6;
> + unsigned long v7;
> +};
> +
> +static inline void _ufs_mtk_smc(struct ufs_mtk_smc_arg s)
> +{
> + arm_smccc_smc(MTK_SIP_UFS_CONTROL,
> + s.cmd,
> + s.v1, s.v2, s.v3, s.v4, s.v5, s.v6, s.res);
> +}
> +
> +#define ufs_mtk_smc(...) \
> + _ufs_mtk_smc((struct ufs_mtk_smc_arg) {__VA_ARGS__})
> +
> +/* SIP interface */
> +
> +#define ufs_mtk_ref_clk_notify(on, stage, res) \
> + ufs_mtk_smc(UFS_MTK_SIP_REF_CLK_NOTIFICATION, &(res), on, stage)
> +
> +#define ufs_mtk_device_reset_ctrl(high, res) \
> + ufs_mtk_smc(UFS_MTK_SIP_DEVICE_RESET, &(res), high)
> +
> +#endif /* !_UFS_MEDIATEK_SIP_H */
> diff --git a/drivers/ufs/ufs-mediatek.c b/drivers/ufs/ufs-mediatek.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..10e7990d6bbb54fe54b192d2ccaf7a44faeffd05
> --- /dev/null
> +++ b/drivers/ufs/ufs-mediatek.c
> @@ -0,0 +1,403 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2025, Igor Belwon <igor.belwon at mentallysanemainliners.org>
> + *
> + * Loosely based on Linux driver: drivers/ufs/host/ufs-mediatek.c
> + */
> +
> +#include <asm/io.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <dm/device_compat.h>
> +#include <generic-phy.h>
> +#include <ufs.h>
> +#include <asm/gpio.h>
> +#include <reset.h>
> +
> +#include <linux/arm-smccc.h>
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +
> +#include "ufs.h"
> +#include "ufs-mediatek.h"
> +#include "ufs-mediatek-sip.h"
> +
> +static void ufs_mtk_advertise_quirks(struct ufs_hba *hba)
> +{
> + hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL |
> + UFSHCD_QUIRK_MCQ_BROKEN_INTR |
> + UFSHCD_QUIRK_BROKEN_LSDBS_CAP;
> +}
> +
> +static int ufs_mtk_hce_enable_notify(struct ufs_hba *hba,
> + enum ufs_notify_change_status status)
> +{
> + struct ufs_mtk_host *host = dev_get_priv(hba->dev);
> +
> + if (status == PRE_CHANGE) {
> + if (host->caps & UFS_MTK_CAP_DISABLE_AH8) {
> + ufshcd_writel(hba, 0,
> + REG_AUTO_HIBERNATE_IDLE_TIMER);
> + hba->capabilities &= ~MASK_AUTO_HIBERN8_SUPPORT;
> + }
> +
> + /*
> + * Turn on CLK_CG early to bypass abnormal ERR_CHK signal
> + * to prevent host hang issue
> + */
> + ufshcd_writel(hba,
> + ufshcd_readl(hba, REG_UFS_XOUFS_CTRL) | 0x80,
> + REG_UFS_XOUFS_CTRL);
> +
> + /* DDR_EN setting */
> + if (host->ip_ver >= IP_VER_MT6989) {
> + ufshcd_rmwl(hba, UFS_MASK(0x7FFF, 8),
> + 0x453000, REG_UFS_MMIO_OPT_CTRL_0);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int ufs_mtk_unipro_set_lpm(struct ufs_hba *hba, bool lpm)
> +{
> + int ret;
> + struct ufs_mtk_host *host = dev_get_priv(hba->dev);
> +
> + ret = ufshcd_dme_set(hba,
> + UIC_ARG_MIB_SEL(VS_UNIPROPOWERDOWNCONTROL, 0),
> + lpm ? 1 : 0);
> + if (!ret || !lpm) {
> + /*
> + * Forcibly set as non-LPM mode if UIC commands is failed
> + * to use default hba_enable_delay_us value for re-enabling
> + * the host.
> + */
> + host->unipro_lpm = lpm;
> + }
> +
> + return ret;
> +}
> +
> +static int ufs_mtk_pre_link(struct ufs_hba *hba)
> +{
> + int ret;
> + u32 tmp;
> +
> + ret = ufs_mtk_unipro_set_lpm(hba, false);
> + if (ret)
> + return ret;
> +
> + /*
> + * Setting PA_Local_TX_LCC_Enable to 0 before link startup
> + * to make sure that both host and device TX LCC are disabled
> + * once link startup is completed.
> + */
> + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);
> + if (ret)
> + return ret;
> +
> + /* disable deep stall */
> + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
> + if (ret)
> + return ret;
> +
> + tmp &= ~(1 << 6);
> +
> + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
> + if (ret)
> + return ret;
> +
> + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_SCRAMBLING), tmp);
> +
> + return ret;
> +}
> +
> +static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable)
> +{
> + u32 tmp;
> +
> + if (enable) {
> + ufshcd_dme_get(hba,
> + UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
> + tmp = tmp |
> + (1 << RX_SYMBOL_CLK_GATE_EN) |
> + (1 << SYS_CLK_GATE_EN) |
> + (1 << TX_CLK_GATE_EN);
> + ufshcd_dme_set(hba,
> + UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
> +
> + ufshcd_dme_get(hba,
> + UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp);
> + tmp = tmp & ~(1 << TX_SYMBOL_CLK_REQ_FORCE);
> + ufshcd_dme_set(hba,
> + UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp);
> + } else {
> + ufshcd_dme_get(hba,
> + UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
> + tmp = tmp & ~((1 << RX_SYMBOL_CLK_GATE_EN) |
> + (1 << SYS_CLK_GATE_EN) |
> + (1 << TX_CLK_GATE_EN));
> + ufshcd_dme_set(hba,
> + UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
> +
> + ufshcd_dme_get(hba,
> + UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp);
> + tmp = tmp | (1 << TX_SYMBOL_CLK_REQ_FORCE);
> + ufshcd_dme_set(hba,
> + UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp);
> + }
> +}
> +
> +static void ufs_mtk_post_link(struct ufs_hba *hba)
> +{
> + /* enable unipro clock gating feature */
> + ufs_mtk_cfg_unipro_cg(hba, true);
> +}
> +
> +static int ufs_mtk_link_startup_notify(struct ufs_hba *hba,
> + enum ufs_notify_change_status status)
> +{
> + int ret = 0;
> +
> + switch (status) {
> + case PRE_CHANGE:
> + ret = ufs_mtk_pre_link(hba);
> + break;
> + case POST_CHANGE:
> + ufs_mtk_post_link(hba);
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int ufs_mtk_bind_mphy(struct ufs_hba *hba)
> +{
> + struct ufs_mtk_host *host = dev_get_priv(hba->dev);
> + int err = 0;
> +
> + err = generic_phy_get_by_index(hba->dev, 0, host->mphy);
> +
> + if (IS_ERR(host->mphy)) {
> + err = PTR_ERR(host->mphy);
> + if (err != -ENODEV) {
> + dev_info(hba->dev, "%s: Could NOT get a valid PHY %d\n", __func__,
> + err);
> + }
> + }
> +
> + if (err)
> + host->mphy = NULL;
> +
> + return err;
> +}
> +
> +static void ufs_mtk_init_reset_control(struct ufs_hba *hba,
> + struct reset_ctl **rc,
> + char *str)
> +{
> + *rc = devm_reset_control_get(hba->dev, str);
> + if (IS_ERR(*rc)) {
> + dev_info(hba->dev, "Failed to get reset control %s: %ld\n",
> + str, PTR_ERR(*rc));
> + *rc = NULL;
> + }
> +}
> +
> +static void ufs_mtk_init_reset(struct ufs_hba *hba)
> +{
> + struct ufs_mtk_host *host = dev_get_priv(hba->dev);
> +
> + ufs_mtk_init_reset_control(hba, &host->hci_reset,
> + "hci_rst");
> + ufs_mtk_init_reset_control(hba, &host->unipro_reset,
> + "unipro_rst");
> + ufs_mtk_init_reset_control(hba, &host->crypto_reset,
> + "crypto_rst");
> +}
> +
> +static void ufs_mtk_get_hw_ip_version(struct ufs_hba *hba)
> +{
> + struct ufs_mtk_host *host = dev_get_priv(hba->dev);
> + u32 hw_ip_ver;
> +
> + hw_ip_ver = ufshcd_readl(hba, REG_UFS_MTK_IP_VER);
> +
> + if (((hw_ip_ver & (0xFF << 24)) == (0x1 << 24)) ||
> + ((hw_ip_ver & (0xFF << 24)) == 0)) {
> + hw_ip_ver &= ~(0xFF << 24);
> + hw_ip_ver |= (0x1 << 28);
> + }
> +
> + host->ip_ver = hw_ip_ver;
> +
> + dev_info(hba->dev, "MediaTek UFS IP Version: 0x%x\n", hw_ip_ver);
> +}
> +
> +static int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on)
> +{
> + struct ufs_mtk_host *host = dev_get_priv(hba->dev);
> + struct arm_smccc_res res;
> + int timeout, time_checked = 0;
> + u32 value;
> +
> + if (host->ref_clk_enabled == on)
> + return 0;
> +
> + ufs_mtk_ref_clk_notify(on, PRE_CHANGE, res);
> +
> + if (on) {
> + ufshcd_writel(hba, REFCLK_REQUEST, REG_UFS_REFCLK_CTRL);
> + } else {
> + udelay(10);
> + ufshcd_writel(hba, REFCLK_RELEASE, REG_UFS_REFCLK_CTRL);
> + }
> +
> + /* Wait for ack */
> + timeout = REFCLK_REQ_TIMEOUT_US;
> + do {
> + value = ufshcd_readl(hba, REG_UFS_REFCLK_CTRL);
> +
> + /* Wait until ack bit equals to req bit */
> + if (((value & REFCLK_ACK) >> 1) == (value & REFCLK_REQUEST))
> + goto out;
> +
> + udelay(200);
> + time_checked += 200;
> + } while (time_checked != timeout);
> +
> + dev_err(hba->dev, "missing ack of refclk req, reg: 0x%x\n", value);
> +
> + /*
> + * If clock on timeout, assume clock is off, notify tfa do clock
> + * off setting.(keep DIFN disable, release resource)
> + * If clock off timeout, assume clock will off finally,
> + * set ref_clk_enabled directly.(keep DIFN disable, keep resource)
> + */
> + if (on)
> + ufs_mtk_ref_clk_notify(false, POST_CHANGE, res);
> + else
> + host->ref_clk_enabled = false;
> +
> + return -ETIMEDOUT;
> +
> +out:
> + host->ref_clk_enabled = on;
> + if (on)
> + udelay(10);
> +
> + ufs_mtk_ref_clk_notify(on, POST_CHANGE, res);
> +
> + return 0;
> +}
> +
> +/**
> + * ufs_mtk_init - bind phy with controller
> + * @hba: host controller instance
> + *
> + * Powers up PHY enabling clocks and regulators.
> + *
> + * Returns -ENODEV if binding fails, returns negative error
> + * on phy power up failure and returns zero on success.
> + */
> +static int ufs_mtk_init(struct ufs_hba *hba)
> +{
> + struct ufs_mtk_host *priv = dev_get_priv(hba->dev);
> + int err;
> +
> + priv->hba = hba;
> +
> + err = ufs_mtk_bind_mphy(hba);
> + if (err)
> + return -ENODEV;
> +
> + ufs_mtk_advertise_quirks(hba);
> +
> + ufs_mtk_init_reset(hba);
> +
> + // TODO: Clocking
> +
> + err = generic_phy_power_on(priv->mphy);
> + if (err) {
> + dev_err(hba->dev, "%s: phy init failed, err = %d\n",
> + __func__, err);
> + return err;
> + }
> +
> + ufs_mtk_setup_ref_clk(hba, true);
> + ufs_mtk_get_hw_ip_version(hba);
> +
> + return 0;
> +}
> +
> +static int ufs_mtk_device_reset(struct ufs_hba *hba)
> +{
> + struct arm_smccc_res res;
> +
> + ufs_mtk_device_reset_ctrl(0, res);
> +
> + /*
> + * The reset signal is active low. UFS devices shall detect
> + * more than or equal to 1us of positive or negative RST_n
> + * pulse width.
> + *
> + * To be on safe side, keep the reset low for at least 10us.
> + */
> + udelay(13);
> +
> + ufs_mtk_device_reset_ctrl(1, res);
> +
> + /* Some devices may need time to respond to rst_n */
> + mdelay(13);
> +
> + dev_dbg(hba->dev, "device reset done\n");
> +
> + return 0;
> +}
> +
> +static struct ufs_hba_ops ufs_mtk_hba_ops = {
> + .init = ufs_mtk_init,
> + .hce_enable_notify = ufs_mtk_hce_enable_notify,
> + .link_startup_notify = ufs_mtk_link_startup_notify,
> + .device_reset = ufs_mtk_device_reset,
> +};
> +
> +static int ufs_mtk_probe(struct udevice *dev)
> +{
> + int ret;
> +
> + ret = ufshcd_probe(dev, &ufs_mtk_hba_ops);
> + if (ret) {
> + dev_err(dev, "ufshcd_probe() failed, ret:%d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ufs_mtk_bind(struct udevice *dev)
> +{
> + struct udevice *scsi_dev;
> +
> + return ufs_scsi_bind(dev, &scsi_dev);
> +}
> +
> +static const struct udevice_id ufs_mtk_ids[] = {
> + { .compatible = "mediatek,mt6878-ufshci" },
> + {},
> +};
> +
> +U_BOOT_DRIVER(mediatek_ufshci) = {
> + .name = "mediatek-ufshci",
> + .id = UCLASS_UFS,
> + .of_match = ufs_mtk_ids,
> + .probe = ufs_mtk_probe,
> + .bind = ufs_mtk_bind,
> + .priv_auto = sizeof(struct ufs_mtk_host),
> +};
> diff --git a/drivers/ufs/ufs-mediatek.h b/drivers/ufs/ufs-mediatek.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..11a83d34c5b93ce173575eb8bee895f6f70af2d6
> --- /dev/null
> +++ b/drivers/ufs/ufs-mediatek.h
> @@ -0,0 +1,210 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2019 MediaTek Inc.
> + * Copyright (c) 2025, Igor Belwon <igor.belwon at mentallysanemainliners.org>
> + *
> + * Slimmed down header from Linux: drivers/ufs/host/ufs-mediatek.h
> + */
> +
> +#ifndef _UFS_MEDIATEK_H
> +#define _UFS_MEDIATEK_H
> +
> +#include <clk.h>
> +#include <linux/bitops.h>
> +
> +/*
> + * MCQ define and struct
> + */
> +#define UFSHCD_MAX_Q_NR 8
> +#define MTK_MCQ_INVALID_IRQ 0xFFFF
> +
> +/* REG_UFS_MMIO_OPT_CTRL_0 160h */
> +#define EHS_EN BIT(0)
> +#define PFM_IMPV BIT(1)
> +#define MCQ_MULTI_INTR_EN BIT(2)
> +#define MCQ_CMB_INTR_EN BIT(3)
> +#define MCQ_AH8 BIT(4)
> +
> +#define MCQ_INTR_EN_MSK (MCQ_MULTI_INTR_EN | MCQ_CMB_INTR_EN)
> +
> +/*
> + * Vendor specific UFSHCI Registers
> + */
> +#define REG_UFS_XOUFS_CTRL 0x140
> +#define REG_UFS_REFCLK_CTRL 0x144
> +#define REG_UFS_MMIO_OPT_CTRL_0 0x160
> +#define REG_UFS_EXTREG 0x2100
> +#define REG_UFS_MPHYCTRL 0x2200
> +#define REG_UFS_MTK_IP_VER 0x2240
> +#define REG_UFS_REJECT_MON 0x22AC
> +#define REG_UFS_DEBUG_SEL 0x22C0
> +#define REG_UFS_PROBE 0x22C8
> +#define REG_UFS_DEBUG_SEL_B0 0x22D0
> +#define REG_UFS_DEBUG_SEL_B1 0x22D4
> +#define REG_UFS_DEBUG_SEL_B2 0x22D8
> +#define REG_UFS_DEBUG_SEL_B3 0x22DC
> +
> +#define REG_UFS_MTK_SQD 0x2800
> +#define REG_UFS_MTK_SQIS 0x2814
> +#define REG_UFS_MTK_CQD 0x281C
> +#define REG_UFS_MTK_CQIS 0x2824
> +
> +#define REG_UFS_MCQ_STRIDE 0x30
> +
> +/*
> + * Ref-clk control
> + *
> + * Values for register REG_UFS_REFCLK_CTRL
> + */
> +#define REFCLK_RELEASE 0x0
> +#define REFCLK_REQUEST BIT(0)
> +#define REFCLK_ACK BIT(1)
> +
> +#define REFCLK_REQ_TIMEOUT_US 3000
> +#define REFCLK_DEFAULT_WAIT_US 32
> +
> +/*
> + * Other attributes
> + */
> +#define VS_DEBUGCLOCKENABLE 0xD0A1
> +#define VS_SAVEPOWERCONTROL 0xD0A6
> +#define VS_UNIPROPOWERDOWNCONTROL 0xD0A8
> +
> +/*
> + * Vendor specific link state
> + */
> +enum {
> + VS_LINK_DISABLED = 0,
> + VS_LINK_DOWN = 1,
> + VS_LINK_UP = 2,
> + VS_LINK_HIBERN8 = 3,
> + VS_LINK_LOST = 4,
> + VS_LINK_CFG = 5,
> +};
> +
> +/*
> + * Vendor specific host controller state
> + */
> +enum {
> + VS_HCE_RESET = 0,
> + VS_HCE_BASE = 1,
> + VS_HCE_OOCPR_WAIT = 2,
> + VS_HCE_DME_RESET = 3,
> + VS_HCE_MIDDLE = 4,
> + VS_HCE_DME_ENABLE = 5,
> + VS_HCE_DEFAULTS = 6,
> + VS_HIB_IDLEEN = 7,
> + VS_HIB_ENTER = 8,
> + VS_HIB_ENTER_CONF = 9,
> + VS_HIB_MIDDLE = 10,
> + VS_HIB_WAITTIMER = 11,
> + VS_HIB_EXIT_CONF = 12,
> + VS_HIB_EXIT = 13,
> +};
> +
> +/*
> + * VS_DEBUGCLOCKENABLE
> + */
> +enum {
> + TX_SYMBOL_CLK_REQ_FORCE = 5,
> +};
> +
> +/*
> + * VS_SAVEPOWERCONTROL
> + */
> +enum {
> + RX_SYMBOL_CLK_GATE_EN = 0,
> + SYS_CLK_GATE_EN = 2,
> + TX_CLK_GATE_EN = 3,
> +};
> +
> +/*
> + * Host capability
> + */
> +enum ufs_mtk_host_caps {
> + UFS_MTK_CAP_BOOST_CRYPT_ENGINE = 1 << 0,
> + UFS_MTK_CAP_VA09_PWR_CTRL = 1 << 1,
> + UFS_MTK_CAP_DISABLE_AH8 = 1 << 2,
> + UFS_MTK_CAP_BROKEN_VCC = 1 << 3,
> +
> + /*
> + * Override UFS_MTK_CAP_BROKEN_VCC's behavior to
> + * allow vccqx upstream to enter LPM
> + */
> + UFS_MTK_CAP_ALLOW_VCCQX_LPM = 1 << 5,
> + UFS_MTK_CAP_PMC_VIA_FASTAUTO = 1 << 6,
> + UFS_MTK_CAP_TX_SKEW_FIX = 1 << 7,
> + UFS_MTK_CAP_DISABLE_MCQ = 1 << 8,
> + /* Control MTCMOS with RTFF */
> + UFS_MTK_CAP_RTFF_MTCMOS = 1 << 9,
> +
> + UFS_MTK_CAP_MCQ_BROKEN_RTC = 1 << 10,
> +};
> +
> +struct ufs_mtk_hw_ver {
> + u8 step;
> + u8 minor;
> + u8 major;
> +};
> +
> +struct ufs_mtk_mcq_intr_info {
> + struct ufs_hba *hba;
> + u32 irq;
> + u8 qid;
> +};
> +
> +struct ufs_mtk_host {
> + struct phy *mphy;
> + struct reset_ctl *unipro_reset;
> + struct reset_ctl *crypto_reset;
> + struct reset_ctl *hci_reset;
> + struct ufs_hba *hba;
> + struct ufs_mtk_crypt_cfg *crypt;
> + struct clk_bulk clks;
> + struct ufs_mtk_hw_ver hw_ver;
> + enum ufs_mtk_host_caps caps;
> + bool mphy_powered_on;
> + bool unipro_lpm;
> + bool ref_clk_enabled;
> + bool is_clks_enabled;
> + u16 ref_clk_ungating_wait_us;
> + u16 ref_clk_gating_wait_us;
> + u32 ip_ver;
> + bool legacy_ip_ver;
> +
> + bool mcq_set_intr;
> + bool is_mcq_intr_enabled;
> + int mcq_nr_intr;
> + struct ufs_mtk_mcq_intr_info mcq_intr_info[UFSHCD_MAX_Q_NR];
> +};
> +
> +/* MTK delay of autosuspend: 500 ms */
> +#define MTK_RPM_AUTOSUSPEND_DELAY_MS 500
> +
> +/* MTK RTT support number */
> +#define MTK_MAX_NUM_RTT 2
> +
> +/* UFSHCI MTK ip version value */
> +enum {
> + /* UFSHCI 3.1 */
> + IP_VER_MT6983 = 0x10360000,
> + IP_VER_MT6878 = 0x10420200,
> +
> + /* UFSHCI 4.0 */
> + IP_VER_MT6897 = 0x10440000,
> + IP_VER_MT6989 = 0x10450000,
> + IP_VER_MT6899 = 0x10450100,
> + IP_VER_MT6991_A0 = 0x10460000,
> + IP_VER_MT6991_B0 = 0x10470000,
> + IP_VER_MT6993 = 0x10480000,
> +
> + IP_VER_NONE = 0xFFFFFFFF
> +};
> +
> +enum ip_ver_legacy {
> + IP_LEGACY_VER_MT6781 = 0x10380000,
> + IP_LEGACY_VER_MT6879 = 0x10360000,
> + IP_LEGACY_VER_MT6893 = 0x20160706
> +};
> +
> +#endif /* !_UFS_MEDIATEK_H */
>
Reviewed-by: Neil Armstrong <neil.armstrong at linaro.org>
More information about the U-Boot
mailing list