162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for SDHCI on STMicroelectronics SoCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 STMicroelectronics Ltd 662306a36Sopenharmony_ci * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> 762306a36Sopenharmony_ci * Contributors: Peter Griffin <peter.griffin@linaro.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on sdhci-cns3xxx.c 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/mmc/host.h> 1762306a36Sopenharmony_ci#include <linux/reset.h> 1862306a36Sopenharmony_ci#include "sdhci-pltfm.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct st_mmc_platform_data { 2162306a36Sopenharmony_ci struct reset_control *rstc; 2262306a36Sopenharmony_ci struct clk *icnclk; 2362306a36Sopenharmony_ci void __iomem *top_ioaddr; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* MMCSS glue logic to setup the HC on some ST SoCs (e.g. STiH407 family) */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define ST_MMC_CCONFIG_REG_1 0x400 2962306a36Sopenharmony_ci#define ST_MMC_CCONFIG_TIMEOUT_CLK_UNIT BIT(24) 3062306a36Sopenharmony_ci#define ST_MMC_CCONFIG_TIMEOUT_CLK_FREQ BIT(12) 3162306a36Sopenharmony_ci#define ST_MMC_CCONFIG_TUNING_COUNT_DEFAULT BIT(8) 3262306a36Sopenharmony_ci#define ST_MMC_CCONFIG_ASYNC_WAKEUP BIT(0) 3362306a36Sopenharmony_ci#define ST_MMC_CCONFIG_1_DEFAULT \ 3462306a36Sopenharmony_ci ((ST_MMC_CCONFIG_TIMEOUT_CLK_UNIT) | \ 3562306a36Sopenharmony_ci (ST_MMC_CCONFIG_TIMEOUT_CLK_FREQ) | \ 3662306a36Sopenharmony_ci (ST_MMC_CCONFIG_TUNING_COUNT_DEFAULT)) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define ST_MMC_CCONFIG_REG_2 0x404 3962306a36Sopenharmony_ci#define ST_MMC_CCONFIG_HIGH_SPEED BIT(28) 4062306a36Sopenharmony_ci#define ST_MMC_CCONFIG_ADMA2 BIT(24) 4162306a36Sopenharmony_ci#define ST_MMC_CCONFIG_8BIT BIT(20) 4262306a36Sopenharmony_ci#define ST_MMC_CCONFIG_MAX_BLK_LEN 16 4362306a36Sopenharmony_ci#define MAX_BLK_LEN_1024 1 4462306a36Sopenharmony_ci#define MAX_BLK_LEN_2048 2 4562306a36Sopenharmony_ci#define BASE_CLK_FREQ_200 0xc8 4662306a36Sopenharmony_ci#define BASE_CLK_FREQ_100 0x64 4762306a36Sopenharmony_ci#define BASE_CLK_FREQ_50 0x32 4862306a36Sopenharmony_ci#define ST_MMC_CCONFIG_2_DEFAULT \ 4962306a36Sopenharmony_ci (ST_MMC_CCONFIG_HIGH_SPEED | ST_MMC_CCONFIG_ADMA2 | \ 5062306a36Sopenharmony_ci ST_MMC_CCONFIG_8BIT | \ 5162306a36Sopenharmony_ci (MAX_BLK_LEN_1024 << ST_MMC_CCONFIG_MAX_BLK_LEN)) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define ST_MMC_CCONFIG_REG_3 0x408 5462306a36Sopenharmony_ci#define ST_MMC_CCONFIG_EMMC_SLOT_TYPE BIT(28) 5562306a36Sopenharmony_ci#define ST_MMC_CCONFIG_64BIT BIT(24) 5662306a36Sopenharmony_ci#define ST_MMC_CCONFIG_ASYNCH_INTR_SUPPORT BIT(20) 5762306a36Sopenharmony_ci#define ST_MMC_CCONFIG_1P8_VOLT BIT(16) 5862306a36Sopenharmony_ci#define ST_MMC_CCONFIG_3P0_VOLT BIT(12) 5962306a36Sopenharmony_ci#define ST_MMC_CCONFIG_3P3_VOLT BIT(8) 6062306a36Sopenharmony_ci#define ST_MMC_CCONFIG_SUSP_RES_SUPPORT BIT(4) 6162306a36Sopenharmony_ci#define ST_MMC_CCONFIG_SDMA BIT(0) 6262306a36Sopenharmony_ci#define ST_MMC_CCONFIG_3_DEFAULT \ 6362306a36Sopenharmony_ci (ST_MMC_CCONFIG_ASYNCH_INTR_SUPPORT | \ 6462306a36Sopenharmony_ci ST_MMC_CCONFIG_3P3_VOLT | \ 6562306a36Sopenharmony_ci ST_MMC_CCONFIG_SUSP_RES_SUPPORT | \ 6662306a36Sopenharmony_ci ST_MMC_CCONFIG_SDMA) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define ST_MMC_CCONFIG_REG_4 0x40c 6962306a36Sopenharmony_ci#define ST_MMC_CCONFIG_D_DRIVER BIT(20) 7062306a36Sopenharmony_ci#define ST_MMC_CCONFIG_C_DRIVER BIT(16) 7162306a36Sopenharmony_ci#define ST_MMC_CCONFIG_A_DRIVER BIT(12) 7262306a36Sopenharmony_ci#define ST_MMC_CCONFIG_DDR50 BIT(8) 7362306a36Sopenharmony_ci#define ST_MMC_CCONFIG_SDR104 BIT(4) 7462306a36Sopenharmony_ci#define ST_MMC_CCONFIG_SDR50 BIT(0) 7562306a36Sopenharmony_ci#define ST_MMC_CCONFIG_4_DEFAULT 0 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define ST_MMC_CCONFIG_REG_5 0x410 7862306a36Sopenharmony_ci#define ST_MMC_CCONFIG_TUNING_FOR_SDR50 BIT(8) 7962306a36Sopenharmony_ci#define RETUNING_TIMER_CNT_MAX 0xf 8062306a36Sopenharmony_ci#define ST_MMC_CCONFIG_5_DEFAULT 0 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* I/O configuration for Arasan IP */ 8362306a36Sopenharmony_ci#define ST_MMC_GP_OUTPUT 0x450 8462306a36Sopenharmony_ci#define ST_MMC_GP_OUTPUT_CD BIT(12) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define ST_MMC_STATUS_R 0x460 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define ST_TOP_MMC_DLY_FIX_OFF(x) (x - 0x8) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* TOP config registers to manage static and dynamic delay */ 9162306a36Sopenharmony_ci#define ST_TOP_MMC_TX_CLK_DLY ST_TOP_MMC_DLY_FIX_OFF(0x8) 9262306a36Sopenharmony_ci#define ST_TOP_MMC_RX_CLK_DLY ST_TOP_MMC_DLY_FIX_OFF(0xc) 9362306a36Sopenharmony_ci/* MMC delay control register */ 9462306a36Sopenharmony_ci#define ST_TOP_MMC_DLY_CTRL ST_TOP_MMC_DLY_FIX_OFF(0x18) 9562306a36Sopenharmony_ci#define ST_TOP_MMC_DLY_CTRL_DLL_BYPASS_CMD BIT(0) 9662306a36Sopenharmony_ci#define ST_TOP_MMC_DLY_CTRL_DLL_BYPASS_PH_SEL BIT(1) 9762306a36Sopenharmony_ci#define ST_TOP_MMC_DLY_CTRL_TX_DLL_ENABLE BIT(8) 9862306a36Sopenharmony_ci#define ST_TOP_MMC_DLY_CTRL_RX_DLL_ENABLE BIT(9) 9962306a36Sopenharmony_ci#define ST_TOP_MMC_DLY_CTRL_ATUNE_NOT_CFG_DLY BIT(10) 10062306a36Sopenharmony_ci#define ST_TOP_MMC_START_DLL_LOCK BIT(11) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* register to provide the phase-shift value for DLL */ 10362306a36Sopenharmony_ci#define ST_TOP_MMC_TX_DLL_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x1c) 10462306a36Sopenharmony_ci#define ST_TOP_MMC_RX_DLL_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x20) 10562306a36Sopenharmony_ci#define ST_TOP_MMC_RX_CMD_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x24) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* phase shift delay on the tx clk 2.188ns */ 10862306a36Sopenharmony_ci#define ST_TOP_MMC_TX_DLL_STEP_DLY_VALID 0x6 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define ST_TOP_MMC_DLY_MAX 0xf 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define ST_TOP_MMC_DYN_DLY_CONF \ 11362306a36Sopenharmony_ci (ST_TOP_MMC_DLY_CTRL_TX_DLL_ENABLE | \ 11462306a36Sopenharmony_ci ST_TOP_MMC_DLY_CTRL_ATUNE_NOT_CFG_DLY | \ 11562306a36Sopenharmony_ci ST_TOP_MMC_START_DLL_LOCK) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * For clock speeds greater than 90MHz, we need to check that the 11962306a36Sopenharmony_ci * DLL procedure has finished before switching to ultra-speed modes. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci#define CLK_TO_CHECK_DLL_LOCK 90000000 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline void st_mmcss_set_static_delay(void __iomem *ioaddr) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci if (!ioaddr) 12662306a36Sopenharmony_ci return; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci writel_relaxed(0x0, ioaddr + ST_TOP_MMC_DLY_CTRL); 12962306a36Sopenharmony_ci writel_relaxed(ST_TOP_MMC_DLY_MAX, 13062306a36Sopenharmony_ci ioaddr + ST_TOP_MMC_TX_CLK_DLY); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/** 13462306a36Sopenharmony_ci * st_mmcss_cconfig: configure the Arasan HC inside the flashSS. 13562306a36Sopenharmony_ci * @np: dt device node. 13662306a36Sopenharmony_ci * @host: sdhci host 13762306a36Sopenharmony_ci * Description: this function is to configure the Arasan host controller. 13862306a36Sopenharmony_ci * On some ST SoCs, i.e. STiH407 family, the MMC devices inside a dedicated 13962306a36Sopenharmony_ci * flashSS sub-system which needs to be configured to be compliant to eMMC 4.5 14062306a36Sopenharmony_ci * or eMMC4.3. This has to be done before registering the sdhci host. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cistatic void st_mmcss_cconfig(struct device_node *np, struct sdhci_host *host) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 14562306a36Sopenharmony_ci struct mmc_host *mhost = host->mmc; 14662306a36Sopenharmony_ci u32 cconf2, cconf3, cconf4, cconf5; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!of_device_is_compatible(np, "st,sdhci-stih407")) 14962306a36Sopenharmony_ci return; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci cconf2 = ST_MMC_CCONFIG_2_DEFAULT; 15262306a36Sopenharmony_ci cconf3 = ST_MMC_CCONFIG_3_DEFAULT; 15362306a36Sopenharmony_ci cconf4 = ST_MMC_CCONFIG_4_DEFAULT; 15462306a36Sopenharmony_ci cconf5 = ST_MMC_CCONFIG_5_DEFAULT; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci writel_relaxed(ST_MMC_CCONFIG_1_DEFAULT, 15762306a36Sopenharmony_ci host->ioaddr + ST_MMC_CCONFIG_REG_1); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* Set clock frequency, default to 50MHz if max-frequency is not 16062306a36Sopenharmony_ci * provided */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci switch (mhost->f_max) { 16362306a36Sopenharmony_ci case 200000000: 16462306a36Sopenharmony_ci clk_set_rate(pltfm_host->clk, mhost->f_max); 16562306a36Sopenharmony_ci cconf2 |= BASE_CLK_FREQ_200; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case 100000000: 16862306a36Sopenharmony_ci clk_set_rate(pltfm_host->clk, mhost->f_max); 16962306a36Sopenharmony_ci cconf2 |= BASE_CLK_FREQ_100; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci default: 17262306a36Sopenharmony_ci clk_set_rate(pltfm_host->clk, 50000000); 17362306a36Sopenharmony_ci cconf2 |= BASE_CLK_FREQ_50; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci writel_relaxed(cconf2, host->ioaddr + ST_MMC_CCONFIG_REG_2); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!mmc_card_is_removable(mhost)) 17962306a36Sopenharmony_ci cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE; 18062306a36Sopenharmony_ci else 18162306a36Sopenharmony_ci /* CARD _D ET_CTRL */ 18262306a36Sopenharmony_ci writel_relaxed(ST_MMC_GP_OUTPUT_CD, 18362306a36Sopenharmony_ci host->ioaddr + ST_MMC_GP_OUTPUT); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (mhost->caps & MMC_CAP_UHS_SDR50) { 18662306a36Sopenharmony_ci /* use 1.8V */ 18762306a36Sopenharmony_ci cconf3 |= ST_MMC_CCONFIG_1P8_VOLT; 18862306a36Sopenharmony_ci cconf4 |= ST_MMC_CCONFIG_SDR50; 18962306a36Sopenharmony_ci /* Use tuning */ 19062306a36Sopenharmony_ci cconf5 |= ST_MMC_CCONFIG_TUNING_FOR_SDR50; 19162306a36Sopenharmony_ci /* Max timeout for retuning */ 19262306a36Sopenharmony_ci cconf5 |= RETUNING_TIMER_CNT_MAX; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (mhost->caps & MMC_CAP_UHS_SDR104) { 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * SDR104 implies the HC can support HS200 mode, so 19862306a36Sopenharmony_ci * it's mandatory to use 1.8V 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci cconf3 |= ST_MMC_CCONFIG_1P8_VOLT; 20162306a36Sopenharmony_ci cconf4 |= ST_MMC_CCONFIG_SDR104; 20262306a36Sopenharmony_ci /* Max timeout for retuning */ 20362306a36Sopenharmony_ci cconf5 |= RETUNING_TIMER_CNT_MAX; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (mhost->caps & MMC_CAP_UHS_DDR50) 20762306a36Sopenharmony_ci cconf4 |= ST_MMC_CCONFIG_DDR50; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci writel_relaxed(cconf3, host->ioaddr + ST_MMC_CCONFIG_REG_3); 21062306a36Sopenharmony_ci writel_relaxed(cconf4, host->ioaddr + ST_MMC_CCONFIG_REG_4); 21162306a36Sopenharmony_ci writel_relaxed(cconf5, host->ioaddr + ST_MMC_CCONFIG_REG_5); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic inline void st_mmcss_set_dll(void __iomem *ioaddr) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci if (!ioaddr) 21762306a36Sopenharmony_ci return; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci writel_relaxed(ST_TOP_MMC_DYN_DLY_CONF, ioaddr + ST_TOP_MMC_DLY_CTRL); 22062306a36Sopenharmony_ci writel_relaxed(ST_TOP_MMC_TX_DLL_STEP_DLY_VALID, 22162306a36Sopenharmony_ci ioaddr + ST_TOP_MMC_TX_DLL_STEP_DLY); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int st_mmcss_lock_dll(void __iomem *ioaddr) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci unsigned long curr, value; 22762306a36Sopenharmony_ci unsigned long finish = jiffies + HZ; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Checks if the DLL procedure is finished */ 23062306a36Sopenharmony_ci do { 23162306a36Sopenharmony_ci curr = jiffies; 23262306a36Sopenharmony_ci value = readl(ioaddr + ST_MMC_STATUS_R); 23362306a36Sopenharmony_ci if (value & 0x1) 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci cpu_relax(); 23762306a36Sopenharmony_ci } while (!time_after_eq(curr, finish)); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return -EBUSY; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int sdhci_st_set_dll_for_clock(struct sdhci_host *host) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci int ret = 0; 24562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 24662306a36Sopenharmony_ci struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (host->clock > CLK_TO_CHECK_DLL_LOCK) { 24962306a36Sopenharmony_ci st_mmcss_set_dll(pdata->top_ioaddr); 25062306a36Sopenharmony_ci ret = st_mmcss_lock_dll(host->ioaddr); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void sdhci_st_set_uhs_signaling(struct sdhci_host *host, 25762306a36Sopenharmony_ci unsigned int uhs) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 26062306a36Sopenharmony_ci struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host); 26162306a36Sopenharmony_ci u16 ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 26262306a36Sopenharmony_ci int ret = 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Select Bus Speed Mode for host */ 26562306a36Sopenharmony_ci ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 26662306a36Sopenharmony_ci switch (uhs) { 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * Set V18_EN -- UHS modes do not work without this. 26962306a36Sopenharmony_ci * does not change signaling voltage 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR12: 27362306a36Sopenharmony_ci st_mmcss_set_static_delay(pdata->top_ioaddr); 27462306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR12 | SDHCI_CTRL_VDD_180; 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR25: 27762306a36Sopenharmony_ci st_mmcss_set_static_delay(pdata->top_ioaddr); 27862306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR25 | SDHCI_CTRL_VDD_180; 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR50: 28162306a36Sopenharmony_ci st_mmcss_set_static_delay(pdata->top_ioaddr); 28262306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180; 28362306a36Sopenharmony_ci ret = sdhci_st_set_dll_for_clock(host); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR104: 28662306a36Sopenharmony_ci case MMC_TIMING_MMC_HS200: 28762306a36Sopenharmony_ci st_mmcss_set_static_delay(pdata->top_ioaddr); 28862306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180; 28962306a36Sopenharmony_ci ret = sdhci_st_set_dll_for_clock(host); 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case MMC_TIMING_UHS_DDR50: 29262306a36Sopenharmony_ci case MMC_TIMING_MMC_DDR52: 29362306a36Sopenharmony_ci st_mmcss_set_static_delay(pdata->top_ioaddr); 29462306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180; 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (ret) 29962306a36Sopenharmony_ci dev_warn(mmc_dev(host->mmc), "Error setting dll for clock " 30062306a36Sopenharmony_ci "(uhs %d)\n", uhs); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "uhs %d, ctrl_2 %04X\n", uhs, ctrl_2); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic u32 sdhci_st_readl(struct sdhci_host *host, int reg) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci u32 ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci switch (reg) { 31262306a36Sopenharmony_ci case SDHCI_CAPABILITIES: 31362306a36Sopenharmony_ci ret = readl_relaxed(host->ioaddr + reg); 31462306a36Sopenharmony_ci /* Support 3.3V and 1.8V */ 31562306a36Sopenharmony_ci ret &= ~SDHCI_CAN_VDD_300; 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci default: 31862306a36Sopenharmony_ci ret = readl_relaxed(host->ioaddr + reg); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic const struct sdhci_ops sdhci_st_ops = { 32462306a36Sopenharmony_ci .get_max_clock = sdhci_pltfm_clk_get_max_clock, 32562306a36Sopenharmony_ci .set_clock = sdhci_set_clock, 32662306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 32762306a36Sopenharmony_ci .read_l = sdhci_st_readl, 32862306a36Sopenharmony_ci .reset = sdhci_reset, 32962306a36Sopenharmony_ci .set_uhs_signaling = sdhci_st_set_uhs_signaling, 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_st_pdata = { 33362306a36Sopenharmony_ci .ops = &sdhci_st_ops, 33462306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | 33562306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 33662306a36Sopenharmony_ci SDHCI_QUIRK_NO_HISPD_BIT, 33762306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 33862306a36Sopenharmony_ci SDHCI_QUIRK2_STOP_WITH_TC, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int sdhci_st_probe(struct platform_device *pdev) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 34562306a36Sopenharmony_ci struct sdhci_host *host; 34662306a36Sopenharmony_ci struct st_mmc_platform_data *pdata; 34762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 34862306a36Sopenharmony_ci struct clk *clk, *icnclk; 34962306a36Sopenharmony_ci int ret = 0; 35062306a36Sopenharmony_ci u16 host_version; 35162306a36Sopenharmony_ci struct reset_control *rstc; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, "mmc"); 35462306a36Sopenharmony_ci if (IS_ERR(clk)) { 35562306a36Sopenharmony_ci dev_err(&pdev->dev, "Peripheral clk not found\n"); 35662306a36Sopenharmony_ci return PTR_ERR(clk); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* ICN clock isn't compulsory, but use it if it's provided. */ 36062306a36Sopenharmony_ci icnclk = devm_clk_get(&pdev->dev, "icn"); 36162306a36Sopenharmony_ci if (IS_ERR(icnclk)) 36262306a36Sopenharmony_ci icnclk = NULL; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); 36562306a36Sopenharmony_ci if (IS_ERR(rstc)) 36662306a36Sopenharmony_ci return PTR_ERR(rstc); 36762306a36Sopenharmony_ci reset_control_deassert(rstc); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, sizeof(*pdata)); 37062306a36Sopenharmony_ci if (IS_ERR(host)) { 37162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n"); 37262306a36Sopenharmony_ci ret = PTR_ERR(host); 37362306a36Sopenharmony_ci goto err_pltfm_init; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 37762306a36Sopenharmony_ci pdata = sdhci_pltfm_priv(pltfm_host); 37862306a36Sopenharmony_ci pdata->rstc = rstc; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ret = mmc_of_parse(host->mmc); 38162306a36Sopenharmony_ci if (ret) { 38262306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed mmc_of_parse\n"); 38362306a36Sopenharmony_ci goto err_of; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ret = clk_prepare_enable(clk); 38762306a36Sopenharmony_ci if (ret) { 38862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to prepare clock\n"); 38962306a36Sopenharmony_ci goto err_of; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = clk_prepare_enable(icnclk); 39362306a36Sopenharmony_ci if (ret) { 39462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to prepare icn clock\n"); 39562306a36Sopenharmony_ci goto err_icnclk; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Configure the FlashSS Top registers for setting eMMC TX/RX delay */ 39962306a36Sopenharmony_ci pdata->top_ioaddr = devm_platform_ioremap_resource_byname(pdev, "top-mmc-delay"); 40062306a36Sopenharmony_ci if (IS_ERR(pdata->top_ioaddr)) 40162306a36Sopenharmony_ci pdata->top_ioaddr = NULL; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci pltfm_host->clk = clk; 40462306a36Sopenharmony_ci pdata->icnclk = icnclk; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Configure the Arasan HC inside the flashSS */ 40762306a36Sopenharmony_ci st_mmcss_cconfig(np, host); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ret = sdhci_add_host(host); 41062306a36Sopenharmony_ci if (ret) 41162306a36Sopenharmony_ci goto err_out; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci dev_info(&pdev->dev, "SDHCI ST Initialised: Host Version: 0x%x Vendor Version 0x%x\n", 41662306a36Sopenharmony_ci ((host_version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT), 41762306a36Sopenharmony_ci ((host_version & SDHCI_VENDOR_VER_MASK) >> 41862306a36Sopenharmony_ci SDHCI_VENDOR_VER_SHIFT)); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cierr_out: 42362306a36Sopenharmony_ci clk_disable_unprepare(icnclk); 42462306a36Sopenharmony_cierr_icnclk: 42562306a36Sopenharmony_ci clk_disable_unprepare(clk); 42662306a36Sopenharmony_cierr_of: 42762306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 42862306a36Sopenharmony_cierr_pltfm_init: 42962306a36Sopenharmony_ci reset_control_assert(rstc); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void sdhci_st_remove(struct platform_device *pdev) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct sdhci_host *host = platform_get_drvdata(pdev); 43762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 43862306a36Sopenharmony_ci struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host); 43962306a36Sopenharmony_ci struct reset_control *rstc = pdata->rstc; 44062306a36Sopenharmony_ci struct clk *clk = pltfm_host->clk; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci sdhci_pltfm_remove(pdev); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci clk_disable_unprepare(pdata->icnclk); 44562306a36Sopenharmony_ci clk_disable_unprepare(clk); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci reset_control_assert(rstc); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 45162306a36Sopenharmony_cistatic int sdhci_st_suspend(struct device *dev) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 45462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 45562306a36Sopenharmony_ci struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host); 45662306a36Sopenharmony_ci int ret; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (host->tuning_mode != SDHCI_TUNING_MODE_3) 45962306a36Sopenharmony_ci mmc_retune_needed(host->mmc); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ret = sdhci_suspend_host(host); 46262306a36Sopenharmony_ci if (ret) 46362306a36Sopenharmony_ci goto out; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci reset_control_assert(pdata->rstc); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci clk_disable_unprepare(pdata->icnclk); 46862306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 46962306a36Sopenharmony_ciout: 47062306a36Sopenharmony_ci return ret; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int sdhci_st_resume(struct device *dev) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 47662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 47762306a36Sopenharmony_ci struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host); 47862306a36Sopenharmony_ci struct device_node *np = dev->of_node; 47962306a36Sopenharmony_ci int ret; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ret = clk_prepare_enable(pltfm_host->clk); 48262306a36Sopenharmony_ci if (ret) 48362306a36Sopenharmony_ci return ret; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ret = clk_prepare_enable(pdata->icnclk); 48662306a36Sopenharmony_ci if (ret) { 48762306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 48862306a36Sopenharmony_ci return ret; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci reset_control_deassert(pdata->rstc); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci st_mmcss_cconfig(np, host); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return sdhci_resume_host(host); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci#endif 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic const struct of_device_id st_sdhci_match[] = { 50262306a36Sopenharmony_ci { .compatible = "st,sdhci" }, 50362306a36Sopenharmony_ci {}, 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, st_sdhci_match); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic struct platform_driver sdhci_st_driver = { 50962306a36Sopenharmony_ci .probe = sdhci_st_probe, 51062306a36Sopenharmony_ci .remove_new = sdhci_st_remove, 51162306a36Sopenharmony_ci .driver = { 51262306a36Sopenharmony_ci .name = "sdhci-st", 51362306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 51462306a36Sopenharmony_ci .pm = &sdhci_st_pmops, 51562306a36Sopenharmony_ci .of_match_table = st_sdhci_match, 51662306a36Sopenharmony_ci }, 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cimodule_platform_driver(sdhci_st_driver); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ciMODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs"); 52262306a36Sopenharmony_ciMODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 52362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 52462306a36Sopenharmony_ciMODULE_ALIAS("platform:sdhci-st"); 525