Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
750 lines
19 KiB
C
750 lines
19 KiB
C
/*
|
|
* Copyright (C) 2017 MediaTek Inc.
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See http://www.gnu.org/licenses/gpl-2.0.html for more details.
|
|
*/
|
|
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/pinctrl/pinctrl.h>
|
|
#include "ufs.h"
|
|
#include "ufshcd.h"
|
|
#include "ufs_quirks.h"
|
|
#include "unipro.h"
|
|
#include "ufs-mtk.h"
|
|
#include "ufs-mtk-platform.h"
|
|
#include "mtk_idle.h"
|
|
#include "mtk_spm_resource_req.h"
|
|
#include "mtk_secure_api.h"
|
|
|
|
#ifdef MTK_UFS_HQA
|
|
#include <mtk_reboot.h>
|
|
#endif
|
|
|
|
#ifdef UPMU_READY
|
|
#include <mt-plat/upmu_common.h>
|
|
#endif
|
|
|
|
#ifdef CLKBUF_READY
|
|
#include "mtk_clkbuf_ctl.h"
|
|
#endif
|
|
|
|
static void __iomem *ufs_mtk_mmio_base_gpio;
|
|
static void __iomem *ufs_mtk_mmio_base_infracfg_ao;
|
|
static void __iomem *ufs_mtk_mmio_base_ufs_mphy;
|
|
static struct regulator *reg_va09;
|
|
|
|
/**
|
|
* ufs_mtk_pltfrm_pwr_change_final_gear - change pwr mode fianl gear value.
|
|
*/
|
|
void ufs_mtk_pltfrm_pwr_change_final_gear(struct ufs_hba *hba,
|
|
struct ufs_pa_layer_attr *final)
|
|
{
|
|
/* Change final gear if necessary */
|
|
}
|
|
|
|
#ifdef MTK_UFS_HQA
|
|
|
|
/* UFS write is performance 200MB/s, 4KB will take around 20us */
|
|
/* Set delay before reset to 100us to cover 4KB & >4KB write */
|
|
|
|
#define UFS_SPOH_USDELAY_BEFORE_RESET (100)
|
|
|
|
void random_delay(struct ufs_hba *hba)
|
|
{
|
|
u32 time = (sched_clock()%UFS_SPOH_USDELAY_BEFORE_RESET);
|
|
|
|
udelay(time);
|
|
}
|
|
void wdt_pmic_full_reset(struct ufs_hba *hba)
|
|
{
|
|
/*
|
|
* Cmd issue to PMIC on MT6771 will take around 20us ~ 30us, in order
|
|
* to speed up VEMC disable time, we disable VEMC first coz PMIC cold
|
|
* reset may take longer to disable VEMC in it's reset flow. Can not
|
|
* use regulator_disable() here because it can not use in preemption
|
|
* disabled context. Use pmic raw API without nlock instead.
|
|
*/
|
|
pmic_set_register_value_nolock(PMIC_RG_LDO_VEMC_EN, 0);
|
|
|
|
/* Need reset external LDO for VUFS18, UFS needs VEMC&VUFS18 reset at
|
|
* the same time
|
|
*/
|
|
pmic_set_register_value_nolock(PMIC_RG_STRUP_EXT_PMIC_SEL, 0x1);
|
|
|
|
/* VA09 off, may not require */
|
|
/* pmic_set_register_value_nolock(PMIC_RG_STRUP_EXT_PMIC_EN, 0x1); */
|
|
/* pmic_set_register_value_nolock(PMIC_RG_STRUP_EXT_PMIC_SEL, 0x2); */
|
|
|
|
/* PMIC cold reset */
|
|
pmic_set_register_value_nolock(PMIC_RG_CRST, 1);
|
|
}
|
|
#endif
|
|
|
|
#define PMIC_REG_MASK (0xFFFF)
|
|
#define PMIC_REG_SHIFT (0)
|
|
void ufs_mtk_pltfrm_gpio_trigger_and_debugInfo_dump(struct ufs_hba *hba)
|
|
{
|
|
#ifdef UPMU_READY
|
|
int vcc_enabled, vcc_value, vccq2_enabled, va09_enabled;
|
|
|
|
vcc_enabled = pmic_get_register_value(PMIC_RG_LDO_VEMC_EN);
|
|
vcc_value = pmic_get_register_value(PMIC_RG_VEMC_VOSEL);
|
|
vccq2_enabled = pmic_get_register_value(PMIC_DA_EXT_PMIC_EN1);
|
|
va09_enabled = pmic_get_register_value(PMIC_DA_EXT_PMIC_EN1);
|
|
/* dump vcc, vccq2 and va09 info */
|
|
dev_info(hba->dev,
|
|
"vcc_enabled:%d, vcc_value:%d, vccq2_enabled:%d, va09:%d!!!\n",
|
|
vcc_enabled, vcc_value, vccq2_enabled, va09_enabled);
|
|
/* dump clock buffer */
|
|
clk_buf_dump_clkbuf_log();
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_MTK_UFS_DEGUG_GPIO_TRIGGER
|
|
#include <mt-plat/sync_write.h>
|
|
|
|
struct device_node *msdc_gpio_node;
|
|
void __iomem *msdc_gpio_base;
|
|
static struct regulator *vmc;
|
|
|
|
#define MSDC1_GPIO_MODE17 (msdc_gpio_base + 0x410)
|
|
#define MSDC1_GPIO_DIR4 (msdc_gpio_base + 0x40)
|
|
#define MSDC1_GPIO_DOUT4 (msdc_gpio_base + 0x140)
|
|
|
|
#define MSDC1_GPIO_DOUT (MSDC1_GPIO_DOUT4)
|
|
#define MSDC1_GPIO_DOUT_DIR_FIELD (0x00000F00)
|
|
#define MSDC1_GPIO_DOUT_DIR_VAL_SET (0x0000000F)
|
|
#define MSDC1_GPIO_DOUT_DIR_VAL_CLR (0x00000000)
|
|
|
|
#define MSDC_READ32(reg) __raw_readl(reg)
|
|
#define MSDC_WRITE32(reg, val) mt_reg_sync_writel(val, reg)
|
|
static inline unsigned int uffs(unsigned int x)
|
|
{
|
|
unsigned int r = 1;
|
|
|
|
if (!x)
|
|
return 0;
|
|
if (!(x & 0xffff)) {
|
|
x >>= 16;
|
|
r += 16;
|
|
}
|
|
if (!(x & 0xff)) {
|
|
x >>= 8;
|
|
r += 8;
|
|
}
|
|
if (!(x & 0xf)) {
|
|
x >>= 4;
|
|
r += 4;
|
|
}
|
|
if (!(x & 3)) {
|
|
x >>= 2;
|
|
r += 2;
|
|
}
|
|
if (!(x & 1)) {
|
|
x >>= 1;
|
|
r += 1;
|
|
}
|
|
return r;
|
|
}
|
|
#define MSDC_SET_FIELD(reg, field, val) \
|
|
do { \
|
|
unsigned int tv = MSDC_READ32(reg); \
|
|
tv &= ~(field); \
|
|
tv |= ((val) << (uffs((unsigned int)field) - 1)); \
|
|
MSDC_WRITE32(reg, tv); \
|
|
} while (0)
|
|
|
|
void ufs_mtk_pltfrm_gpio_trigger_init(struct ufs_hba *hba)
|
|
{
|
|
/* Need porting for UFS Debug*/
|
|
/* Set gpio mode */
|
|
/* Set gpio output */
|
|
/* Set gpio default low */
|
|
dev_info(hba->dev, "%s: trigger_gpio_init!!!\n",
|
|
__func__);
|
|
|
|
if (msdc_gpio_node == NULL) {
|
|
msdc_gpio_node = of_find_compatible_node(NULL, NULL,
|
|
"mediatek,GPIO");
|
|
msdc_gpio_base = of_iomap(msdc_gpio_node, 0);
|
|
pr_notice("of_iomap for gpio base @ 0x%p\n", msdc_gpio_base);
|
|
};
|
|
|
|
/*
|
|
* Power on
|
|
* Be sure add dts in ufs node
|
|
* vqmmc-supply = <&mt_pmic_vmc_ldo_reg>;
|
|
*/
|
|
vmc = regulator_get(hba->dev, "vqmmc");
|
|
if (!IS_ERR(vmc)) {
|
|
regulator_set_voltage(vmc, VOL_3000 * 1000, VOL_3000 * 1000);
|
|
regulator_enable(vmc);
|
|
}
|
|
|
|
/*
|
|
* Set gpio to gpio mode. (dat0/dat1/dat2/dat3 in GPIO)
|
|
* Be sure remove msdc_set_pin_mode in msdc_cust.c set mode
|
|
* back to msdc.
|
|
*/
|
|
MSDC_SET_FIELD(MSDC1_GPIO_MODE17, 0x0000FFFF, 0x0);
|
|
|
|
/*
|
|
* Set gpio dir output. (dat0/dat1/dat2/dat3 in GPIO)
|
|
*/
|
|
MSDC_SET_FIELD(MSDC1_GPIO_DIR4,
|
|
MSDC1_GPIO_DOUT_DIR_FIELD,
|
|
MSDC1_GPIO_DOUT_DIR_VAL_SET);
|
|
|
|
/*
|
|
* Set gpio output 0. (dat0/dat1/dat2/dat3 in GPIO)
|
|
*/
|
|
MSDC_SET_FIELD(MSDC1_GPIO_DOUT,
|
|
MSDC1_GPIO_DOUT_DIR_FIELD,
|
|
MSDC1_GPIO_DOUT_DIR_VAL_CLR);
|
|
}
|
|
|
|
void ufs_mtk_pltfrm_gpio_trigger(int value)
|
|
{
|
|
if (-EPROBE_DEFER == PTR_ERR(vmc)) {
|
|
vmc = regulator_get(ufs_mtk_hba->dev, "vqmmc");
|
|
if (!IS_ERR(vmc)) {
|
|
regulator_set_voltage(vmc,
|
|
VOL_3000 * 1000, VOL_3000 * 1000);
|
|
regulator_enable(vmc);
|
|
}
|
|
}
|
|
|
|
if (value == 1) {
|
|
MSDC_SET_FIELD(MSDC1_GPIO_DOUT,
|
|
MSDC1_GPIO_DOUT_DIR_FIELD,
|
|
MSDC1_GPIO_DOUT_DIR_VAL_SET);
|
|
} else {
|
|
MSDC_SET_FIELD(MSDC1_GPIO_DOUT,
|
|
MSDC1_GPIO_DOUT_DIR_FIELD,
|
|
MSDC1_GPIO_DOUT_DIR_VAL_CLR);
|
|
}
|
|
|
|
pr_notice("error trigger, %d\n", value);
|
|
|
|
pr_info("%s vmc=%d uv\n", __func__, regulator_get_voltage(vmc));
|
|
|
|
pr_info("%s mode=0x%x, dir=0x%x, out=0x%x\n", __func__,
|
|
MSDC_READ32(MSDC1_GPIO_MODE17),
|
|
MSDC_READ32(MSDC1_GPIO_DIR4),
|
|
MSDC_READ32(MSDC1_GPIO_DOUT));
|
|
}
|
|
#endif
|
|
|
|
int ufs_mtk_pltfrm_xo_ufs_req(struct ufs_hba *hba, bool on)
|
|
{
|
|
u32 value;
|
|
|
|
if (!hba->card) {
|
|
/*
|
|
* In case card is not init, just ignore it.
|
|
* Because clock is default on.
|
|
* After card init done the clk control
|
|
* will be normal.
|
|
*/
|
|
dev_info(hba->dev, "%s: card not init skip\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Delay before disable ref-clk: H8 -> delay A -> disable ref-clk
|
|
* delayA
|
|
* Hynix 30us
|
|
* Samsung 1us
|
|
* Toshiba 100us
|
|
*/
|
|
if (!on) {
|
|
switch (hba->card->wmanufacturerid) {
|
|
case UFS_VENDOR_TOSHIBA:
|
|
udelay(100);
|
|
break;
|
|
case UFS_VENDOR_SKHYNIX:
|
|
udelay(30);
|
|
break;
|
|
case UFS_VENDOR_SAMSUNG:
|
|
udelay(1);
|
|
break;
|
|
default:
|
|
udelay(30);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (on) {
|
|
clk_buf_ctrl(CLK_BUF_UFS, true);
|
|
} else {
|
|
clk_buf_ctrl(CLK_BUF_UFS, false);
|
|
spm_resource_req(SPM_RESOURCE_USER_UFS, SPM_RESOURCE_RELEASE);
|
|
}
|
|
|
|
/* Delay after enable ref-clk: enable ref-clk -> delay B -> leave H8
|
|
* delayB
|
|
* Hynix 30us
|
|
* Samsung max(1us,32us)
|
|
* Toshiba 32us
|
|
*/
|
|
if (on) {
|
|
switch (hba->card->wmanufacturerid) {
|
|
case UFS_VENDOR_TOSHIBA:
|
|
udelay(32);
|
|
break;
|
|
case UFS_VENDOR_SKHYNIX:
|
|
udelay(30);
|
|
break;
|
|
case UFS_VENDOR_SAMSUNG:
|
|
udelay(32);
|
|
break;
|
|
default:
|
|
udelay(30);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ufs_mtk_pltfrm_ufs_device_reset(struct ufs_hba *hba)
|
|
{
|
|
mt_secure_call(MTK_SIP_KERNEL_UFS_CTL, 2, 0, 0, 0);
|
|
|
|
/*
|
|
* The reset signal is active low.
|
|
* The UFS device 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.
|
|
*/
|
|
usleep_range(10, 15);
|
|
|
|
mt_secure_call(MTK_SIP_KERNEL_UFS_CTL, 2, 1, 0, 0);
|
|
|
|
/* some device may need time to respond to rst_n */
|
|
usleep_range(10000, 15000);
|
|
|
|
dev_info(hba->dev, "%s: UFS device reset done\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* In early-porting stage, because of no bootrom,
|
|
* something finished by bootrom shall be finished here instead.
|
|
* Returns:
|
|
* 0: Successful.
|
|
* Non-zero: Failed.
|
|
*/
|
|
int ufs_mtk_pltfrm_bootrom_deputy(struct ufs_hba *hba)
|
|
{
|
|
#ifdef CONFIG_FPGA_EARLY_PORTING
|
|
mt_secure_call(MTK_SIP_KERNEL_UFS_CTL, 2, 1, 0, 0);
|
|
#endif
|
|
#ifdef CONFIG_MTK_UFS_DEGUG_GPIO_TRIGGER
|
|
ufs_mtk_pltfrm_gpio_trigger_init(hba);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ufs_mtk_pltfrm_ref_clk_ctrl(struct ufs_hba *hba, bool on)
|
|
{
|
|
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
|
|
int ret = 0;
|
|
u32 val = 0;
|
|
|
|
if (on) {
|
|
/* Host need turn on clock by itself */
|
|
ret = ufs_mtk_pltfrm_xo_ufs_req(hba, true);
|
|
if (ret)
|
|
goto out;
|
|
} else {
|
|
val = VENDOR_POWERSTATE_HIBERNATE;
|
|
ufs_mtk_wait_link_state(hba, &val,
|
|
hba->clk_gating.delay_ms);
|
|
|
|
if (val == VENDOR_POWERSTATE_HIBERNATE) {
|
|
/* Host need turn off clock by itself */
|
|
ret = ufs_mtk_pltfrm_xo_ufs_req(hba, false);
|
|
if (ret)
|
|
goto out;
|
|
} else if (val == VENDOR_POWERSTATE_DISABLED) {
|
|
/* hba stop after shoutdown, do nothing */
|
|
} else {
|
|
dev_info(hba->dev, "%s: power state (%d) clk not off\n",
|
|
__func__, val);
|
|
dev_info(hba->dev, "%s: ah8_en(%d), ah_reg = 0x%x\n",
|
|
__func__,
|
|
ufs_mtk_auto_hibern8_enabled,
|
|
ufshcd_readl(hba,
|
|
REG_AUTO_HIBERNATE_IDLE_TIMER));
|
|
}
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ufs_mtk_deepidle_hibern8_check - callback function for Deepidle & SODI.
|
|
* Release all resources: DRAM/26M clk/Main PLL and dsiable 26M ref clk if
|
|
* in H8.
|
|
*
|
|
* @return: 0 for success, negative/postive error code otherwise
|
|
*/
|
|
int ufs_mtk_pltfrm_deepidle_check_h8(void)
|
|
{
|
|
/* No Need for MT6785 */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* ufs_mtk_deepidle_leave - callback function for leaving Deepidle & SODI.
|
|
*/
|
|
void ufs_mtk_pltfrm_deepidle_leave(void)
|
|
{
|
|
/* No Need for MT6785 */
|
|
}
|
|
|
|
/**
|
|
* ufs_mtk_pltfrm_deepidle_lock - Deepidle & SODI lock.
|
|
* @hba: per-adapter instance
|
|
* @lock: lock or unlock deepidle & SODI entrance.
|
|
*/
|
|
void ufs_mtk_pltfrm_deepidle_lock(struct ufs_hba *hba, bool lock)
|
|
{
|
|
/* No Need for MT6785 */
|
|
}
|
|
|
|
int ufs_mtk_pltfrm_host_sw_rst(struct ufs_hba *hba, u32 target)
|
|
{
|
|
u32 reg;
|
|
|
|
if (!ufs_mtk_mmio_base_infracfg_ao) {
|
|
dev_info(hba->dev,
|
|
"ufs_mtk_host_sw_rst: failed, null ufs_mtk_mmio_base_infracfg_ao.\n");
|
|
return 1;
|
|
}
|
|
|
|
dev_dbg(hba->dev, "ufs_mtk_host_sw_rst: 0x%x\n", target);
|
|
|
|
if (target & SW_RST_TARGET_UFSHCI) {
|
|
/* reset HCI */
|
|
reg = readl(ufs_mtk_mmio_base_infracfg_ao +
|
|
REG_UFSHCI_SW_RST_SET);
|
|
reg = reg | (1 << REG_UFSHCI_SW_RST_SET_BIT);
|
|
writel(reg,
|
|
ufs_mtk_mmio_base_infracfg_ao + REG_UFSHCI_SW_RST_SET);
|
|
}
|
|
|
|
if (target & SW_RST_TARGET_UFSCPT) {
|
|
/* reset AES */
|
|
reg = readl(ufs_mtk_mmio_base_infracfg_ao +
|
|
REG_UFSCPT_SW_RST_SET);
|
|
reg = reg | (1 << REG_UFSCPT_SW_RST_SET_BIT);
|
|
writel(reg,
|
|
ufs_mtk_mmio_base_infracfg_ao + REG_UFSCPT_SW_RST_SET);
|
|
}
|
|
|
|
if (target & SW_RST_TARGET_UNIPRO) {
|
|
/* reset UniPro */
|
|
reg = readl(ufs_mtk_mmio_base_infracfg_ao +
|
|
REG_UNIPRO_SW_RST_SET);
|
|
reg = reg | (1 << REG_UNIPRO_SW_RST_SET_BIT);
|
|
writel(reg,
|
|
ufs_mtk_mmio_base_infracfg_ao + REG_UNIPRO_SW_RST_SET);
|
|
}
|
|
|
|
if (target & SW_RST_TARGET_MPHY) {
|
|
/* reset Mphy */
|
|
reg = readl(ufs_mtk_mmio_base_infracfg_ao +
|
|
REG_UFSPHY_SW_RST_SET);
|
|
reg = reg | (1 << REG_UFSPHY_SW_RST_SET_BIT);
|
|
writel(reg,
|
|
ufs_mtk_mmio_base_infracfg_ao + REG_UFSPHY_SW_RST_SET);
|
|
}
|
|
|
|
udelay(100);
|
|
|
|
if (target & SW_RST_TARGET_UFSHCI) {
|
|
/* clear HCI reset */
|
|
reg = readl(ufs_mtk_mmio_base_infracfg_ao +
|
|
REG_UFSHCI_SW_RST_CLR);
|
|
reg = reg | (1 << REG_UFSHCI_SW_RST_CLR_BIT);
|
|
writel(reg,
|
|
ufs_mtk_mmio_base_infracfg_ao + REG_UFSHCI_SW_RST_CLR);
|
|
}
|
|
|
|
if (target & SW_RST_TARGET_UFSCPT) {
|
|
/* clear AES reset */
|
|
reg = readl(ufs_mtk_mmio_base_infracfg_ao +
|
|
REG_UFSCPT_SW_RST_CLR);
|
|
reg = reg | (1 << REG_UFSCPT_SW_RST_CLR_BIT);
|
|
writel(reg,
|
|
ufs_mtk_mmio_base_infracfg_ao + REG_UFSCPT_SW_RST_CLR);
|
|
}
|
|
|
|
if (target & SW_RST_TARGET_UNIPRO) {
|
|
/* clear UniPro reset */
|
|
reg = readl(ufs_mtk_mmio_base_infracfg_ao +
|
|
REG_UNIPRO_SW_RST_CLR);
|
|
reg = reg | (1 << REG_UNIPRO_SW_RST_CLR_BIT);
|
|
writel(reg,
|
|
ufs_mtk_mmio_base_infracfg_ao + REG_UNIPRO_SW_RST_CLR);
|
|
}
|
|
|
|
if (target & SW_RST_TARGET_MPHY) {
|
|
/* clear Mphy reset */
|
|
reg = readl(ufs_mtk_mmio_base_infracfg_ao +
|
|
REG_UFSPHY_SW_RST_CLR);
|
|
reg = reg | (1 << REG_UFSPHY_SW_RST_CLR_BIT);
|
|
writel(reg,
|
|
ufs_mtk_mmio_base_infracfg_ao + REG_UFSPHY_SW_RST_CLR);
|
|
}
|
|
|
|
udelay(100);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ufs_mtk_pltfrm_init(void)
|
|
{
|
|
ufs_mtk_hba->caps |= UFSHCD_CAP_CLK_GATING;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ufs_mtk_pltfrm_parse_dt(struct ufs_hba *hba)
|
|
{
|
|
struct device_node *node_gpio;
|
|
struct device_node *node_ufs_mphy;
|
|
struct device_node *node_infracfg_ao;
|
|
int err = 0;
|
|
|
|
/* get ufs_mtk_gpio */
|
|
node_gpio = of_find_compatible_node(NULL, NULL, "mediatek,gpio");
|
|
|
|
if (node_gpio) {
|
|
ufs_mtk_mmio_base_gpio = of_iomap(node_gpio, 0);
|
|
|
|
if (IS_ERR(*(void **)&ufs_mtk_mmio_base_gpio)) {
|
|
err = PTR_ERR(*(void **)&ufs_mtk_mmio_base_gpio);
|
|
dev_err(hba->dev, "error: ufs_mtk_mmio_base_gpio init fail\n");
|
|
ufs_mtk_mmio_base_gpio = NULL;
|
|
}
|
|
} else
|
|
dev_err(hba->dev, "error: node_gpio init fail\n");
|
|
|
|
/* get ufs_mtk_mmio_base_ufs_mphy */
|
|
|
|
node_ufs_mphy =
|
|
of_find_compatible_node(NULL, NULL, "mediatek,ufs_mphy");
|
|
|
|
if (node_ufs_mphy) {
|
|
ufs_mtk_mmio_base_ufs_mphy = of_iomap(node_ufs_mphy, 0);
|
|
|
|
if (IS_ERR(*(void **)&ufs_mtk_mmio_base_ufs_mphy)) {
|
|
err = PTR_ERR(*(void **)&ufs_mtk_mmio_base_ufs_mphy);
|
|
dev_err(hba->dev, "error: ufs_mtk_mmio_base_ufs_mphy init fail\n");
|
|
ufs_mtk_mmio_base_ufs_mphy = NULL;
|
|
}
|
|
} else
|
|
dev_err(hba->dev, "error: ufs_mtk_mmio_base_ufs_mphy init fail\n");
|
|
|
|
|
|
/* get ufs_mtk_mmio_base_infracfg_ao */
|
|
node_infracfg_ao =
|
|
of_find_compatible_node(NULL, NULL, "mediatek,infracfg_ao");
|
|
if (node_infracfg_ao) {
|
|
ufs_mtk_mmio_base_infracfg_ao = of_iomap(node_infracfg_ao, 0);
|
|
|
|
if (IS_ERR(*(void **)&ufs_mtk_mmio_base_infracfg_ao)) {
|
|
err = PTR_ERR(*(void **)&ufs_mtk_mmio_base_infracfg_ao);
|
|
dev_err(hba->dev, "error: ufs_mtk_mmio_base_infracfg_ao init fail\n");
|
|
ufs_mtk_mmio_base_infracfg_ao = NULL;
|
|
}
|
|
} else
|
|
dev_err(hba->dev, "error: ufs_mtk_mmio_base_infracfg_ao init fail\n");
|
|
|
|
/* get va09 regulator and enable it */
|
|
reg_va09 = regulator_get(hba->dev, "va09");
|
|
if (!reg_va09) {
|
|
dev_info(hba->dev, "%s: get va09 fail!!!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
err = regulator_enable(reg_va09);
|
|
if (err < 0) {
|
|
dev_info(hba->dev, "%s: enalbe va09 fail, err = %d\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int ufs_mtk_pltfrm_resume(struct ufs_hba *hba)
|
|
{
|
|
int ret = 0;
|
|
u32 reg;
|
|
|
|
#ifdef CLKBUF_READY
|
|
/* Enable MPHY 26MHz ref clock */
|
|
clk_buf_ctrl(CLK_BUF_UFS, true);
|
|
ufshcd_writel(hba, 0, REG_UFS_ADDR_XOUFS_ST);
|
|
#endif
|
|
/* Set regulator to turn on VA09 LDO */
|
|
ret = regulator_enable(reg_va09);
|
|
if (ret < 0) {
|
|
dev_info(hba->dev,
|
|
"%s: enalbe va09 fail, err = %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
/* wait 200 us to stablize VA09 */
|
|
udelay(200);
|
|
|
|
/* Step 1: Set RG_VA09_ON to 1 */
|
|
mt_secure_call(MTK_SIP_KERNEL_UFS_CTL, 1, 1, 0, 0);
|
|
|
|
/* Step 2: release DA_MP_PLL_PWR_ON */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = reg | (0x1 << 11);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = reg & ~(0x1 << 10);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
|
|
/* Step 3: release DA_MP_PLL_ISO_EN */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = reg & ~(0x1 << 9);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = reg & ~(0x1 << 8);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
|
|
/* Step 4: release DA_MP_CDR_PWR_ON */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = reg | (0x1 << 18);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = reg & ~(0x1 << 17);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
|
|
/* Step 5: release DA_MP_CDR_ISO_EN */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = reg & ~(0x1 << 20);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = reg & ~(0x1 << 19);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
|
|
/* Step 6: release DA_MP_RX0_SQ_EN */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xA0AC);
|
|
reg = reg | (0x1 << 1);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xA0AC);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xA0AC);
|
|
reg = reg & ~(0x1);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xA0AC);
|
|
|
|
/* Delay 1us to wait DIFZ stable */
|
|
udelay(1);
|
|
|
|
/* Step 7: release DIFZ */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xA09C);
|
|
reg = reg & ~(0x1 << 18);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xA09C);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ufs_mtk_pltfrm_suspend(struct ufs_hba *hba)
|
|
{
|
|
int ret = 0;
|
|
u32 reg;
|
|
|
|
/* Step 1: force DIFZ */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xA09C);
|
|
reg = reg | (0x1 << 18);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xA09C);
|
|
|
|
/* Step 2: force DA_MP_RX0_SQ_EN */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xA0AC);
|
|
reg = reg | (0x1);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xA0AC);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xA0AC);
|
|
reg = reg & ~(0x1 << 1);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xA0AC);
|
|
|
|
/* Step 3: force DA_MP_CDR_ISO_EN */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = reg | (0x1 << 19);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = reg | (0x1 << 20);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
|
|
/* Step 4: force DA_MP_CDR_PWR_ON */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = reg | (0x1 << 17);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
reg = reg & ~(0x1 << 18);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0xB044);
|
|
|
|
/* Step 5: force DA_MP_PLL_ISO_EN */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = reg | (0x1 << 8);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = reg | (0x1 << 9);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
|
|
/* Step 6: force DA_MP_PLL_PWR_ON */
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = reg | (0x1 << 10);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = readl(ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
reg = reg & ~(0x1 << 11);
|
|
writel(reg, ufs_mtk_mmio_base_ufs_mphy + 0x008C);
|
|
|
|
/* Step 7: Set RG_VA09_ON to 0 */
|
|
mt_secure_call(MTK_SIP_KERNEL_UFS_CTL, 1, 0, 0, 0);
|
|
|
|
/* delay awhile to satisfy T_HIBERNATE */
|
|
mdelay(15);
|
|
|
|
#ifdef CLKBUF_READY
|
|
/* Disable MPHY 26MHz ref clock in H8 mode */
|
|
clk_buf_ctrl(CLK_BUF_UFS, false);
|
|
ufshcd_writel(hba, 0, REG_UFS_ADDR_XOUFS_ST);
|
|
#endif
|
|
if (ufs_mtk_hba->curr_dev_pwr_mode != UFS_ACTIVE_PWR_MODE)
|
|
spm_resource_req(SPM_RESOURCE_USER_UFS, SPM_RESOURCE_RELEASE);
|
|
|
|
/* Set regulator to turn off VA09 LDO */
|
|
ret = regulator_disable(reg_va09);
|
|
if (ret < 0) {
|
|
dev_info(hba->dev,
|
|
"%s: disalbe va09 fail, err = %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
#if 0
|
|
/* TEST ONLY: emulate UFSHCI power off by HCI SW reset */
|
|
ufs_mtk_pltfrm_host_sw_rst(hba, SW_RST_TARGET_UFSHCI);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|