kernel_samsung_a34x-permissive/drivers/mmc/host/mediatek/ComboA/msdc_tune.c
2024-04-28 15:49:01 +02:00

495 lines
14 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) "["KBUILD_MODNAME"]" fmt
#include <linux/gpio.h>
#include <linux/delay.h>
#include "mtk_sd.h"
#include <mmc/core/core.h>
#include <mmc/core/card.h>
#include "dbg.h"
#include "autok.h"
#include "autok_dvfs.h"
void msdc_sdio_restore_after_resume(struct msdc_host *host)
{
}
void msdc_save_timing_setting(struct msdc_host *host)
{
struct msdc_hw *hw = host->hw;
void __iomem *base = host->base, *base_top;
int i;
MSDC_GET_FIELD(MSDC_IOCON, MSDC_IOCON_RSPL, hw->cmd_edge);
MSDC_GET_FIELD(MSDC_IOCON, MSDC_IOCON_R_D_SMPL, hw->rdata_edge);
MSDC_GET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D_SMPL, hw->wdata_edge);
// this is for suspend only
host->saved_para.sdc_cfg = MSDC_READ32(SDC_CFG);
host->saved_para.iocon = MSDC_READ32(MSDC_IOCON);
host->saved_para.emmc50_cfg0 = MSDC_READ32(EMMC50_CFG0);
host->saved_para.pb0 = MSDC_READ32(MSDC_PATCH_BIT0);
host->saved_para.pb1 = MSDC_READ32(MSDC_PATCH_BIT1);
host->saved_para.pb2 = MSDC_READ32(MSDC_PATCH_BIT2);
host->saved_para.sdc_fifo_cfg = MSDC_READ32(SDC_FIFO_CFG);
host->saved_para.sdc_adv_cfg0 = MSDC_READ32(SDC_ADV_CFG0);
if (host->base_top) {
base_top = host->base_top;
host->saved_para.emmc_top_control
= MSDC_READ32(EMMC_TOP_CONTROL);
host->saved_para.emmc_top_cmd
= MSDC_READ32(EMMC_TOP_CMD);
host->saved_para.top_emmc50_pad_ctl0
= MSDC_READ32(TOP_EMMC50_PAD_CTL0);
host->saved_para.top_emmc50_pad_ds_tune
= MSDC_READ32(TOP_EMMC50_PAD_DS_TUNE);
for (i = 0; i < 8; i++) {
host->saved_para.top_emmc50_pad_dat_tune[i]
= MSDC_READ32(TOP_EMMC50_PAD_DAT0_TUNE + i * 4);
}
} else {
host->saved_para.pad_tune0 = MSDC_READ32(MSDC_PAD_TUNE0);
host->saved_para.pad_tune1 = MSDC_READ32(MSDC_PAD_TUNE1);
}
}
void msdc_set_bad_card_and_remove(struct msdc_host *host)
{
unsigned long flags;
if (host == NULL) {
pr_info("WARN: host is NULL");
return;
}
if (host->card_inserted) {
host->block_bad_card = 1;
host->card_inserted = 0;
}
if ((host->mmc == NULL) || (host->mmc->card == NULL)) {
ERR_MSG("WARN: mmc or card is NULL");
return;
}
if (host->mmc->card) {
spin_lock_irqsave(&host->remove_bad_card, flags);
mmc_card_set_removed(host->mmc->card);
spin_unlock_irqrestore(&host->remove_bad_card, flags);
#ifndef CONFIG_GPIOLIB
ERR_MSG("Cannot get gpio %d level", cd_gpio);
#else
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE)) {
ERR_MSG("Schedule mmc_rescan");
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
} else
#endif
{
/*
* prevent from calling device_del with mmcqd/X,
* it will cause dead lock
*/
ERR_MSG("Schedule msdc_remove_card");
schedule_delayed_work(&host->remove_card,
msecs_to_jiffies(200));
}
if (host->block_bad_card)
ERR_MSG(
"Remove the bad card, block_bad_card=%d, card_inserted=%d",
host->block_bad_card, host->card_inserted);
}
}
void msdc_remove_card(struct work_struct *work)
{
struct msdc_host *host =
container_of(work, struct msdc_host, remove_card.work);
if (!host->mmc || !host->mmc->card) {
ERR_MSG("WARN: mmc or card is NULL");
return;
}
ERR_MSG("Remove card");
mmc_claim_host(host->mmc);
mmc_remove_card(host->mmc->card);
host->mmc->card = NULL;
mmc_detach_bus(host->mmc);
mmc_power_off(host->mmc);
mmc_release_host(host->mmc);
}
int msdc_data_timeout_cont_chk(struct msdc_host *host)
{
if ((host->hw->host_function == MSDC_SD) &&
(host->data_timeout_cont >= MSDC_MAX_DATA_TIMEOUT_CONTINUOUS)) {
ERR_MSG("force remove bad card, data timeout continuous %d",
host->data_timeout_cont);
msdc_set_bad_card_and_remove(host);
return 1;
}
return 0;
}
int emmc_reinit_tuning(struct mmc_host *mmc)
{
struct msdc_host *host = mmc_priv(mmc);
void __iomem *base = host->base;
u32 div = 0;
u32 mode = 0;
unsigned int caps_hw_reset = 0;
if (!mmc->card) {
pr_notice("mmc card = NULL, skip reset tuning\n");
return -1;
}
/* Switch to DDR/HS mode */
if (mmc->card->mmc_avail_type
& (EXT_CSD_CARD_TYPE_HS200 | EXT_CSD_CARD_TYPE_HS400)) {
mmc->card->mmc_avail_type &=
~(EXT_CSD_CARD_TYPE_HS200|EXT_CSD_CARD_TYPE_HS400);
pr_notice("msdc%d: switch to DDR/HS mode, reinit card\n",
host->id);
if (mmc->caps & MMC_CAP_HW_RESET) {
caps_hw_reset = 1;
} else {
caps_hw_reset = 0;
mmc->caps |= MMC_CAP_HW_RESET;
}
mmc->ios.timing = MMC_TIMING_LEGACY;
mmc->ios.clock = 260000;
msdc_ops_set_ios(mmc, &mmc->ios);
host->hs400_mode = false;
if (mmc_hw_reset(mmc))
pr_notice("msdc%d fail to switch to DDR/HS mode\n",
host->id);
/* restore MMC_CAP_HW_RESET */
if (!caps_hw_reset)
mmc->caps &= ~MMC_CAP_HW_RESET;
goto done;
}
/* Reduce to lower frequency */
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKDIV, div);
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKMOD, mode);
div += 1;
if (div > EMMC_MAX_FREQ_DIV) {
pr_notice("msdc%d: max lower freq: %d\n", host->id, div);
return 1;
}
msdc_clk_stable(host, mode, div, 0);
host->sclk = (div == 0) ? host->hclk / 4 : host->hclk / (4 * div);
pr_notice("msdc%d: reduce frequence to %dMhz\n",
host->id, host->sclk / 1000000);
done:
return 0;
}
int sdcard_hw_reset(struct mmc_host *mmc)
{
struct msdc_host *host = mmc_priv(mmc);
int ret = 0;
int level = 1;
#ifdef CONFIG_GPIOLIB
level = __gpio_get_value(cd_gpio);
#endif
host->card_inserted = (host->hw->cd_level == level) ? 1 : 0;
if (!(host->card_inserted)) {
pr_notice("card is not inserted!\n");
msdc_set_bad_card_and_remove(host);
ret = -1;
return ret;
}
/* power reset sdcard */
mmc->ios.timing = MMC_TIMING_LEGACY;
/* do not set same as HOST_MIN_MCLK
* or else it will be set as block_bad_card when power off
*/
mmc->ios.clock = 300000;
msdc_ops_set_ios(mmc, &mmc->ios);
ret = mmc_hw_reset(mmc);
if (ret) {
if (++host->power_cycle_cnt
> MSDC_MAX_POWER_CYCLE_FAIL_CONTINUOUS)
msdc_set_bad_card_and_remove(host);
pr_notice(
"msdc%d power reset (%d) failed, block_bad_card = %d\n",
host->id, host->power_cycle_cnt, host->block_bad_card);
} else {
host->power_cycle_cnt = 0;
pr_notice("msdc%d power reset success\n", host->id);
}
return ret;
}
/* SDcard will change speed mode and power reset
* UHS card
* UHS_SDR104 --> UHS_DDR50 --> UHS_SDR50 --> UHS_SDR25
* HS card
* 50MHz --> 25MHz --> 12.5MHz --> 6.25MHz
*/
int sdcard_reset_tuning(struct mmc_host *mmc)
{
struct msdc_host *host = mmc_priv(mmc);
char *remove_cap;
int ret = 0;
if (!mmc->card) {
pr_notice("mmc card = NULL, skip reset tuning\n");
return -1;
}
if (mmc_card_uhs(mmc->card)) {
if (mmc->card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104) {
mmc->card->sw_caps.sd3_bus_mode &= ~SD_MODE_UHS_SDR104;
remove_cap = "UHS_SDR104";
} else if (mmc->card->sw_caps.sd3_bus_mode
& SD_MODE_UHS_DDR50) {
mmc->card->sw_caps.sd3_bus_mode &= ~SD_MODE_UHS_DDR50;
remove_cap = "UHS_DDR50";
} else if (mmc->card->sw_caps.sd3_bus_mode
& SD_MODE_UHS_SDR50) {
mmc->card->sw_caps.sd3_bus_mode &= ~SD_MODE_UHS_SDR50;
remove_cap = "UHS_SDR50";
} else if (mmc->card->sw_caps.sd3_bus_mode
& SD_MODE_UHS_SDR25) {
mmc->card->sw_caps.sd3_bus_mode &= ~SD_MODE_UHS_SDR25;
remove_cap = "UHS_SDR25";
} else {
remove_cap = "none";
}
pr_notice("msdc%d: remove %s mode then reinit card\n", host->id,
remove_cap);
} else if (mmc_card_hs(mmc->card)) {
if (mmc->card->sw_caps.hs_max_dtr >= HIGH_SPEED_MAX_DTR / 4)
mmc->card->sw_caps.hs_max_dtr /= 2;
pr_notice("msdc%d: set hs speed %dhz then reinit card\n",
host->id, mmc->card->sw_caps.hs_max_dtr);
} else {
pr_notice("msdc%d: ds card just reinit card\n", host->id);
}
/* force remove card for continuous data timeout */
ret = msdc_data_timeout_cont_chk(host);
if (ret) {
ret = -1;
goto done;
}
/* power cycle sdcard */
ret = sdcard_hw_reset(mmc);
if (ret) {
ret = -1;
goto done;
}
done:
return ret;
}
void msdc_restore_timing_setting(struct msdc_host *host)
{
void __iomem *base = host->base, *base_top = host->base_top;
int emmc = (host->hw->host_function == MSDC_EMMC) ? 1 : 0;
int i;
autok_path_sel(host);
MSDC_WRITE32(SDC_CFG, host->saved_para.sdc_cfg);
MSDC_WRITE32(MSDC_IOCON, host->saved_para.iocon);
if (!host->base_top) {
MSDC_WRITE32(MSDC_PAD_TUNE0, host->saved_para.pad_tune0);
MSDC_WRITE32(MSDC_PAD_TUNE1, host->saved_para.pad_tune1);
}
MSDC_WRITE32(MSDC_PATCH_BIT0, host->saved_para.pb0);
MSDC_WRITE32(MSDC_PATCH_BIT1, host->saved_para.pb1);
MSDC_WRITE32(MSDC_PATCH_BIT2, host->saved_para.pb2);
MSDC_WRITE32(SDC_FIFO_CFG, host->saved_para.sdc_fifo_cfg);
MSDC_WRITE32(SDC_ADV_CFG0, host->saved_para.sdc_adv_cfg0);
if (emmc && !host->base_top) {
/* FIX ME: sdio shall add extra check for sdio3.0+ */
MSDC_SET_FIELD(EMMC50_PAD_DS_TUNE, MSDC_EMMC50_PAD_DS_TUNE_DLY1,
host->saved_para.ds_dly1);
MSDC_SET_FIELD(EMMC50_PAD_DS_TUNE, MSDC_EMMC50_PAD_DS_TUNE_DLY3,
host->saved_para.ds_dly3);
MSDC_WRITE32(EMMC50_PAD_CMD_TUNE,
host->saved_para.emmc50_pad_cmd_tune);
MSDC_WRITE32(EMMC50_PAD_DAT01_TUNE,
host->saved_para.emmc50_dat01);
MSDC_WRITE32(EMMC50_PAD_DAT23_TUNE,
host->saved_para.emmc50_dat23);
MSDC_WRITE32(EMMC50_PAD_DAT45_TUNE,
host->saved_para.emmc50_dat45);
MSDC_WRITE32(EMMC50_PAD_DAT67_TUNE,
host->saved_para.emmc50_dat67);
}
if (emmc)
MSDC_WRITE32(EMMC50_CFG0, host->saved_para.emmc50_cfg0);
if (host->base_top) {
MSDC_WRITE32(EMMC_TOP_CONTROL,
host->saved_para.emmc_top_control);
MSDC_WRITE32(EMMC_TOP_CMD,
host->saved_para.emmc_top_cmd);
MSDC_WRITE32(TOP_EMMC50_PAD_CTL0,
host->saved_para.top_emmc50_pad_ctl0);
MSDC_WRITE32(TOP_EMMC50_PAD_DS_TUNE,
host->saved_para.top_emmc50_pad_ds_tune);
for (i = 0; i < 8; i++) {
MSDC_WRITE32(TOP_EMMC50_PAD_DAT0_TUNE + i * 4,
host->saved_para.top_emmc50_pad_dat_tune[i]);
}
}
if (host->use_hw_dvfs == 1)
msdc_dvfs_reg_restore(host);
}
void msdc_init_tune_path(struct msdc_host *host, unsigned char timing)
{
void __iomem *base = host->base, *base_top = host->base_top;
MSDC_WRITE32(MSDC_PAD_TUNE0, 0x00000000);
if (host->base_top) {
/* FIX ME: toggle these fields accroding to timing */
/* FIX ME: maybe unnecessary if autok can take care */
MSDC_CLR_BIT32(EMMC_TOP_CONTROL, DATA_K_VALUE_SEL);
MSDC_CLR_BIT32(EMMC_TOP_CONTROL, DELAY_EN);
MSDC_CLR_BIT32(EMMC_TOP_CONTROL, PAD_DAT_RD_RXDLY);
MSDC_CLR_BIT32(EMMC_TOP_CONTROL, PAD_DAT_RD_RXDLY_SEL);
MSDC_CLR_BIT32(EMMC_TOP_CONTROL, PAD_RXDLY_SEL);
MSDC_CLR_BIT32(EMMC_TOP_CMD, PAD_CMD_RXDLY);
MSDC_CLR_BIT32(EMMC_TOP_CMD, PAD_CMD_RD_RXDLY_SEL);
MSDC_CLR_BIT32(TOP_EMMC50_PAD_CTL0, PAD_CLK_TXDLY);
}
MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_DDLSEL);
MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_R_D_SMPL_SEL);
MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_R_D_SMPL);
if (timing == MMC_TIMING_MMC_HS400) {
MSDC_CLR_BIT32(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_DATRRDLYSEL);
MSDC_CLR_BIT32(MSDC_PAD_TUNE1, MSDC_PAD_TUNE1_DATRRDLY2SEL);
if (host->base_top) {
/* FIX ME: maybe unnecessary if autok can take care */
MSDC_CLR_BIT32(EMMC_TOP_CONTROL, PAD_DAT_RD_RXDLY_SEL);
MSDC_CLR_BIT32(EMMC_TOP_CONTROL, PAD_DAT_RD_RXDLY2_SEL);
}
} else {
MSDC_SET_BIT32(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_DATRRDLYSEL);
MSDC_CLR_BIT32(MSDC_PAD_TUNE1, MSDC_PAD_TUNE1_DATRRDLY2SEL);
if (host->base_top) {
/* FIX ME: maybe unnecessary if autok can take care */
MSDC_SET_BIT32(EMMC_TOP_CONTROL, PAD_DAT_RD_RXDLY_SEL);
MSDC_CLR_BIT32(EMMC_TOP_CONTROL, PAD_DAT_RD_RXDLY2_SEL);
}
}
if (timing == MMC_TIMING_MMC_HS400)
MSDC_CLR_BIT32(MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTS);
else
MSDC_SET_BIT32(MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTS);
MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_W_D_SMPL_SEL);
MSDC_CLR_BIT32(MSDC_PATCH_BIT2, MSDC_PB2_CFGRESP);
MSDC_SET_BIT32(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_CMDRRDLYSEL);
MSDC_CLR_BIT32(MSDC_PAD_TUNE1, MSDC_PAD_TUNE1_CMDRRDLY2SEL);
if ((timing == MMC_TIMING_MMC_HS400) && (host->base_top)) {
/* FIX ME: maybe unnecessary if autok can take care */
MSDC_SET_BIT32(EMMC_TOP_CMD, PAD_CMD_RD_RXDLY_SEL);
MSDC_CLR_BIT32(EMMC_TOP_CMD, PAD_CMD_RD_RXDLY2_SEL);
}
MSDC_CLR_BIT32(EMMC50_CFG0, MSDC_EMMC50_CFG_CMD_RESP_SEL);
autok_path_sel(host);
}
void msdc_init_tune_setting(struct msdc_host *host)
{
void __iomem *base = host->base, *base_top = host->base_top;
u32 val;
/* FIX ME: check if always convered by autok */
MSDC_SET_FIELD(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_CLKTXDLY,
MSDC_CLKTXDLY);
if (host->base_top) {
MSDC_SET_FIELD(TOP_EMMC50_PAD_CTL0, PAD_CLK_TXDLY,
MSDC_CLKTXDLY);
MSDC_SET_FIELD(EMMC_TOP_CONTROL,
(PAD_DAT_RD_RXDLY2 | PAD_DAT_RD_RXDLY), 0);
MSDC_SET_FIELD(EMMC_TOP_CMD,
(PAD_CMD_RXDLY2 | PAD_CMD_RXDLY), 0);
MSDC_SET_FIELD(TOP_EMMC50_PAD_DS_TUNE,
(PAD_DS_DLY3 | PAD_DS_DLY2 | PAD_DS_DLY1), 0);
}
/* Reserve MSDC_IOCON_DDR50CKD bit, clear all other bits */
val = MSDC_READ32(MSDC_IOCON) & MSDC_IOCON_DDR50CKD;
MSDC_WRITE32(MSDC_IOCON, val);
MSDC_WRITE32(MSDC_DAT_RDDLY0, 0x00000000);
MSDC_WRITE32(MSDC_DAT_RDDLY1, 0x00000000);
MSDC_WRITE32(MSDC_PATCH_BIT0, MSDC_PB0_DEFAULT_VAL);
MSDC_WRITE32(MSDC_PATCH_BIT1, MSDC_PB1_DEFAULT_VAL);
/* Fix HS400 mode */
MSDC_CLR_BIT32(EMMC50_CFG0, MSDC_EMMC50_CFG_TXSKEW_SEL);
MSDC_SET_BIT32(MSDC_PATCH_BIT1, MSDC_PB1_DDR_CMD_FIX_SEL);
/* DDR50 mode */
MSDC_SET_BIT32(MSDC_PATCH_BIT2, MSDC_PB2_DDR50SEL);
/* 64T + 48T cmd <-> resp */
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPWAITCNT,
MSDC_PB2_DEFAULT_RESPWAITCNT);
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL,
MSDC_PB2_DEFAULT_RESPSTENSEL);
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL,
MSDC_PB2_DEFAULT_CRCSTSENSEL);
/* FIX ME: check if can be moved to msdc_cust.c */
#ifdef CONFIG_MACH_MT6799
if (CHIP_IS_VER2())
SET_EMMC50_CFG_END_BIT_CHK_CNT(EMMC50_CFG_END_BIT_CHK_CNT);
#endif
autok_path_sel(host);
}
void msdc_ios_tune_setting(struct msdc_host *host, struct mmc_ios *ios)
{
autok_msdc_tx_setting(host, ios);
}