diff -drupN a/drivers/mmc/host/sunxi-mmc-v5px.c b/drivers/mmc/host/sunxi-mmc-v5px.c --- a/drivers/mmc/host/sunxi-mmc-v5px.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/mmc/host/sunxi-mmc-v5px.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,608 @@ +/* +* Sunxi SD/MMC host driver +* +* Copyright (C) 2015 AllWinnertech Ltd. +* Author: lixiang +* +* 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 "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + + +#ifdef CONFIG_ARCH_SUN8IW10P1 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sunxi-smhc.h" +#include "sunxi-mmc-v5px.h" + +#define SMHC_DS_DLY (0x230) +#define SMHC_THLD (0x20c) + +#define SMHC_SAMP_DL_SW_MASK (0x0000003F) +#define SMHC_DS_DL_SW_MASK (0x0000003F) +#define SMHC_DS_DL_SW_EN (1u<<7) + +#define SMHC_CARD_RD_TH_SZ 0x000007FF +#define SMHC_CARD_RD_TH_MASK 0x000007FF +#define SMHC_CARD_RD_TH_SHIFT 0x0 + +#define SMHC_CARD_WR_TH_SZ 0x000007FF +#define SMHC_CARD_WR_TH_MASK (0x000007FF<<16) +#define SMHC_CARD_WR_TH_SHIFT 16 + +#define SMHC_DES_NUM_SHIFT_V5PX (15) +#define SMHC_DES_BUFFER_MAX_LEN_V5PX (1U << SMHC_DES_NUM_SHIFT) + +enum sunxi_mmc_speed_mode { + SM0_DS26_SDR12 = 0, + SM1_HSSDR52_SDR25, + SM2_HSDDR52_DDR50, + SM3_HS200_SDR104, + SM4_HS400, + SM_NUM, +}; + +struct sunxi_mmc_clk_dly { + enum sunxi_mmc_speed_mode spm; + char *mod_str; + char *raw_tm_sm_str[2]; + u32 raw_tm_sm[2]; + u32 raw_tm_sm_def[2]; +}; + +struct sunxi_mmc_ver_priv { + /*struct sunxi_mmc_spec_regs bak_spec_regs; */ + struct sunxi_mmc_clk_dly mmc_clk_dly[SM_NUM]; +}; + +static void sunxi_mmc_set_clk_dly(struct sunxi_mmc_host *host, int clk, + int bus_width, int timing) +{ + struct mmc_host *mmc = host->mmc; + enum sunxi_mmc_speed_mode speed_mod = SM0_DS26_SDR12; + char *raw_sm_str = NULL; + char *m_str = NULL; + struct device_node *np = NULL; + u32 *raw_sm = 0; + u32 *raw_sm_def = 0; + u32 rval = 0; + int frq_index = 0; + u32 cmd_drv_ph = 1; + u32 dat_drv_ph = 0; + u32 sam_dly = 0; + u32 ds_dly = 0; + struct sunxi_mmc_clk_dly *mmc_clk_dly = + ((struct sunxi_mmc_ver_priv *)host->version_priv_dat)->mmc_clk_dly; + + if (!mmc->parent || !mmc->parent->of_node) { + dev_err(mmc_dev(host->mmc), + "no dts to parse clk dly,use default\n"); + return; + } + + np = mmc->parent->of_node; + + switch (timing) { + case MMC_TIMING_LEGACY: + case MMC_TIMING_UHS_SDR12: + speed_mod = SM0_DS26_SDR12; + break; + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + case MMC_TIMING_UHS_SDR25: + speed_mod = SM1_HSSDR52_SDR25; + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + speed_mod = SM2_HSDDR52_DDR50; + break; + case MMC_TIMING_UHS_SDR50: + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + speed_mod = SM3_HS200_SDR104; + break; + case MMC_TIMING_MMC_HS400: + speed_mod = SM4_HS400; + break; + default: + dev_err(mmc_dev(mmc), "Wrong timing input\n"); + return; + } + + if (clk <= 400 * 1000) { + frq_index = 0; + } else if (clk <= 25 * 1000 * 1000) { + frq_index = 1; + } else if (clk <= 50 * 1000 * 1000) { + frq_index = 2; + } else if (clk <= 100 * 1000 * 1000) { + frq_index = 3; + } else if (clk <= 150 * 1000 * 1000) { + frq_index = 4; + } else if (clk <= 200 * 1000 * 1000) { + frq_index = 5; + } else if (clk <= 250 * 1000 * 1000) { + frq_index = 6; + } else if (clk <= 300 * 1000 * 1000) { + frq_index = 7; + } else { + dev_err(mmc_dev(mmc), "clk is over 300mhz\n"); + return; + } + + if (frq_index / 4 > 2) { + dev_err(mmc_dev(host->mmc), "err frq_index\n"); + return; + } + + dev_dbg(mmc_dev(host->mmc), "freq %d frq index %d,frq/4 %x\n", clk, + frq_index, frq_index / 4); + raw_sm_str = mmc_clk_dly[speed_mod].raw_tm_sm_str[frq_index / 4]; + raw_sm = &mmc_clk_dly[speed_mod].raw_tm_sm[frq_index / 4]; + raw_sm_def = &mmc_clk_dly[speed_mod].raw_tm_sm_def[frq_index / 4]; + m_str = mmc_clk_dly[speed_mod].mod_str; + + rval = of_property_read_u32(np, raw_sm_str, raw_sm); + if (rval) { + dev_info(mmc_dev(host->mmc), "failed to get %s used default\n", + m_str); + } else { + u32 sm_shift = (frq_index % 4) * 8; + + rval = ((*raw_sm) >> sm_shift) & 0xff; + if (rval != 0xff) { + if (timing == MMC_TIMING_MMC_HS400) { + u32 raw_sm_hs200 = 0; + + ds_dly = rval; + raw_sm_hs200 = + mmc_clk_dly[SM3_HS200_SDR104]. + raw_tm_sm[frq_index / 4]; + sam_dly = ((raw_sm_hs200) >> sm_shift) & 0xff; + } else { + sam_dly = rval; + } + dev_dbg(mmc_dev(host->mmc), + "Get speed mode %s clk dly %s ok\n", m_str, + raw_sm_str); + } else { + u32 sm_shift = (frq_index % 4) * 8; + + dev_dbg(mmc_dev(host->mmc), "%s use default value\n", + m_str); + rval = ((*raw_sm_def) >> sm_shift) & 0xff; + if (timing == MMC_TIMING_MMC_HS400) { + u32 raw_sm_hs200 = 0; + + ds_dly = rval; + raw_sm_hs200 = + mmc_clk_dly[SM3_HS200_SDR104]. + raw_tm_sm_def[frq_index / 4]; + sam_dly = ((raw_sm_hs200) >> sm_shift) & 0xff; + } else { + sam_dly = rval; + } + } + + } + + dev_dbg(mmc_dev(host->mmc), "Try set %s clk dly ok\n", m_str); + dev_dbg(mmc_dev(host->mmc), "cmd_drv_ph %d\n", cmd_drv_ph); + dev_dbg(mmc_dev(host->mmc), "dat_drv_ph %d\n", dat_drv_ph); + dev_dbg(mmc_dev(host->mmc), "sam_dly %d\n", sam_dly); + dev_dbg(mmc_dev(host->mmc), "ds_dly %d\n", ds_dly); + + rval = smhc_readl(host, SMHC_DS_DLY); + rval &= ~SMHC_DS_DL_SW_MASK; + rval |= ds_dly & SMHC_DS_DL_SW_MASK; + rval |= SMHC_DS_DL_SW_EN; + smhc_writel(host, SMHC_DS_DLY, rval); + dev_dbg(mmc_dev(host->mmc), " SMHC_DS_DLY %08x\n", + smhc_readl(host, SMHC_DS_DLY)); +} + +void sunxi_mmc_dump_dly2(struct sunxi_mmc_host *host) +{ + dev_dbg(mmc_dev(host->mmc), "no imple %s %d\n", __func__, __LINE__); +} + +static int __sunxi_mmc_do_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en, + u32 pwr_save, u32 ignore_dat0) +{ + u32 tmp = 0; + + tmp = smhc_readl(host, SMHC_RST_CLK_CTRL); + if (oclk_en) + tmp |= SdclkEn; + else + tmp &= ~SdclkEn; + + smhc_writel(host, SMHC_RST_CLK_CTRL, tmp); + + tmp = smhc_readl(host, SMHC_CTRL3); + if (pwr_save) + tmp |= SdclkIdleCtrl; + else + tmp &= ~SdclkIdleCtrl; + + smhc_writel(host, SMHC_CTRL3, tmp); + + return 0; + +} + +int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) +{ + struct device_node *np = NULL; + struct mmc_host *mmc = host->mmc; + int pwr_save = 0; + int len = 0; + + if (!mmc->parent || !mmc->parent->of_node) { + dev_err(mmc_dev(host->mmc), + "no dts to parse power save mode\n"); + return -EIO; + } + + np = mmc->parent->of_node; + if (of_find_property(np, "sunxi-power-save-mode", &len)) + pwr_save = 1; + return __sunxi_mmc_do_oclk_onoff(host, oclk_en, pwr_save, 1); +} +int sunxi_mmc_clk_set_rate_v5px(struct sunxi_mmc_host *host, + struct mmc_ios *ios) +{ + u32 mod_clk = 0; + u32 src_clk = 0; + u32 rval = 0; + s32 err = 0; + u32 rate = 0; + char *sclk_name = NULL; + struct clk *mclk = host->clk_mmc; + struct clk *sclk = NULL; + struct device *dev = mmc_dev(host->mmc); + + if (ios->clock == 0) { + __sunxi_mmc_do_oclk_onoff(host, 0, 0, 1); + return 0; + } + + if (sunxi_mmc_ddr_timing(ios->timing)) + mod_clk = ios->clock << 3; + else + mod_clk = ios->clock << 2; + + if (ios->clock <= 400000) { + sclk = clk_get(dev, "osc24m"); + sclk_name = "osc24m"; + } else { + sclk = clk_get(dev, "pll_periph"); + sclk_name = "pll_periph"; + } + if (IS_ERR(sclk)) { + dev_err(mmc_dev(host->mmc), "Error to get source clock %s\n", + sclk_name); + return -1; + } + + sunxi_mmc_oclk_onoff(host, 0); + + err = clk_set_parent(mclk, sclk); + if (err) { + dev_err(mmc_dev(host->mmc), "set parent failed\n"); + clk_put(sclk); + return -1; + } + + rate = clk_round_rate(mclk, mod_clk); + + dev_dbg(mmc_dev(host->mmc), "get round rate %d\n", rate); + + clk_disable_unprepare(host->clk_mmc); + + err = clk_set_rate(mclk, rate); + if (err) { + dev_err(mmc_dev(host->mmc), "set mclk rate error, rate %dHz\n", + rate); + clk_put(sclk); + return -1; + } + + rval = clk_prepare_enable(host->clk_mmc); + if (rval) { + dev_err(mmc_dev(host->mmc), "Enable mmc clk err %d\n", rval); + return -1; + } + + src_clk = clk_get_rate(sclk); + clk_put(sclk); + + dev_dbg(mmc_dev(host->mmc), "set round clock %d, soure clk is %d\n", + rate, src_clk); + + /*sunxi_of_parse_clk_dly(host); */ + if (sunxi_mmc_ddr_timing(ios->timing)) + ios->clock = rate >> 3; + else + ios->clock = rate >> 2; + + + sunxi_mmc_set_clk_dly(host, ios->clock, ios->bus_width, ios->timing); + + return sunxi_mmc_oclk_onoff(host, 1); +} + +void sunxi_mmc_thld_ctl_v5px(struct sunxi_mmc_host *host, + struct mmc_ios *ios, struct mmc_data *data) +{ + u32 bsz = data->blksz; + u32 rval = 0; + + if ((data->flags & MMC_DATA_WRITE) + && (bsz <= SMHC_CARD_WR_TH_SZ)) { + rval = smhc_readl(host, SMHC_THLD); + rval &= ~SMHC_CARD_WR_TH_MASK; + rval |= data->blksz << SMHC_CARD_WR_TH_SHIFT; + /*rval |= SDXC_CARD_WR_THLD_ENB; */ + smhc_writel(host, SMHC_THLD, rval); + } else { + /* + * rval = mmc_readl(host, REG_THLD); + * rval &= ~SDXC_CARD_WR_THLD_ENB; + * mmc_writel(host, REG_THLD, rval); + */ + } + + if ((data->flags & MMC_DATA_READ) + && (bsz <= SMHC_CARD_RD_TH_SZ) + && ((ios->timing == MMC_TIMING_MMC_HS200) + || (ios->timing == MMC_TIMING_MMC_HS400) + || (ios->timing == MMC_TIMING_UHS_SDR50) + || (ios->timing == MMC_TIMING_UHS_SDR104))) { + rval = smhc_readl(host, SMHC_THLD); + rval &= ~SMHC_CARD_RD_TH_MASK; + rval |= data->blksz << SMHC_CARD_RD_TH_SHIFT; + /*rval |= SDXC_CARD_RD_THLD_ENB; */ + smhc_writel(host, SMHC_THLD, rval); + } else { + /* + * rval = mmc_readl(host, REG_THLD); + * rval &= ~SDXC_CARD_RD_THLD_ENB; + * mmc_writel(host, REG_THLD, rval); + */ + } + + dev_dbg(mmc_dev(host->mmc), "SDXC_REG_THLD: 0x%08x\n", + smhc_readl(host, SMHC_THLD)); +} + +void sunxi_mmc_save_spec_reg_v5px(struct sunxi_mmc_host *host) +{ + dev_dbg(mmc_dev(host->mmc), "no imple %s %d\n", __func__, __LINE__); + +} + +void sunxi_mmc_restore_spec_reg_v5px(struct sunxi_mmc_host *host) +{ + dev_dbg(mmc_dev(host->mmc), "no imple %s %d\n", __func__, __LINE__); + +} + + + +static int sunxi_mmc_can_poweroff_notify(const struct mmc_card *card) +{ + return card && + mmc_card_mmc(card) && + (card->ext_csd.power_off_notification == EXT_CSD_POWER_ON); +} + +static int sunxi_mmc_poweroff_notify(struct mmc_card *card, + unsigned int notify_type) +{ + unsigned int timeout = card->ext_csd.generic_cmd6_time; + int err; + + /* Use EXT_CSD_POWER_OFF_SHORT as default notification type. */ + if (notify_type == EXT_CSD_POWER_OFF_LONG) + timeout = card->ext_csd.power_off_longtime; + + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_OFF_NOTIFICATION, + notify_type, timeout, true, false, false); + if (err) + pr_err("%s: Power Off Notification timed out, %u\n", + mmc_hostname(card->host), timeout); + + /* Disable the power off notification after the switch operation. */ + card->ext_csd.power_off_notification = EXT_CSD_NO_POWER_NOTIFICATION; + + return err; +} + +static int sunxi_mmc_sleep(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + int err = -1; + + if (card && card->ext_csd.rev >= 3) { + err = mmc_card_sleepawake(host, 1); + if (err < 0) + pr_debug("%s: Error %d while putting card into sleep", + mmc_hostname(host), err); + } + + return err; +} + +static int sunxi_mmc_suspend(struct mmc_host *host, bool is_suspend) +{ + int err = 0; + unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT : + EXT_CSD_POWER_OFF_LONG; + + if (!host) { + pr_err("Host should be null\n"); + return -1; + } + if (!host->card) { + pr_err("Card should be null\n"); + return -1; + } + + mmc_claim_host(host); + + /* + * if (mmc_card_suspended(host->card)) + * goto out; + */ + + if (mmc_card_doing_bkops(host->card)) { + err = mmc_stop_bkops(host->card); + if (err) + goto out; + } + + err = mmc_flush_cache(host->card); + + if (err) + goto out; + + if (sunxi_mmc_can_poweroff_notify(host->card) && + ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) || !is_suspend)) { + err = sunxi_mmc_poweroff_notify(host->card, notify_type); + } else if (mmc_card_can_sleep(host)) { + err = sunxi_mmc_sleep(host); + } else if (!mmc_host_is_spi(host)) { + err = mmc_deselect_cards(host); + } + + if (!err) { + pr_info("%s: %s %d\n", + mmc_hostname(host), __func__, __LINE__); + mmc_power_off(host); +/* mmc_card_set_suspended(host->card);*/ + } + +out: + mmc_release_host(host); + return err; +} + +void sunxi_mmc_do_shutdown_v5px(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + u32 shutdown_notify_type = 0; + u32 rval = + of_property_read_u32(mmc->parent->of_node, "shutdown_notify_type", + &shutdown_notify_type); + if (!rval) + sunxi_mmc_suspend(mmc, shutdown_notify_type); + else + sunxi_mmc_suspend(mmc, false); +} + +void sunxi_mmc_init_priv_v5px(struct sunxi_mmc_host *host, + struct platform_device *pdev, int phy_index) +{ + struct sunxi_mmc_ver_priv *ver_priv = + devm_kzalloc(&pdev->dev, sizeof(struct sunxi_mmc_ver_priv), + GFP_KERNEL); + host->version_priv_dat = ver_priv; + + ver_priv->mmc_clk_dly[SM0_DS26_SDR12].spm = SM0_DS26_SDR12; + ver_priv->mmc_clk_dly[SM0_DS26_SDR12].mod_str = "DS26_SDR12"; + ver_priv->mmc_clk_dly[SM0_DS26_SDR12].raw_tm_sm_str[0] = + "sdc_tm4_sm0_freq0"; + ver_priv->mmc_clk_dly[SM0_DS26_SDR12].raw_tm_sm_str[1] = + "sdc_tm4_sm0_freq1"; + ver_priv->mmc_clk_dly[SM0_DS26_SDR12].raw_tm_sm[0] = 0; + ver_priv->mmc_clk_dly[SM0_DS26_SDR12].raw_tm_sm[1] = 0; + ver_priv->mmc_clk_dly[SM0_DS26_SDR12].raw_tm_sm_def[0] = 0; + ver_priv->mmc_clk_dly[SM0_DS26_SDR12].raw_tm_sm_def[1] = 0; + + ver_priv->mmc_clk_dly[SM1_HSSDR52_SDR25].spm = SM1_HSSDR52_SDR25; + ver_priv->mmc_clk_dly[SM1_HSSDR52_SDR25].mod_str = "HSSDR52_SDR25"; + ver_priv->mmc_clk_dly[SM1_HSSDR52_SDR25].raw_tm_sm_str[0] = + "sdc_tm4_sm1_freq0"; + ver_priv->mmc_clk_dly[SM1_HSSDR52_SDR25].raw_tm_sm_str[1] = + "sdc_tm4_sm1_freq1"; + ver_priv->mmc_clk_dly[SM1_HSSDR52_SDR25].raw_tm_sm[0] = 0; + ver_priv->mmc_clk_dly[SM1_HSSDR52_SDR25].raw_tm_sm[1] = 0; + ver_priv->mmc_clk_dly[SM1_HSSDR52_SDR25].raw_tm_sm_def[0] = 0; + ver_priv->mmc_clk_dly[SM1_HSSDR52_SDR25].raw_tm_sm_def[1] = 0; + + ver_priv->mmc_clk_dly[SM2_HSDDR52_DDR50].spm = SM2_HSDDR52_DDR50; + ver_priv->mmc_clk_dly[SM2_HSDDR52_DDR50].mod_str = "HSDDR52_DDR50"; + ver_priv->mmc_clk_dly[SM2_HSDDR52_DDR50].raw_tm_sm_str[0] = + "sdc_tm4_sm2_freq0"; + ver_priv->mmc_clk_dly[SM2_HSDDR52_DDR50].raw_tm_sm_str[1] = + "sdc_tm4_sm2_freq1"; + ver_priv->mmc_clk_dly[SM2_HSDDR52_DDR50].raw_tm_sm[0] = 0; + ver_priv->mmc_clk_dly[SM2_HSDDR52_DDR50].raw_tm_sm[1] = 0; + ver_priv->mmc_clk_dly[SM2_HSDDR52_DDR50].raw_tm_sm_def[0] = 0; + ver_priv->mmc_clk_dly[SM2_HSDDR52_DDR50].raw_tm_sm_def[1] = 0; + + ver_priv->mmc_clk_dly[SM3_HS200_SDR104].spm = SM3_HS200_SDR104; + ver_priv->mmc_clk_dly[SM3_HS200_SDR104].mod_str = "HS200_SDR104"; + ver_priv->mmc_clk_dly[SM3_HS200_SDR104].raw_tm_sm_str[0] = + "sdc_tm4_sm3_freq0"; + ver_priv->mmc_clk_dly[SM3_HS200_SDR104].raw_tm_sm_str[1] = + "sdc_tm4_sm3_freq1"; + ver_priv->mmc_clk_dly[SM3_HS200_SDR104].raw_tm_sm[0] = 0; + ver_priv->mmc_clk_dly[SM3_HS200_SDR104].raw_tm_sm[1] = 0; + ver_priv->mmc_clk_dly[SM3_HS200_SDR104].raw_tm_sm_def[0] = 0; + ver_priv->mmc_clk_dly[SM3_HS200_SDR104].raw_tm_sm_def[1] = 0x00000405; + + ver_priv->mmc_clk_dly[SM4_HS400].spm = SM4_HS400; + ver_priv->mmc_clk_dly[SM4_HS400].mod_str = "HS400"; + ver_priv->mmc_clk_dly[SM4_HS400].raw_tm_sm_str[0] = "sdc_tm4_sm4_freq0"; + ver_priv->mmc_clk_dly[SM4_HS400].raw_tm_sm_str[1] = "sdc_tm4_sm4_freq1"; + ver_priv->mmc_clk_dly[SM4_HS400].raw_tm_sm[0] = 0; + ver_priv->mmc_clk_dly[SM4_HS400].raw_tm_sm[1] = 0x00000608; + ver_priv->mmc_clk_dly[SM4_HS400].raw_tm_sm_def[0] = 0; + ver_priv->mmc_clk_dly[SM4_HS400].raw_tm_sm_def[1] = 0x00000408; + + host->sunxi_mmc_clk_set_rate = sunxi_mmc_clk_set_rate_v5px; + /*host->idma_des_size_bits = 15; */ + host->idma_des_size_bits = SMHC_DES_NUM_SHIFT_V5PX; + host->sunxi_mmc_thld_ctl = sunxi_mmc_thld_ctl_v5px; + host->sunxi_mmc_save_spec_reg = sunxi_mmc_save_spec_reg_v5px; + host->sunxi_mmc_restore_spec_reg = sunxi_mmc_restore_spec_reg_v5px; + /*sunxi_mmc_reg_ex_res_inter(host, phy_index); + * host->sunxi_mmc_set_acmda = sunxi_mmc_set_a12a; + */ + host->sunxi_mmc_dump_dly_table = sunxi_mmc_dump_dly2; + host->phy_index = phy_index; +} + +#endif