162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/mmc/mmc.h> 1162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1262306a36Sopenharmony_ci#include <linux/pm_opp.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/iopoll.h> 1562306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1662306a36Sopenharmony_ci#include <linux/interconnect.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1962306a36Sopenharmony_ci#include <linux/reset.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <soc/qcom/ice.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "sdhci-cqhci.h" 2462306a36Sopenharmony_ci#include "sdhci-pltfm.h" 2562306a36Sopenharmony_ci#include "cqhci.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define CORE_MCI_VERSION 0x50 2862306a36Sopenharmony_ci#define CORE_VERSION_MAJOR_SHIFT 28 2962306a36Sopenharmony_ci#define CORE_VERSION_MAJOR_MASK (0xf << CORE_VERSION_MAJOR_SHIFT) 3062306a36Sopenharmony_ci#define CORE_VERSION_MINOR_MASK 0xff 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define CORE_MCI_GENERICS 0x70 3362306a36Sopenharmony_ci#define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define HC_MODE_EN 0x1 3662306a36Sopenharmony_ci#define CORE_POWER 0x0 3762306a36Sopenharmony_ci#define CORE_SW_RST BIT(7) 3862306a36Sopenharmony_ci#define FF_CLK_SW_RST_DIS BIT(13) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define CORE_PWRCTL_BUS_OFF BIT(0) 4162306a36Sopenharmony_ci#define CORE_PWRCTL_BUS_ON BIT(1) 4262306a36Sopenharmony_ci#define CORE_PWRCTL_IO_LOW BIT(2) 4362306a36Sopenharmony_ci#define CORE_PWRCTL_IO_HIGH BIT(3) 4462306a36Sopenharmony_ci#define CORE_PWRCTL_BUS_SUCCESS BIT(0) 4562306a36Sopenharmony_ci#define CORE_PWRCTL_BUS_FAIL BIT(1) 4662306a36Sopenharmony_ci#define CORE_PWRCTL_IO_SUCCESS BIT(2) 4762306a36Sopenharmony_ci#define CORE_PWRCTL_IO_FAIL BIT(3) 4862306a36Sopenharmony_ci#define REQ_BUS_OFF BIT(0) 4962306a36Sopenharmony_ci#define REQ_BUS_ON BIT(1) 5062306a36Sopenharmony_ci#define REQ_IO_LOW BIT(2) 5162306a36Sopenharmony_ci#define REQ_IO_HIGH BIT(3) 5262306a36Sopenharmony_ci#define INT_MASK 0xf 5362306a36Sopenharmony_ci#define MAX_PHASES 16 5462306a36Sopenharmony_ci#define CORE_DLL_LOCK BIT(7) 5562306a36Sopenharmony_ci#define CORE_DDR_DLL_LOCK BIT(11) 5662306a36Sopenharmony_ci#define CORE_DLL_EN BIT(16) 5762306a36Sopenharmony_ci#define CORE_CDR_EN BIT(17) 5862306a36Sopenharmony_ci#define CORE_CK_OUT_EN BIT(18) 5962306a36Sopenharmony_ci#define CORE_CDR_EXT_EN BIT(19) 6062306a36Sopenharmony_ci#define CORE_DLL_PDN BIT(29) 6162306a36Sopenharmony_ci#define CORE_DLL_RST BIT(30) 6262306a36Sopenharmony_ci#define CORE_CMD_DAT_TRACK_SEL BIT(0) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define CORE_DDR_CAL_EN BIT(0) 6562306a36Sopenharmony_ci#define CORE_FLL_CYCLE_CNT BIT(18) 6662306a36Sopenharmony_ci#define CORE_DLL_CLOCK_DISABLE BIT(21) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define DLL_USR_CTL_POR_VAL 0x10800 6962306a36Sopenharmony_ci#define ENABLE_DLL_LOCK_STATUS BIT(26) 7062306a36Sopenharmony_ci#define FINE_TUNE_MODE_EN BIT(27) 7162306a36Sopenharmony_ci#define BIAS_OK_SIGNAL BIT(29) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define DLL_CONFIG_3_LOW_FREQ_VAL 0x08 7462306a36Sopenharmony_ci#define DLL_CONFIG_3_HIGH_FREQ_VAL 0x10 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define CORE_VENDOR_SPEC_POR_VAL 0xa9c 7762306a36Sopenharmony_ci#define CORE_CLK_PWRSAVE BIT(1) 7862306a36Sopenharmony_ci#define CORE_HC_MCLK_SEL_DFLT (2 << 8) 7962306a36Sopenharmony_ci#define CORE_HC_MCLK_SEL_HS400 (3 << 8) 8062306a36Sopenharmony_ci#define CORE_HC_MCLK_SEL_MASK (3 << 8) 8162306a36Sopenharmony_ci#define CORE_IO_PAD_PWR_SWITCH_EN BIT(15) 8262306a36Sopenharmony_ci#define CORE_IO_PAD_PWR_SWITCH BIT(16) 8362306a36Sopenharmony_ci#define CORE_HC_SELECT_IN_EN BIT(18) 8462306a36Sopenharmony_ci#define CORE_HC_SELECT_IN_HS400 (6 << 19) 8562306a36Sopenharmony_ci#define CORE_HC_SELECT_IN_MASK (7 << 19) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define CORE_3_0V_SUPPORT BIT(25) 8862306a36Sopenharmony_ci#define CORE_1_8V_SUPPORT BIT(26) 8962306a36Sopenharmony_ci#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define CORE_CSR_CDC_CTLR_CFG0 0x130 9262306a36Sopenharmony_ci#define CORE_SW_TRIG_FULL_CALIB BIT(16) 9362306a36Sopenharmony_ci#define CORE_HW_AUTOCAL_ENA BIT(17) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define CORE_CSR_CDC_CTLR_CFG1 0x134 9662306a36Sopenharmony_ci#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138 9762306a36Sopenharmony_ci#define CORE_TIMER_ENA BIT(16) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C 10062306a36Sopenharmony_ci#define CORE_CSR_CDC_REFCOUNT_CFG 0x140 10162306a36Sopenharmony_ci#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144 10262306a36Sopenharmony_ci#define CORE_CDC_OFFSET_CFG 0x14C 10362306a36Sopenharmony_ci#define CORE_CSR_CDC_DELAY_CFG 0x150 10462306a36Sopenharmony_ci#define CORE_CDC_SLAVE_DDA_CFG 0x160 10562306a36Sopenharmony_ci#define CORE_CSR_CDC_STATUS0 0x164 10662306a36Sopenharmony_ci#define CORE_CALIBRATION_DONE BIT(0) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define CORE_CDC_ERROR_CODE_MASK 0x7000000 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define CORE_CSR_CDC_GEN_CFG 0x178 11162306a36Sopenharmony_ci#define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) 11262306a36Sopenharmony_ci#define CORE_CDC_SWITCH_RC_EN BIT(1) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define CORE_CDC_T4_DLY_SEL BIT(0) 11562306a36Sopenharmony_ci#define CORE_CMDIN_RCLK_EN BIT(1) 11662306a36Sopenharmony_ci#define CORE_START_CDC_TRAFFIC BIT(6) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define CORE_PWRSAVE_DLL BIT(3) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define DDR_CONFIG_POR_VAL 0x80040873 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define INVALID_TUNING_PHASE -1 12462306a36Sopenharmony_ci#define SDHCI_MSM_MIN_CLOCK 400000 12562306a36Sopenharmony_ci#define CORE_FREQ_100MHZ (100 * 1000 * 1000) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define CDR_SELEXT_SHIFT 20 12862306a36Sopenharmony_ci#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT) 12962306a36Sopenharmony_ci#define CMUX_SHIFT_PHASE_SHIFT 24 13062306a36Sopenharmony_ci#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define MSM_MMC_AUTOSUSPEND_DELAY_MS 50 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* Timeout value to avoid infinite waiting for pwr_irq */ 13562306a36Sopenharmony_ci#define MSM_PWR_IRQ_TIMEOUT_MS 5000 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* Max load for eMMC Vdd-io supply */ 13862306a36Sopenharmony_ci#define MMC_VQMMC_MAX_LOAD_UA 325000 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#define msm_host_readl(msm_host, host, offset) \ 14162306a36Sopenharmony_ci msm_host->var_ops->msm_readl_relaxed(host, offset) 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define msm_host_writel(msm_host, val, host, offset) \ 14462306a36Sopenharmony_ci msm_host->var_ops->msm_writel_relaxed(val, host, offset) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* CQHCI vendor specific registers */ 14762306a36Sopenharmony_ci#define CQHCI_VENDOR_CFG1 0xA00 14862306a36Sopenharmony_ci#define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct sdhci_msm_offset { 15162306a36Sopenharmony_ci u32 core_hc_mode; 15262306a36Sopenharmony_ci u32 core_mci_data_cnt; 15362306a36Sopenharmony_ci u32 core_mci_status; 15462306a36Sopenharmony_ci u32 core_mci_fifo_cnt; 15562306a36Sopenharmony_ci u32 core_mci_version; 15662306a36Sopenharmony_ci u32 core_generics; 15762306a36Sopenharmony_ci u32 core_testbus_config; 15862306a36Sopenharmony_ci u32 core_testbus_sel2_bit; 15962306a36Sopenharmony_ci u32 core_testbus_ena; 16062306a36Sopenharmony_ci u32 core_testbus_sel2; 16162306a36Sopenharmony_ci u32 core_pwrctl_status; 16262306a36Sopenharmony_ci u32 core_pwrctl_mask; 16362306a36Sopenharmony_ci u32 core_pwrctl_clear; 16462306a36Sopenharmony_ci u32 core_pwrctl_ctl; 16562306a36Sopenharmony_ci u32 core_sdcc_debug_reg; 16662306a36Sopenharmony_ci u32 core_dll_config; 16762306a36Sopenharmony_ci u32 core_dll_status; 16862306a36Sopenharmony_ci u32 core_vendor_spec; 16962306a36Sopenharmony_ci u32 core_vendor_spec_adma_err_addr0; 17062306a36Sopenharmony_ci u32 core_vendor_spec_adma_err_addr1; 17162306a36Sopenharmony_ci u32 core_vendor_spec_func2; 17262306a36Sopenharmony_ci u32 core_vendor_spec_capabilities0; 17362306a36Sopenharmony_ci u32 core_ddr_200_cfg; 17462306a36Sopenharmony_ci u32 core_vendor_spec3; 17562306a36Sopenharmony_ci u32 core_dll_config_2; 17662306a36Sopenharmony_ci u32 core_dll_config_3; 17762306a36Sopenharmony_ci u32 core_ddr_config_old; /* Applicable to sdcc minor ver < 0x49 */ 17862306a36Sopenharmony_ci u32 core_ddr_config; 17962306a36Sopenharmony_ci u32 core_dll_usr_ctl; /* Present on SDCC5.1 onwards */ 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct sdhci_msm_offset sdhci_msm_v5_offset = { 18362306a36Sopenharmony_ci .core_mci_data_cnt = 0x35c, 18462306a36Sopenharmony_ci .core_mci_status = 0x324, 18562306a36Sopenharmony_ci .core_mci_fifo_cnt = 0x308, 18662306a36Sopenharmony_ci .core_mci_version = 0x318, 18762306a36Sopenharmony_ci .core_generics = 0x320, 18862306a36Sopenharmony_ci .core_testbus_config = 0x32c, 18962306a36Sopenharmony_ci .core_testbus_sel2_bit = 3, 19062306a36Sopenharmony_ci .core_testbus_ena = (1 << 31), 19162306a36Sopenharmony_ci .core_testbus_sel2 = (1 << 3), 19262306a36Sopenharmony_ci .core_pwrctl_status = 0x240, 19362306a36Sopenharmony_ci .core_pwrctl_mask = 0x244, 19462306a36Sopenharmony_ci .core_pwrctl_clear = 0x248, 19562306a36Sopenharmony_ci .core_pwrctl_ctl = 0x24c, 19662306a36Sopenharmony_ci .core_sdcc_debug_reg = 0x358, 19762306a36Sopenharmony_ci .core_dll_config = 0x200, 19862306a36Sopenharmony_ci .core_dll_status = 0x208, 19962306a36Sopenharmony_ci .core_vendor_spec = 0x20c, 20062306a36Sopenharmony_ci .core_vendor_spec_adma_err_addr0 = 0x214, 20162306a36Sopenharmony_ci .core_vendor_spec_adma_err_addr1 = 0x218, 20262306a36Sopenharmony_ci .core_vendor_spec_func2 = 0x210, 20362306a36Sopenharmony_ci .core_vendor_spec_capabilities0 = 0x21c, 20462306a36Sopenharmony_ci .core_ddr_200_cfg = 0x224, 20562306a36Sopenharmony_ci .core_vendor_spec3 = 0x250, 20662306a36Sopenharmony_ci .core_dll_config_2 = 0x254, 20762306a36Sopenharmony_ci .core_dll_config_3 = 0x258, 20862306a36Sopenharmony_ci .core_ddr_config = 0x25c, 20962306a36Sopenharmony_ci .core_dll_usr_ctl = 0x388, 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic const struct sdhci_msm_offset sdhci_msm_mci_offset = { 21362306a36Sopenharmony_ci .core_hc_mode = 0x78, 21462306a36Sopenharmony_ci .core_mci_data_cnt = 0x30, 21562306a36Sopenharmony_ci .core_mci_status = 0x34, 21662306a36Sopenharmony_ci .core_mci_fifo_cnt = 0x44, 21762306a36Sopenharmony_ci .core_mci_version = 0x050, 21862306a36Sopenharmony_ci .core_generics = 0x70, 21962306a36Sopenharmony_ci .core_testbus_config = 0x0cc, 22062306a36Sopenharmony_ci .core_testbus_sel2_bit = 4, 22162306a36Sopenharmony_ci .core_testbus_ena = (1 << 3), 22262306a36Sopenharmony_ci .core_testbus_sel2 = (1 << 4), 22362306a36Sopenharmony_ci .core_pwrctl_status = 0xdc, 22462306a36Sopenharmony_ci .core_pwrctl_mask = 0xe0, 22562306a36Sopenharmony_ci .core_pwrctl_clear = 0xe4, 22662306a36Sopenharmony_ci .core_pwrctl_ctl = 0xe8, 22762306a36Sopenharmony_ci .core_sdcc_debug_reg = 0x124, 22862306a36Sopenharmony_ci .core_dll_config = 0x100, 22962306a36Sopenharmony_ci .core_dll_status = 0x108, 23062306a36Sopenharmony_ci .core_vendor_spec = 0x10c, 23162306a36Sopenharmony_ci .core_vendor_spec_adma_err_addr0 = 0x114, 23262306a36Sopenharmony_ci .core_vendor_spec_adma_err_addr1 = 0x118, 23362306a36Sopenharmony_ci .core_vendor_spec_func2 = 0x110, 23462306a36Sopenharmony_ci .core_vendor_spec_capabilities0 = 0x11c, 23562306a36Sopenharmony_ci .core_ddr_200_cfg = 0x184, 23662306a36Sopenharmony_ci .core_vendor_spec3 = 0x1b0, 23762306a36Sopenharmony_ci .core_dll_config_2 = 0x1b4, 23862306a36Sopenharmony_ci .core_ddr_config_old = 0x1b8, 23962306a36Sopenharmony_ci .core_ddr_config = 0x1bc, 24062306a36Sopenharmony_ci}; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistruct sdhci_msm_variant_ops { 24362306a36Sopenharmony_ci u32 (*msm_readl_relaxed)(struct sdhci_host *host, u32 offset); 24462306a36Sopenharmony_ci void (*msm_writel_relaxed)(u32 val, struct sdhci_host *host, 24562306a36Sopenharmony_ci u32 offset); 24662306a36Sopenharmony_ci}; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * From V5, register spaces have changed. Wrap this info in a structure 25062306a36Sopenharmony_ci * and choose the data_structure based on version info mentioned in DT. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_cistruct sdhci_msm_variant_info { 25362306a36Sopenharmony_ci bool mci_removed; 25462306a36Sopenharmony_ci bool restore_dll_config; 25562306a36Sopenharmony_ci const struct sdhci_msm_variant_ops *var_ops; 25662306a36Sopenharmony_ci const struct sdhci_msm_offset *offset; 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistruct sdhci_msm_host { 26062306a36Sopenharmony_ci struct platform_device *pdev; 26162306a36Sopenharmony_ci void __iomem *core_mem; /* MSM SDCC mapped address */ 26262306a36Sopenharmony_ci int pwr_irq; /* power irq */ 26362306a36Sopenharmony_ci struct clk *bus_clk; /* SDHC bus voter clock */ 26462306a36Sopenharmony_ci struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/ 26562306a36Sopenharmony_ci /* core, iface, cal and sleep clocks */ 26662306a36Sopenharmony_ci struct clk_bulk_data bulk_clks[4]; 26762306a36Sopenharmony_ci#ifdef CONFIG_MMC_CRYPTO 26862306a36Sopenharmony_ci struct qcom_ice *ice; 26962306a36Sopenharmony_ci#endif 27062306a36Sopenharmony_ci unsigned long clk_rate; 27162306a36Sopenharmony_ci struct mmc_host *mmc; 27262306a36Sopenharmony_ci bool use_14lpp_dll_reset; 27362306a36Sopenharmony_ci bool tuning_done; 27462306a36Sopenharmony_ci bool calibration_done; 27562306a36Sopenharmony_ci u8 saved_tuning_phase; 27662306a36Sopenharmony_ci bool use_cdclp533; 27762306a36Sopenharmony_ci u32 curr_pwr_state; 27862306a36Sopenharmony_ci u32 curr_io_level; 27962306a36Sopenharmony_ci wait_queue_head_t pwr_irq_wait; 28062306a36Sopenharmony_ci bool pwr_irq_flag; 28162306a36Sopenharmony_ci u32 caps_0; 28262306a36Sopenharmony_ci bool mci_removed; 28362306a36Sopenharmony_ci bool restore_dll_config; 28462306a36Sopenharmony_ci const struct sdhci_msm_variant_ops *var_ops; 28562306a36Sopenharmony_ci const struct sdhci_msm_offset *offset; 28662306a36Sopenharmony_ci bool use_cdr; 28762306a36Sopenharmony_ci u32 transfer_mode; 28862306a36Sopenharmony_ci bool updated_ddr_cfg; 28962306a36Sopenharmony_ci bool uses_tassadar_dll; 29062306a36Sopenharmony_ci u32 dll_config; 29162306a36Sopenharmony_ci u32 ddr_config; 29262306a36Sopenharmony_ci bool vqmmc_enabled; 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 29862306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return msm_host->offset; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/* 30462306a36Sopenharmony_ci * APIs to read/write to vendor specific registers which were there in the 30562306a36Sopenharmony_ci * core_mem region before MCI was removed. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_cistatic u32 sdhci_msm_mci_variant_readl_relaxed(struct sdhci_host *host, 30862306a36Sopenharmony_ci u32 offset) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 31162306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return readl_relaxed(msm_host->core_mem + offset); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic u32 sdhci_msm_v5_variant_readl_relaxed(struct sdhci_host *host, 31762306a36Sopenharmony_ci u32 offset) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci return readl_relaxed(host->ioaddr + offset); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void sdhci_msm_mci_variant_writel_relaxed(u32 val, 32362306a36Sopenharmony_ci struct sdhci_host *host, u32 offset) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 32662306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci writel_relaxed(val, msm_host->core_mem + offset); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic void sdhci_msm_v5_variant_writel_relaxed(u32 val, 33262306a36Sopenharmony_ci struct sdhci_host *host, u32 offset) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci writel_relaxed(val, host->ioaddr + offset); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct mmc_ios ios = host->mmc->ios; 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * The SDHC requires internal clock frequency to be double the 34262306a36Sopenharmony_ci * actual clock that will be set for DDR mode. The controller 34362306a36Sopenharmony_ci * uses the faster clock(100/400MHz) for some of its parts and 34462306a36Sopenharmony_ci * send the actual required clock (50/200MHz) to the card. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci if (ios.timing == MMC_TIMING_UHS_DDR50 || 34762306a36Sopenharmony_ci ios.timing == MMC_TIMING_MMC_DDR52 || 34862306a36Sopenharmony_ci ios.timing == MMC_TIMING_MMC_HS400 || 34962306a36Sopenharmony_ci host->flags & SDHCI_HS400_TUNING) 35062306a36Sopenharmony_ci return 2; 35162306a36Sopenharmony_ci return 1; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, 35562306a36Sopenharmony_ci unsigned int clock) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 35862306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 35962306a36Sopenharmony_ci struct mmc_ios curr_ios = host->mmc->ios; 36062306a36Sopenharmony_ci struct clk *core_clk = msm_host->bulk_clks[0].clk; 36162306a36Sopenharmony_ci unsigned long achieved_rate; 36262306a36Sopenharmony_ci unsigned int desired_rate; 36362306a36Sopenharmony_ci unsigned int mult; 36462306a36Sopenharmony_ci int rc; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci mult = msm_get_clock_mult_for_bus_mode(host); 36762306a36Sopenharmony_ci desired_rate = clock * mult; 36862306a36Sopenharmony_ci rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate); 36962306a36Sopenharmony_ci if (rc) { 37062306a36Sopenharmony_ci pr_err("%s: Failed to set clock at rate %u at timing %d\n", 37162306a36Sopenharmony_ci mmc_hostname(host->mmc), desired_rate, curr_ios.timing); 37262306a36Sopenharmony_ci return; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Qualcomm clock drivers by default round clock _up_ if they can't 37762306a36Sopenharmony_ci * make the requested rate. This is not good for SD. Yell if we 37862306a36Sopenharmony_ci * encounter it. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci achieved_rate = clk_get_rate(core_clk); 38162306a36Sopenharmony_ci if (achieved_rate > desired_rate) 38262306a36Sopenharmony_ci pr_warn("%s: Card appears overclocked; req %u Hz, actual %lu Hz\n", 38362306a36Sopenharmony_ci mmc_hostname(host->mmc), desired_rate, achieved_rate); 38462306a36Sopenharmony_ci host->mmc->actual_clock = achieved_rate / mult; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Stash the rate we requested to use in sdhci_msm_runtime_resume() */ 38762306a36Sopenharmony_ci msm_host->clk_rate = desired_rate; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci pr_debug("%s: Setting clock at rate %lu at timing %d\n", 39062306a36Sopenharmony_ci mmc_hostname(host->mmc), achieved_rate, curr_ios.timing); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/* Platform specific tuning */ 39462306a36Sopenharmony_cistatic inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci u32 wait_cnt = 50; 39762306a36Sopenharmony_ci u8 ck_out_en; 39862306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 39962306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 40062306a36Sopenharmony_ci sdhci_priv_msm_offset(host); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Poll for CK_OUT_EN bit. max. poll time = 50us */ 40362306a36Sopenharmony_ci ck_out_en = !!(readl_relaxed(host->ioaddr + 40462306a36Sopenharmony_ci msm_offset->core_dll_config) & CORE_CK_OUT_EN); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci while (ck_out_en != poll) { 40762306a36Sopenharmony_ci if (--wait_cnt == 0) { 40862306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n", 40962306a36Sopenharmony_ci mmc_hostname(mmc), poll); 41062306a36Sopenharmony_ci return -ETIMEDOUT; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci udelay(1); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ck_out_en = !!(readl_relaxed(host->ioaddr + 41562306a36Sopenharmony_ci msm_offset->core_dll_config) & CORE_CK_OUT_EN); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci int rc; 42462306a36Sopenharmony_ci static const u8 grey_coded_phase_table[] = { 42562306a36Sopenharmony_ci 0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4, 42662306a36Sopenharmony_ci 0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8 42762306a36Sopenharmony_ci }; 42862306a36Sopenharmony_ci unsigned long flags; 42962306a36Sopenharmony_ci u32 config; 43062306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 43162306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 43262306a36Sopenharmony_ci sdhci_priv_msm_offset(host); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (phase > 0xf) 43562306a36Sopenharmony_ci return -EINVAL; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); 44062306a36Sopenharmony_ci config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN); 44162306a36Sopenharmony_ci config |= (CORE_CDR_EXT_EN | CORE_DLL_EN); 44262306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */ 44562306a36Sopenharmony_ci rc = msm_dll_poll_ck_out_en(host, 0); 44662306a36Sopenharmony_ci if (rc) 44762306a36Sopenharmony_ci goto err_out; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* 45062306a36Sopenharmony_ci * Write the selected DLL clock output phase (0 ... 15) 45162306a36Sopenharmony_ci * to CDR_SELEXT bit field of DLL_CONFIG register. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); 45462306a36Sopenharmony_ci config &= ~CDR_SELEXT_MASK; 45562306a36Sopenharmony_ci config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT; 45662306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); 45962306a36Sopenharmony_ci config |= CORE_CK_OUT_EN; 46062306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ 46362306a36Sopenharmony_ci rc = msm_dll_poll_ck_out_en(host, 1); 46462306a36Sopenharmony_ci if (rc) 46562306a36Sopenharmony_ci goto err_out; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); 46862306a36Sopenharmony_ci config |= CORE_CDR_EN; 46962306a36Sopenharmony_ci config &= ~CORE_CDR_EXT_EN; 47062306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); 47162306a36Sopenharmony_ci goto out; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cierr_out: 47462306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n", 47562306a36Sopenharmony_ci mmc_hostname(mmc), phase); 47662306a36Sopenharmony_ciout: 47762306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 47862306a36Sopenharmony_ci return rc; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/* 48262306a36Sopenharmony_ci * Find out the greatest range of consecuitive selected 48362306a36Sopenharmony_ci * DLL clock output phases that can be used as sampling 48462306a36Sopenharmony_ci * setting for SD3.0 UHS-I card read operation (in SDR104 48562306a36Sopenharmony_ci * timing mode) or for eMMC4.5 card read operation (in 48662306a36Sopenharmony_ci * HS400/HS200 timing mode). 48762306a36Sopenharmony_ci * Select the 3/4 of the range and configure the DLL with the 48862306a36Sopenharmony_ci * selected DLL clock output phase. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int msm_find_most_appropriate_phase(struct sdhci_host *host, 49262306a36Sopenharmony_ci u8 *phase_table, u8 total_phases) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci int ret; 49562306a36Sopenharmony_ci u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} }; 49662306a36Sopenharmony_ci u8 phases_per_row[MAX_PHASES] = { 0 }; 49762306a36Sopenharmony_ci int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0; 49862306a36Sopenharmony_ci int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0; 49962306a36Sopenharmony_ci bool phase_0_found = false, phase_15_found = false; 50062306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (!total_phases || (total_phases > MAX_PHASES)) { 50362306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n", 50462306a36Sopenharmony_ci mmc_hostname(mmc), total_phases); 50562306a36Sopenharmony_ci return -EINVAL; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci for (cnt = 0; cnt < total_phases; cnt++) { 50962306a36Sopenharmony_ci ranges[row_index][col_index] = phase_table[cnt]; 51062306a36Sopenharmony_ci phases_per_row[row_index] += 1; 51162306a36Sopenharmony_ci col_index++; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if ((cnt + 1) == total_phases) { 51462306a36Sopenharmony_ci continue; 51562306a36Sopenharmony_ci /* check if next phase in phase_table is consecutive or not */ 51662306a36Sopenharmony_ci } else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) { 51762306a36Sopenharmony_ci row_index++; 51862306a36Sopenharmony_ci col_index = 0; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (row_index >= MAX_PHASES) 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Check if phase-0 is present in first valid window? */ 52662306a36Sopenharmony_ci if (!ranges[0][0]) { 52762306a36Sopenharmony_ci phase_0_found = true; 52862306a36Sopenharmony_ci phase_0_raw_index = 0; 52962306a36Sopenharmony_ci /* Check if cycle exist between 2 valid windows */ 53062306a36Sopenharmony_ci for (cnt = 1; cnt <= row_index; cnt++) { 53162306a36Sopenharmony_ci if (phases_per_row[cnt]) { 53262306a36Sopenharmony_ci for (i = 0; i < phases_per_row[cnt]; i++) { 53362306a36Sopenharmony_ci if (ranges[cnt][i] == 15) { 53462306a36Sopenharmony_ci phase_15_found = true; 53562306a36Sopenharmony_ci phase_15_raw_index = cnt; 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* If 2 valid windows form cycle then merge them as single window */ 54462306a36Sopenharmony_ci if (phase_0_found && phase_15_found) { 54562306a36Sopenharmony_ci /* number of phases in raw where phase 0 is present */ 54662306a36Sopenharmony_ci u8 phases_0 = phases_per_row[phase_0_raw_index]; 54762306a36Sopenharmony_ci /* number of phases in raw where phase 15 is present */ 54862306a36Sopenharmony_ci u8 phases_15 = phases_per_row[phase_15_raw_index]; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (phases_0 + phases_15 >= MAX_PHASES) 55162306a36Sopenharmony_ci /* 55262306a36Sopenharmony_ci * If there are more than 1 phase windows then total 55362306a36Sopenharmony_ci * number of phases in both the windows should not be 55462306a36Sopenharmony_ci * more than or equal to MAX_PHASES. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci return -EINVAL; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* Merge 2 cyclic windows */ 55962306a36Sopenharmony_ci i = phases_15; 56062306a36Sopenharmony_ci for (cnt = 0; cnt < phases_0; cnt++) { 56162306a36Sopenharmony_ci ranges[phase_15_raw_index][i] = 56262306a36Sopenharmony_ci ranges[phase_0_raw_index][cnt]; 56362306a36Sopenharmony_ci if (++i >= MAX_PHASES) 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci phases_per_row[phase_0_raw_index] = 0; 56862306a36Sopenharmony_ci phases_per_row[phase_15_raw_index] = phases_15 + phases_0; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci for (cnt = 0; cnt <= row_index; cnt++) { 57262306a36Sopenharmony_ci if (phases_per_row[cnt] > curr_max) { 57362306a36Sopenharmony_ci curr_max = phases_per_row[cnt]; 57462306a36Sopenharmony_ci selected_row_index = cnt; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci i = (curr_max * 3) / 4; 57962306a36Sopenharmony_ci if (i) 58062306a36Sopenharmony_ci i--; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ret = ranges[selected_row_index][i]; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (ret >= MAX_PHASES) { 58562306a36Sopenharmony_ci ret = -EINVAL; 58662306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n", 58762306a36Sopenharmony_ci mmc_hostname(mmc), ret); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return ret; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic inline void msm_cm_dll_set_freq(struct sdhci_host *host) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci u32 mclk_freq = 0, config; 59662306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 59762306a36Sopenharmony_ci sdhci_priv_msm_offset(host); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* Program the MCLK value to MCLK_FREQ bit field */ 60062306a36Sopenharmony_ci if (host->clock <= 112000000) 60162306a36Sopenharmony_ci mclk_freq = 0; 60262306a36Sopenharmony_ci else if (host->clock <= 125000000) 60362306a36Sopenharmony_ci mclk_freq = 1; 60462306a36Sopenharmony_ci else if (host->clock <= 137000000) 60562306a36Sopenharmony_ci mclk_freq = 2; 60662306a36Sopenharmony_ci else if (host->clock <= 150000000) 60762306a36Sopenharmony_ci mclk_freq = 3; 60862306a36Sopenharmony_ci else if (host->clock <= 162000000) 60962306a36Sopenharmony_ci mclk_freq = 4; 61062306a36Sopenharmony_ci else if (host->clock <= 175000000) 61162306a36Sopenharmony_ci mclk_freq = 5; 61262306a36Sopenharmony_ci else if (host->clock <= 187000000) 61362306a36Sopenharmony_ci mclk_freq = 6; 61462306a36Sopenharmony_ci else if (host->clock <= 200000000) 61562306a36Sopenharmony_ci mclk_freq = 7; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); 61862306a36Sopenharmony_ci config &= ~CMUX_SHIFT_PHASE_MASK; 61962306a36Sopenharmony_ci config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT; 62062306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* Initialize the DLL (Programmable Delay Line) */ 62462306a36Sopenharmony_cistatic int msm_init_cm_dll(struct sdhci_host *host) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 62762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 62862306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 62962306a36Sopenharmony_ci int wait_cnt = 50; 63062306a36Sopenharmony_ci unsigned long flags, xo_clk = 0; 63162306a36Sopenharmony_ci u32 config; 63262306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 63362306a36Sopenharmony_ci msm_host->offset; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (msm_host->use_14lpp_dll_reset && !IS_ERR_OR_NULL(msm_host->xo_clk)) 63662306a36Sopenharmony_ci xo_clk = clk_get_rate(msm_host->xo_clk); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * Make sure that clock is always enabled when DLL 64262306a36Sopenharmony_ci * tuning is in progress. Keeping PWRSAVE ON may 64362306a36Sopenharmony_ci * turn off the clock. 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); 64662306a36Sopenharmony_ci config &= ~CORE_CLK_PWRSAVE; 64762306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (msm_host->dll_config) 65062306a36Sopenharmony_ci writel_relaxed(msm_host->dll_config, 65162306a36Sopenharmony_ci host->ioaddr + msm_offset->core_dll_config); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (msm_host->use_14lpp_dll_reset) { 65462306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 65562306a36Sopenharmony_ci msm_offset->core_dll_config); 65662306a36Sopenharmony_ci config &= ~CORE_CK_OUT_EN; 65762306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 65862306a36Sopenharmony_ci msm_offset->core_dll_config); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 66162306a36Sopenharmony_ci msm_offset->core_dll_config_2); 66262306a36Sopenharmony_ci config |= CORE_DLL_CLOCK_DISABLE; 66362306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 66462306a36Sopenharmony_ci msm_offset->core_dll_config_2); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 66862306a36Sopenharmony_ci msm_offset->core_dll_config); 66962306a36Sopenharmony_ci config |= CORE_DLL_RST; 67062306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 67162306a36Sopenharmony_ci msm_offset->core_dll_config); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 67462306a36Sopenharmony_ci msm_offset->core_dll_config); 67562306a36Sopenharmony_ci config |= CORE_DLL_PDN; 67662306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 67762306a36Sopenharmony_ci msm_offset->core_dll_config); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (!msm_host->dll_config) 68062306a36Sopenharmony_ci msm_cm_dll_set_freq(host); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (msm_host->use_14lpp_dll_reset && 68362306a36Sopenharmony_ci !IS_ERR_OR_NULL(msm_host->xo_clk)) { 68462306a36Sopenharmony_ci u32 mclk_freq = 0; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 68762306a36Sopenharmony_ci msm_offset->core_dll_config_2); 68862306a36Sopenharmony_ci config &= CORE_FLL_CYCLE_CNT; 68962306a36Sopenharmony_ci if (config) 69062306a36Sopenharmony_ci mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 8), 69162306a36Sopenharmony_ci xo_clk); 69262306a36Sopenharmony_ci else 69362306a36Sopenharmony_ci mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 4), 69462306a36Sopenharmony_ci xo_clk); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 69762306a36Sopenharmony_ci msm_offset->core_dll_config_2); 69862306a36Sopenharmony_ci config &= ~(0xFF << 10); 69962306a36Sopenharmony_ci config |= mclk_freq << 10; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 70262306a36Sopenharmony_ci msm_offset->core_dll_config_2); 70362306a36Sopenharmony_ci /* wait for 5us before enabling DLL clock */ 70462306a36Sopenharmony_ci udelay(5); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 70862306a36Sopenharmony_ci msm_offset->core_dll_config); 70962306a36Sopenharmony_ci config &= ~CORE_DLL_RST; 71062306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 71162306a36Sopenharmony_ci msm_offset->core_dll_config); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 71462306a36Sopenharmony_ci msm_offset->core_dll_config); 71562306a36Sopenharmony_ci config &= ~CORE_DLL_PDN; 71662306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 71762306a36Sopenharmony_ci msm_offset->core_dll_config); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (msm_host->use_14lpp_dll_reset) { 72062306a36Sopenharmony_ci if (!msm_host->dll_config) 72162306a36Sopenharmony_ci msm_cm_dll_set_freq(host); 72262306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 72362306a36Sopenharmony_ci msm_offset->core_dll_config_2); 72462306a36Sopenharmony_ci config &= ~CORE_DLL_CLOCK_DISABLE; 72562306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 72662306a36Sopenharmony_ci msm_offset->core_dll_config_2); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* 73062306a36Sopenharmony_ci * Configure DLL user control register to enable DLL status. 73162306a36Sopenharmony_ci * This setting is applicable to SDCC v5.1 onwards only. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci if (msm_host->uses_tassadar_dll) { 73462306a36Sopenharmony_ci config = DLL_USR_CTL_POR_VAL | FINE_TUNE_MODE_EN | 73562306a36Sopenharmony_ci ENABLE_DLL_LOCK_STATUS | BIAS_OK_SIGNAL; 73662306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 73762306a36Sopenharmony_ci msm_offset->core_dll_usr_ctl); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 74062306a36Sopenharmony_ci msm_offset->core_dll_config_3); 74162306a36Sopenharmony_ci config &= ~0xFF; 74262306a36Sopenharmony_ci if (msm_host->clk_rate < 150000000) 74362306a36Sopenharmony_ci config |= DLL_CONFIG_3_LOW_FREQ_VAL; 74462306a36Sopenharmony_ci else 74562306a36Sopenharmony_ci config |= DLL_CONFIG_3_HIGH_FREQ_VAL; 74662306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 74762306a36Sopenharmony_ci msm_offset->core_dll_config_3); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 75162306a36Sopenharmony_ci msm_offset->core_dll_config); 75262306a36Sopenharmony_ci config |= CORE_DLL_EN; 75362306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 75462306a36Sopenharmony_ci msm_offset->core_dll_config); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 75762306a36Sopenharmony_ci msm_offset->core_dll_config); 75862306a36Sopenharmony_ci config |= CORE_CK_OUT_EN; 75962306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 76062306a36Sopenharmony_ci msm_offset->core_dll_config); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ 76362306a36Sopenharmony_ci while (!(readl_relaxed(host->ioaddr + msm_offset->core_dll_status) & 76462306a36Sopenharmony_ci CORE_DLL_LOCK)) { 76562306a36Sopenharmony_ci /* max. wait for 50us sec for LOCK bit to be set */ 76662306a36Sopenharmony_ci if (--wait_cnt == 0) { 76762306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n", 76862306a36Sopenharmony_ci mmc_hostname(mmc)); 76962306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 77062306a36Sopenharmony_ci return -ETIMEDOUT; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci udelay(1); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 77662306a36Sopenharmony_ci return 0; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic void msm_hc_select_default(struct sdhci_host *host) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 78262306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 78362306a36Sopenharmony_ci u32 config; 78462306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 78562306a36Sopenharmony_ci msm_host->offset; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (!msm_host->use_cdclp533) { 78862306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 78962306a36Sopenharmony_ci msm_offset->core_vendor_spec3); 79062306a36Sopenharmony_ci config &= ~CORE_PWRSAVE_DLL; 79162306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 79262306a36Sopenharmony_ci msm_offset->core_vendor_spec3); 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); 79662306a36Sopenharmony_ci config &= ~CORE_HC_MCLK_SEL_MASK; 79762306a36Sopenharmony_ci config |= CORE_HC_MCLK_SEL_DFLT; 79862306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* 80162306a36Sopenharmony_ci * Disable HC_SELECT_IN to be able to use the UHS mode select 80262306a36Sopenharmony_ci * configuration from Host Control2 register for all other 80362306a36Sopenharmony_ci * modes. 80462306a36Sopenharmony_ci * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field 80562306a36Sopenharmony_ci * in VENDOR_SPEC_FUNC 80662306a36Sopenharmony_ci */ 80762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); 80862306a36Sopenharmony_ci config &= ~CORE_HC_SELECT_IN_EN; 80962306a36Sopenharmony_ci config &= ~CORE_HC_SELECT_IN_MASK; 81062306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* 81362306a36Sopenharmony_ci * Make sure above writes impacting free running MCLK are completed 81462306a36Sopenharmony_ci * before changing the clk_rate at GCC. 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_ci wmb(); 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic void msm_hc_select_hs400(struct sdhci_host *host) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 82262306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 82362306a36Sopenharmony_ci struct mmc_ios ios = host->mmc->ios; 82462306a36Sopenharmony_ci u32 config, dll_lock; 82562306a36Sopenharmony_ci int rc; 82662306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 82762306a36Sopenharmony_ci msm_host->offset; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Select the divided clock (free running MCLK/2) */ 83062306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); 83162306a36Sopenharmony_ci config &= ~CORE_HC_MCLK_SEL_MASK; 83262306a36Sopenharmony_ci config |= CORE_HC_MCLK_SEL_HS400; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); 83562306a36Sopenharmony_ci /* 83662306a36Sopenharmony_ci * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC 83762306a36Sopenharmony_ci * register 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci if ((msm_host->tuning_done || ios.enhanced_strobe) && 84062306a36Sopenharmony_ci !msm_host->calibration_done) { 84162306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 84262306a36Sopenharmony_ci msm_offset->core_vendor_spec); 84362306a36Sopenharmony_ci config |= CORE_HC_SELECT_IN_HS400; 84462306a36Sopenharmony_ci config |= CORE_HC_SELECT_IN_EN; 84562306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 84662306a36Sopenharmony_ci msm_offset->core_vendor_spec); 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci if (!msm_host->clk_rate && !msm_host->use_cdclp533) { 84962306a36Sopenharmony_ci /* 85062306a36Sopenharmony_ci * Poll on DLL_LOCK or DDR_DLL_LOCK bits in 85162306a36Sopenharmony_ci * core_dll_status to be set. This should get set 85262306a36Sopenharmony_ci * within 15 us at 200 MHz. 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_ci rc = readl_relaxed_poll_timeout(host->ioaddr + 85562306a36Sopenharmony_ci msm_offset->core_dll_status, 85662306a36Sopenharmony_ci dll_lock, 85762306a36Sopenharmony_ci (dll_lock & 85862306a36Sopenharmony_ci (CORE_DLL_LOCK | 85962306a36Sopenharmony_ci CORE_DDR_DLL_LOCK)), 10, 86062306a36Sopenharmony_ci 1000); 86162306a36Sopenharmony_ci if (rc == -ETIMEDOUT) 86262306a36Sopenharmony_ci pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n", 86362306a36Sopenharmony_ci mmc_hostname(host->mmc), dll_lock); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci /* 86662306a36Sopenharmony_ci * Make sure above writes impacting free running MCLK are completed 86762306a36Sopenharmony_ci * before changing the clk_rate at GCC. 86862306a36Sopenharmony_ci */ 86962306a36Sopenharmony_ci wmb(); 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci/* 87362306a36Sopenharmony_ci * sdhci_msm_hc_select_mode :- In general all timing modes are 87462306a36Sopenharmony_ci * controlled via UHS mode select in Host Control2 register. 87562306a36Sopenharmony_ci * eMMC specific HS200/HS400 doesn't have their respective modes 87662306a36Sopenharmony_ci * defined here, hence we use these values. 87762306a36Sopenharmony_ci * 87862306a36Sopenharmony_ci * HS200 - SDR104 (Since they both are equivalent in functionality) 87962306a36Sopenharmony_ci * HS400 - This involves multiple configurations 88062306a36Sopenharmony_ci * Initially SDR104 - when tuning is required as HS200 88162306a36Sopenharmony_ci * Then when switching to DDR @ 400MHz (HS400) we use 88262306a36Sopenharmony_ci * the vendor specific HC_SELECT_IN to control the mode. 88362306a36Sopenharmony_ci * 88462306a36Sopenharmony_ci * In addition to controlling the modes we also need to select the 88562306a36Sopenharmony_ci * correct input clock for DLL depending on the mode. 88662306a36Sopenharmony_ci * 88762306a36Sopenharmony_ci * HS400 - divided clock (free running MCLK/2) 88862306a36Sopenharmony_ci * All other modes - default (free running MCLK) 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_cistatic void sdhci_msm_hc_select_mode(struct sdhci_host *host) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct mmc_ios ios = host->mmc->ios; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (ios.timing == MMC_TIMING_MMC_HS400 || 89562306a36Sopenharmony_ci host->flags & SDHCI_HS400_TUNING) 89662306a36Sopenharmony_ci msm_hc_select_hs400(host); 89762306a36Sopenharmony_ci else 89862306a36Sopenharmony_ci msm_hc_select_default(host); 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 90462306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 90562306a36Sopenharmony_ci u32 config, calib_done; 90662306a36Sopenharmony_ci int ret; 90762306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 90862306a36Sopenharmony_ci msm_host->offset; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* 91362306a36Sopenharmony_ci * Retuning in HS400 (DDR mode) will fail, just reset the 91462306a36Sopenharmony_ci * tuning block and restore the saved tuning phase. 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_ci ret = msm_init_cm_dll(host); 91762306a36Sopenharmony_ci if (ret) 91862306a36Sopenharmony_ci goto out; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* Set the selected phase in delay line hw block */ 92162306a36Sopenharmony_ci ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); 92262306a36Sopenharmony_ci if (ret) 92362306a36Sopenharmony_ci goto out; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); 92662306a36Sopenharmony_ci config |= CORE_CMD_DAT_TRACK_SEL; 92762306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); 93062306a36Sopenharmony_ci config &= ~CORE_CDC_T4_DLY_SEL; 93162306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG); 93462306a36Sopenharmony_ci config &= ~CORE_CDC_SWITCH_BYPASS_OFF; 93562306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG); 93862306a36Sopenharmony_ci config |= CORE_CDC_SWITCH_RC_EN; 93962306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); 94262306a36Sopenharmony_ci config &= ~CORE_START_CDC_TRAFFIC; 94362306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* Perform CDC Register Initialization Sequence */ 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci writel_relaxed(0x11800EC, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0); 94862306a36Sopenharmony_ci writel_relaxed(0x3011111, host->ioaddr + CORE_CSR_CDC_CTLR_CFG1); 94962306a36Sopenharmony_ci writel_relaxed(0x1201000, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0); 95062306a36Sopenharmony_ci writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1); 95162306a36Sopenharmony_ci writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG); 95262306a36Sopenharmony_ci writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG); 95362306a36Sopenharmony_ci writel_relaxed(0x4E2, host->ioaddr + CORE_CSR_CDC_DELAY_CFG); 95462306a36Sopenharmony_ci writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG); 95562306a36Sopenharmony_ci writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* CDC HW Calibration */ 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0); 96062306a36Sopenharmony_ci config |= CORE_SW_TRIG_FULL_CALIB; 96162306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0); 96462306a36Sopenharmony_ci config &= ~CORE_SW_TRIG_FULL_CALIB; 96562306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0); 96862306a36Sopenharmony_ci config |= CORE_HW_AUTOCAL_ENA; 96962306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0); 97262306a36Sopenharmony_ci config |= CORE_TIMER_ENA; 97362306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout(host->ioaddr + CORE_CSR_CDC_STATUS0, 97662306a36Sopenharmony_ci calib_done, 97762306a36Sopenharmony_ci (calib_done & CORE_CALIBRATION_DONE), 97862306a36Sopenharmony_ci 1, 50); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (ret == -ETIMEDOUT) { 98162306a36Sopenharmony_ci pr_err("%s: %s: CDC calibration was not completed\n", 98262306a36Sopenharmony_ci mmc_hostname(host->mmc), __func__); 98362306a36Sopenharmony_ci goto out; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci ret = readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0) 98762306a36Sopenharmony_ci & CORE_CDC_ERROR_CODE_MASK; 98862306a36Sopenharmony_ci if (ret) { 98962306a36Sopenharmony_ci pr_err("%s: %s: CDC error code %d\n", 99062306a36Sopenharmony_ci mmc_hostname(host->mmc), __func__, ret); 99162306a36Sopenharmony_ci ret = -EINVAL; 99262306a36Sopenharmony_ci goto out; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); 99662306a36Sopenharmony_ci config |= CORE_START_CDC_TRAFFIC; 99762306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); 99862306a36Sopenharmony_ciout: 99962306a36Sopenharmony_ci pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc), 100062306a36Sopenharmony_ci __func__, ret); 100162306a36Sopenharmony_ci return ret; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 100762306a36Sopenharmony_ci u32 dll_status, config, ddr_cfg_offset; 100862306a36Sopenharmony_ci int ret; 100962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 101062306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 101162306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 101262306a36Sopenharmony_ci sdhci_priv_msm_offset(host); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci /* 101762306a36Sopenharmony_ci * Currently the core_ddr_config register defaults to desired 101862306a36Sopenharmony_ci * configuration on reset. Currently reprogramming the power on 101962306a36Sopenharmony_ci * reset (POR) value in case it might have been modified by 102062306a36Sopenharmony_ci * bootloaders. In the future, if this changes, then the desired 102162306a36Sopenharmony_ci * values will need to be programmed appropriately. 102262306a36Sopenharmony_ci */ 102362306a36Sopenharmony_ci if (msm_host->updated_ddr_cfg) 102462306a36Sopenharmony_ci ddr_cfg_offset = msm_offset->core_ddr_config; 102562306a36Sopenharmony_ci else 102662306a36Sopenharmony_ci ddr_cfg_offset = msm_offset->core_ddr_config_old; 102762306a36Sopenharmony_ci writel_relaxed(msm_host->ddr_config, host->ioaddr + ddr_cfg_offset); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (mmc->ios.enhanced_strobe) { 103062306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 103162306a36Sopenharmony_ci msm_offset->core_ddr_200_cfg); 103262306a36Sopenharmony_ci config |= CORE_CMDIN_RCLK_EN; 103362306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 103462306a36Sopenharmony_ci msm_offset->core_ddr_200_cfg); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2); 103862306a36Sopenharmony_ci config |= CORE_DDR_CAL_EN; 103962306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config_2); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout(host->ioaddr + 104262306a36Sopenharmony_ci msm_offset->core_dll_status, 104362306a36Sopenharmony_ci dll_status, 104462306a36Sopenharmony_ci (dll_status & CORE_DDR_DLL_LOCK), 104562306a36Sopenharmony_ci 10, 1000); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (ret == -ETIMEDOUT) { 104862306a36Sopenharmony_ci pr_err("%s: %s: CM_DLL_SDC4 calibration was not completed\n", 104962306a36Sopenharmony_ci mmc_hostname(host->mmc), __func__); 105062306a36Sopenharmony_ci goto out; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* 105462306a36Sopenharmony_ci * Set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3. 105562306a36Sopenharmony_ci * When MCLK is gated OFF, it is not gated for less than 0.5us 105662306a36Sopenharmony_ci * and MCLK must be switched on for at-least 1us before DATA 105762306a36Sopenharmony_ci * starts coming. Controllers with 14lpp and later tech DLL cannot 105862306a36Sopenharmony_ci * guarantee above requirement. So PWRSAVE_DLL should not be 105962306a36Sopenharmony_ci * turned on for host controllers using this DLL. 106062306a36Sopenharmony_ci */ 106162306a36Sopenharmony_ci if (!msm_host->use_14lpp_dll_reset) { 106262306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 106362306a36Sopenharmony_ci msm_offset->core_vendor_spec3); 106462306a36Sopenharmony_ci config |= CORE_PWRSAVE_DLL; 106562306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 106662306a36Sopenharmony_ci msm_offset->core_vendor_spec3); 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* 107062306a36Sopenharmony_ci * Drain writebuffer to ensure above DLL calibration 107162306a36Sopenharmony_ci * and PWRSAVE DLL is enabled. 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ci wmb(); 107462306a36Sopenharmony_ciout: 107562306a36Sopenharmony_ci pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc), 107662306a36Sopenharmony_ci __func__, ret); 107762306a36Sopenharmony_ci return ret; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 108362306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 108462306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 108562306a36Sopenharmony_ci int ret; 108662306a36Sopenharmony_ci u32 config; 108762306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 108862306a36Sopenharmony_ci msm_host->offset; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* 109362306a36Sopenharmony_ci * Retuning in HS400 (DDR mode) will fail, just reset the 109462306a36Sopenharmony_ci * tuning block and restore the saved tuning phase. 109562306a36Sopenharmony_ci */ 109662306a36Sopenharmony_ci ret = msm_init_cm_dll(host); 109762306a36Sopenharmony_ci if (ret) 109862306a36Sopenharmony_ci goto out; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (!mmc->ios.enhanced_strobe) { 110162306a36Sopenharmony_ci /* Set the selected phase in delay line hw block */ 110262306a36Sopenharmony_ci ret = msm_config_cm_dll_phase(host, 110362306a36Sopenharmony_ci msm_host->saved_tuning_phase); 110462306a36Sopenharmony_ci if (ret) 110562306a36Sopenharmony_ci goto out; 110662306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 110762306a36Sopenharmony_ci msm_offset->core_dll_config); 110862306a36Sopenharmony_ci config |= CORE_CMD_DAT_TRACK_SEL; 110962306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 111062306a36Sopenharmony_ci msm_offset->core_dll_config); 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (msm_host->use_cdclp533) 111462306a36Sopenharmony_ci ret = sdhci_msm_cdclp533_calibration(host); 111562306a36Sopenharmony_ci else 111662306a36Sopenharmony_ci ret = sdhci_msm_cm_dll_sdc4_calibration(host); 111762306a36Sopenharmony_ciout: 111862306a36Sopenharmony_ci pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc), 111962306a36Sopenharmony_ci __func__, ret); 112062306a36Sopenharmony_ci return ret; 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic bool sdhci_msm_is_tuning_needed(struct sdhci_host *host) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct mmc_ios *ios = &host->mmc->ios; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci /* 112862306a36Sopenharmony_ci * Tuning is required for SDR104, HS200 and HS400 cards and 112962306a36Sopenharmony_ci * if clock frequency is greater than 100MHz in these modes. 113062306a36Sopenharmony_ci */ 113162306a36Sopenharmony_ci if (host->clock <= CORE_FREQ_100MHZ || 113262306a36Sopenharmony_ci !(ios->timing == MMC_TIMING_MMC_HS400 || 113362306a36Sopenharmony_ci ios->timing == MMC_TIMING_MMC_HS200 || 113462306a36Sopenharmony_ci ios->timing == MMC_TIMING_UHS_SDR104) || 113562306a36Sopenharmony_ci ios->enhanced_strobe) 113662306a36Sopenharmony_ci return false; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci return true; 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic int sdhci_msm_restore_sdr_dll_config(struct sdhci_host *host) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 114462306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 114562306a36Sopenharmony_ci int ret; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* 114862306a36Sopenharmony_ci * SDR DLL comes into picture only for timing modes which needs 114962306a36Sopenharmony_ci * tuning. 115062306a36Sopenharmony_ci */ 115162306a36Sopenharmony_ci if (!sdhci_msm_is_tuning_needed(host)) 115262306a36Sopenharmony_ci return 0; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci /* Reset the tuning block */ 115562306a36Sopenharmony_ci ret = msm_init_cm_dll(host); 115662306a36Sopenharmony_ci if (ret) 115762306a36Sopenharmony_ci return ret; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci /* Restore the tuning block */ 116062306a36Sopenharmony_ci ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return ret; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic void sdhci_msm_set_cdr(struct sdhci_host *host, bool enable) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = sdhci_priv_msm_offset(host); 116862306a36Sopenharmony_ci u32 config, oldconfig = readl_relaxed(host->ioaddr + 116962306a36Sopenharmony_ci msm_offset->core_dll_config); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci config = oldconfig; 117262306a36Sopenharmony_ci if (enable) { 117362306a36Sopenharmony_ci config |= CORE_CDR_EN; 117462306a36Sopenharmony_ci config &= ~CORE_CDR_EXT_EN; 117562306a36Sopenharmony_ci } else { 117662306a36Sopenharmony_ci config &= ~CORE_CDR_EN; 117762306a36Sopenharmony_ci config |= CORE_CDR_EXT_EN; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (config != oldconfig) { 118162306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 118262306a36Sopenharmony_ci msm_offset->core_dll_config); 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 118962306a36Sopenharmony_ci int tuning_seq_cnt = 10; 119062306a36Sopenharmony_ci u8 phase, tuned_phases[16], tuned_phase_cnt = 0; 119162306a36Sopenharmony_ci int rc; 119262306a36Sopenharmony_ci struct mmc_ios ios = host->mmc->ios; 119362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 119462306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (!sdhci_msm_is_tuning_needed(host)) { 119762306a36Sopenharmony_ci msm_host->use_cdr = false; 119862306a36Sopenharmony_ci sdhci_msm_set_cdr(host, false); 119962306a36Sopenharmony_ci return 0; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* Clock-Data-Recovery used to dynamically adjust RX sampling point */ 120362306a36Sopenharmony_ci msm_host->use_cdr = true; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* 120662306a36Sopenharmony_ci * Clear tuning_done flag before tuning to ensure proper 120762306a36Sopenharmony_ci * HS400 settings. 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_ci msm_host->tuning_done = 0; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * For HS400 tuning in HS200 timing requires: 121362306a36Sopenharmony_ci * - select MCLK/2 in VENDOR_SPEC 121462306a36Sopenharmony_ci * - program MCLK to 400MHz (or nearest supported) in GCC 121562306a36Sopenharmony_ci */ 121662306a36Sopenharmony_ci if (host->flags & SDHCI_HS400_TUNING) { 121762306a36Sopenharmony_ci sdhci_msm_hc_select_mode(host); 121862306a36Sopenharmony_ci msm_set_clock_rate_for_bus_mode(host, ios.clock); 121962306a36Sopenharmony_ci host->flags &= ~SDHCI_HS400_TUNING; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ciretry: 122362306a36Sopenharmony_ci /* First of all reset the tuning block */ 122462306a36Sopenharmony_ci rc = msm_init_cm_dll(host); 122562306a36Sopenharmony_ci if (rc) 122662306a36Sopenharmony_ci return rc; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci phase = 0; 122962306a36Sopenharmony_ci do { 123062306a36Sopenharmony_ci /* Set the phase in delay line hw block */ 123162306a36Sopenharmony_ci rc = msm_config_cm_dll_phase(host, phase); 123262306a36Sopenharmony_ci if (rc) 123362306a36Sopenharmony_ci return rc; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci rc = mmc_send_tuning(mmc, opcode, NULL); 123662306a36Sopenharmony_ci if (!rc) { 123762306a36Sopenharmony_ci /* Tuning is successful at this tuning point */ 123862306a36Sopenharmony_ci tuned_phases[tuned_phase_cnt++] = phase; 123962306a36Sopenharmony_ci dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", 124062306a36Sopenharmony_ci mmc_hostname(mmc), phase); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci } while (++phase < ARRAY_SIZE(tuned_phases)); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (tuned_phase_cnt) { 124562306a36Sopenharmony_ci if (tuned_phase_cnt == ARRAY_SIZE(tuned_phases)) { 124662306a36Sopenharmony_ci /* 124762306a36Sopenharmony_ci * All phases valid is _almost_ as bad as no phases 124862306a36Sopenharmony_ci * valid. Probably all phases are not really reliable 124962306a36Sopenharmony_ci * but we didn't detect where the unreliable place is. 125062306a36Sopenharmony_ci * That means we'll essentially be guessing and hoping 125162306a36Sopenharmony_ci * we get a good phase. Better to try a few times. 125262306a36Sopenharmony_ci */ 125362306a36Sopenharmony_ci dev_dbg(mmc_dev(mmc), "%s: All phases valid; try again\n", 125462306a36Sopenharmony_ci mmc_hostname(mmc)); 125562306a36Sopenharmony_ci if (--tuning_seq_cnt) { 125662306a36Sopenharmony_ci tuned_phase_cnt = 0; 125762306a36Sopenharmony_ci goto retry; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci rc = msm_find_most_appropriate_phase(host, tuned_phases, 126262306a36Sopenharmony_ci tuned_phase_cnt); 126362306a36Sopenharmony_ci if (rc < 0) 126462306a36Sopenharmony_ci return rc; 126562306a36Sopenharmony_ci else 126662306a36Sopenharmony_ci phase = rc; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci /* 126962306a36Sopenharmony_ci * Finally set the selected phase in delay 127062306a36Sopenharmony_ci * line hw block. 127162306a36Sopenharmony_ci */ 127262306a36Sopenharmony_ci rc = msm_config_cm_dll_phase(host, phase); 127362306a36Sopenharmony_ci if (rc) 127462306a36Sopenharmony_ci return rc; 127562306a36Sopenharmony_ci msm_host->saved_tuning_phase = phase; 127662306a36Sopenharmony_ci dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", 127762306a36Sopenharmony_ci mmc_hostname(mmc), phase); 127862306a36Sopenharmony_ci } else { 127962306a36Sopenharmony_ci if (--tuning_seq_cnt) 128062306a36Sopenharmony_ci goto retry; 128162306a36Sopenharmony_ci /* Tuning failed */ 128262306a36Sopenharmony_ci dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n", 128362306a36Sopenharmony_ci mmc_hostname(mmc)); 128462306a36Sopenharmony_ci rc = -EIO; 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci if (!rc) 128862306a36Sopenharmony_ci msm_host->tuning_done = true; 128962306a36Sopenharmony_ci return rc; 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci/* 129362306a36Sopenharmony_ci * sdhci_msm_hs400 - Calibrate the DLL for HS400 bus speed mode operation. 129462306a36Sopenharmony_ci * This needs to be done for both tuning and enhanced_strobe mode. 129562306a36Sopenharmony_ci * DLL operation is only needed for clock > 100MHz. For clock <= 100MHz 129662306a36Sopenharmony_ci * fixed feedback clock is used. 129762306a36Sopenharmony_ci */ 129862306a36Sopenharmony_cistatic void sdhci_msm_hs400(struct sdhci_host *host, struct mmc_ios *ios) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 130162306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 130262306a36Sopenharmony_ci int ret; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (host->clock > CORE_FREQ_100MHZ && 130562306a36Sopenharmony_ci (msm_host->tuning_done || ios->enhanced_strobe) && 130662306a36Sopenharmony_ci !msm_host->calibration_done) { 130762306a36Sopenharmony_ci ret = sdhci_msm_hs400_dll_calibration(host); 130862306a36Sopenharmony_ci if (!ret) 130962306a36Sopenharmony_ci msm_host->calibration_done = true; 131062306a36Sopenharmony_ci else 131162306a36Sopenharmony_ci pr_err("%s: Failed to calibrate DLL for hs400 mode (%d)\n", 131262306a36Sopenharmony_ci mmc_hostname(host->mmc), ret); 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, 131762306a36Sopenharmony_ci unsigned int uhs) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 132062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 132162306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 132262306a36Sopenharmony_ci u16 ctrl_2; 132362306a36Sopenharmony_ci u32 config; 132462306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 132562306a36Sopenharmony_ci msm_host->offset; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 132862306a36Sopenharmony_ci /* Select Bus Speed Mode for host */ 132962306a36Sopenharmony_ci ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 133062306a36Sopenharmony_ci switch (uhs) { 133162306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR12: 133262306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR12; 133362306a36Sopenharmony_ci break; 133462306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR25: 133562306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR50: 133862306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR50; 133962306a36Sopenharmony_ci break; 134062306a36Sopenharmony_ci case MMC_TIMING_MMC_HS400: 134162306a36Sopenharmony_ci case MMC_TIMING_MMC_HS200: 134262306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR104: 134362306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 134462306a36Sopenharmony_ci break; 134562306a36Sopenharmony_ci case MMC_TIMING_UHS_DDR50: 134662306a36Sopenharmony_ci case MMC_TIMING_MMC_DDR52: 134762306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 134862306a36Sopenharmony_ci break; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci /* 135262306a36Sopenharmony_ci * When clock frequency is less than 100MHz, the feedback clock must be 135362306a36Sopenharmony_ci * provided and DLL must not be used so that tuning can be skipped. To 135462306a36Sopenharmony_ci * provide feedback clock, the mode selection can be any value less 135562306a36Sopenharmony_ci * than 3'b011 in bits [2:0] of HOST CONTROL2 register. 135662306a36Sopenharmony_ci */ 135762306a36Sopenharmony_ci if (host->clock <= CORE_FREQ_100MHZ) { 135862306a36Sopenharmony_ci if (uhs == MMC_TIMING_MMC_HS400 || 135962306a36Sopenharmony_ci uhs == MMC_TIMING_MMC_HS200 || 136062306a36Sopenharmony_ci uhs == MMC_TIMING_UHS_SDR104) 136162306a36Sopenharmony_ci ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 136262306a36Sopenharmony_ci /* 136362306a36Sopenharmony_ci * DLL is not required for clock <= 100MHz 136462306a36Sopenharmony_ci * Thus, make sure DLL it is disabled when not required 136562306a36Sopenharmony_ci */ 136662306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 136762306a36Sopenharmony_ci msm_offset->core_dll_config); 136862306a36Sopenharmony_ci config |= CORE_DLL_RST; 136962306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 137062306a36Sopenharmony_ci msm_offset->core_dll_config); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 137362306a36Sopenharmony_ci msm_offset->core_dll_config); 137462306a36Sopenharmony_ci config |= CORE_DLL_PDN; 137562306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 137662306a36Sopenharmony_ci msm_offset->core_dll_config); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* 137962306a36Sopenharmony_ci * The DLL needs to be restored and CDCLP533 recalibrated 138062306a36Sopenharmony_ci * when the clock frequency is set back to 400MHz. 138162306a36Sopenharmony_ci */ 138262306a36Sopenharmony_ci msm_host->calibration_done = false; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n", 138662306a36Sopenharmony_ci mmc_hostname(host->mmc), host->clock, uhs, ctrl_2); 138762306a36Sopenharmony_ci sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci if (mmc->ios.timing == MMC_TIMING_MMC_HS400) 139062306a36Sopenharmony_ci sdhci_msm_hs400(host, &mmc->ios); 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic int sdhci_msm_set_pincfg(struct sdhci_msm_host *msm_host, bool level) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci struct platform_device *pdev = msm_host->pdev; 139662306a36Sopenharmony_ci int ret; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci if (level) 139962306a36Sopenharmony_ci ret = pinctrl_pm_select_default_state(&pdev->dev); 140062306a36Sopenharmony_ci else 140162306a36Sopenharmony_ci ret = pinctrl_pm_select_sleep_state(&pdev->dev); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci return ret; 140462306a36Sopenharmony_ci} 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_cistatic int sdhci_msm_set_vmmc(struct mmc_host *mmc) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci if (IS_ERR(mmc->supply.vmmc)) 140962306a36Sopenharmony_ci return 0; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci return mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, mmc->ios.vdd); 141262306a36Sopenharmony_ci} 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_cistatic int msm_toggle_vqmmc(struct sdhci_msm_host *msm_host, 141562306a36Sopenharmony_ci struct mmc_host *mmc, bool level) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci int ret; 141862306a36Sopenharmony_ci struct mmc_ios ios; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (msm_host->vqmmc_enabled == level) 142162306a36Sopenharmony_ci return 0; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (level) { 142462306a36Sopenharmony_ci /* Set the IO voltage regulator to default voltage level */ 142562306a36Sopenharmony_ci if (msm_host->caps_0 & CORE_3_0V_SUPPORT) 142662306a36Sopenharmony_ci ios.signal_voltage = MMC_SIGNAL_VOLTAGE_330; 142762306a36Sopenharmony_ci else if (msm_host->caps_0 & CORE_1_8V_SUPPORT) 142862306a36Sopenharmony_ci ios.signal_voltage = MMC_SIGNAL_VOLTAGE_180; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { 143162306a36Sopenharmony_ci ret = mmc_regulator_set_vqmmc(mmc, &ios); 143262306a36Sopenharmony_ci if (ret < 0) { 143362306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: vqmmc set volgate failed: %d\n", 143462306a36Sopenharmony_ci mmc_hostname(mmc), ret); 143562306a36Sopenharmony_ci goto out; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci ret = regulator_enable(mmc->supply.vqmmc); 143962306a36Sopenharmony_ci } else { 144062306a36Sopenharmony_ci ret = regulator_disable(mmc->supply.vqmmc); 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci if (ret) 144462306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: vqmm %sable failed: %d\n", 144562306a36Sopenharmony_ci mmc_hostname(mmc), level ? "en":"dis", ret); 144662306a36Sopenharmony_ci else 144762306a36Sopenharmony_ci msm_host->vqmmc_enabled = level; 144862306a36Sopenharmony_ciout: 144962306a36Sopenharmony_ci return ret; 145062306a36Sopenharmony_ci} 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_cistatic int msm_config_vqmmc_mode(struct sdhci_msm_host *msm_host, 145362306a36Sopenharmony_ci struct mmc_host *mmc, bool hpm) 145462306a36Sopenharmony_ci{ 145562306a36Sopenharmony_ci int load, ret; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci load = hpm ? MMC_VQMMC_MAX_LOAD_UA : 0; 145862306a36Sopenharmony_ci ret = regulator_set_load(mmc->supply.vqmmc, load); 145962306a36Sopenharmony_ci if (ret) 146062306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: vqmmc set load failed: %d\n", 146162306a36Sopenharmony_ci mmc_hostname(mmc), ret); 146262306a36Sopenharmony_ci return ret; 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cistatic int sdhci_msm_set_vqmmc(struct sdhci_msm_host *msm_host, 146662306a36Sopenharmony_ci struct mmc_host *mmc, bool level) 146762306a36Sopenharmony_ci{ 146862306a36Sopenharmony_ci int ret; 146962306a36Sopenharmony_ci bool always_on; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci if (IS_ERR(mmc->supply.vqmmc) || 147262306a36Sopenharmony_ci (mmc->ios.power_mode == MMC_POWER_UNDEFINED)) 147362306a36Sopenharmony_ci return 0; 147462306a36Sopenharmony_ci /* 147562306a36Sopenharmony_ci * For eMMC don't turn off Vqmmc, Instead just configure it in LPM 147662306a36Sopenharmony_ci * and HPM modes by setting the corresponding load. 147762306a36Sopenharmony_ci * 147862306a36Sopenharmony_ci * Till eMMC is initialized (i.e. always_on == 0), just turn on/off 147962306a36Sopenharmony_ci * Vqmmc. Vqmmc gets turned off only if init fails and mmc_power_off 148062306a36Sopenharmony_ci * gets invoked. Once eMMC is initialized (i.e. always_on == 1), 148162306a36Sopenharmony_ci * Vqmmc should remain ON, So just set the load instead of turning it 148262306a36Sopenharmony_ci * off/on. 148362306a36Sopenharmony_ci */ 148462306a36Sopenharmony_ci always_on = !mmc_card_is_removable(mmc) && 148562306a36Sopenharmony_ci mmc->card && mmc_card_mmc(mmc->card); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (always_on) 148862306a36Sopenharmony_ci ret = msm_config_vqmmc_mode(msm_host, mmc, level); 148962306a36Sopenharmony_ci else 149062306a36Sopenharmony_ci ret = msm_toggle_vqmmc(msm_host, mmc, level); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci return ret; 149362306a36Sopenharmony_ci} 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_cistatic inline void sdhci_msm_init_pwr_irq_wait(struct sdhci_msm_host *msm_host) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci init_waitqueue_head(&msm_host->pwr_irq_wait); 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic inline void sdhci_msm_complete_pwr_irq_wait( 150162306a36Sopenharmony_ci struct sdhci_msm_host *msm_host) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci wake_up(&msm_host->pwr_irq_wait); 150462306a36Sopenharmony_ci} 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci/* 150762306a36Sopenharmony_ci * sdhci_msm_check_power_status API should be called when registers writes 150862306a36Sopenharmony_ci * which can toggle sdhci IO bus ON/OFF or change IO lines HIGH/LOW happens. 150962306a36Sopenharmony_ci * To what state the register writes will change the IO lines should be passed 151062306a36Sopenharmony_ci * as the argument req_type. This API will check whether the IO line's state 151162306a36Sopenharmony_ci * is already the expected state and will wait for power irq only if 151262306a36Sopenharmony_ci * power irq is expected to be triggered based on the current IO line state 151362306a36Sopenharmony_ci * and expected IO line state. 151462306a36Sopenharmony_ci */ 151562306a36Sopenharmony_cistatic void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 151862306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 151962306a36Sopenharmony_ci bool done = false; 152062306a36Sopenharmony_ci u32 val = SWITCHABLE_SIGNALING_VOLTAGE; 152162306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 152262306a36Sopenharmony_ci msm_host->offset; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", 152562306a36Sopenharmony_ci mmc_hostname(host->mmc), __func__, req_type, 152662306a36Sopenharmony_ci msm_host->curr_pwr_state, msm_host->curr_io_level); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci /* 152962306a36Sopenharmony_ci * The power interrupt will not be generated for signal voltage 153062306a36Sopenharmony_ci * switches if SWITCHABLE_SIGNALING_VOLTAGE in MCI_GENERICS is not set. 153162306a36Sopenharmony_ci * Since sdhci-msm-v5, this bit has been removed and SW must consider 153262306a36Sopenharmony_ci * it as always set. 153362306a36Sopenharmony_ci */ 153462306a36Sopenharmony_ci if (!msm_host->mci_removed) 153562306a36Sopenharmony_ci val = msm_host_readl(msm_host, host, 153662306a36Sopenharmony_ci msm_offset->core_generics); 153762306a36Sopenharmony_ci if ((req_type & REQ_IO_HIGH || req_type & REQ_IO_LOW) && 153862306a36Sopenharmony_ci !(val & SWITCHABLE_SIGNALING_VOLTAGE)) { 153962306a36Sopenharmony_ci return; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci /* 154362306a36Sopenharmony_ci * The IRQ for request type IO High/LOW will be generated when - 154462306a36Sopenharmony_ci * there is a state change in 1.8V enable bit (bit 3) of 154562306a36Sopenharmony_ci * SDHCI_HOST_CONTROL2 register. The reset state of that bit is 0 154662306a36Sopenharmony_ci * which indicates 3.3V IO voltage. So, when MMC core layer tries 154762306a36Sopenharmony_ci * to set it to 3.3V before card detection happens, the 154862306a36Sopenharmony_ci * IRQ doesn't get triggered as there is no state change in this bit. 154962306a36Sopenharmony_ci * The driver already handles this case by changing the IO voltage 155062306a36Sopenharmony_ci * level to high as part of controller power up sequence. Hence, check 155162306a36Sopenharmony_ci * for host->pwr to handle a case where IO voltage high request is 155262306a36Sopenharmony_ci * issued even before controller power up. 155362306a36Sopenharmony_ci */ 155462306a36Sopenharmony_ci if ((req_type & REQ_IO_HIGH) && !host->pwr) { 155562306a36Sopenharmony_ci pr_debug("%s: do not wait for power IRQ that never comes, req_type: %d\n", 155662306a36Sopenharmony_ci mmc_hostname(host->mmc), req_type); 155762306a36Sopenharmony_ci return; 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci if ((req_type & msm_host->curr_pwr_state) || 156062306a36Sopenharmony_ci (req_type & msm_host->curr_io_level)) 156162306a36Sopenharmony_ci done = true; 156262306a36Sopenharmony_ci /* 156362306a36Sopenharmony_ci * This is needed here to handle cases where register writes will 156462306a36Sopenharmony_ci * not change the current bus state or io level of the controller. 156562306a36Sopenharmony_ci * In this case, no power irq will be triggerred and we should 156662306a36Sopenharmony_ci * not wait. 156762306a36Sopenharmony_ci */ 156862306a36Sopenharmony_ci if (!done) { 156962306a36Sopenharmony_ci if (!wait_event_timeout(msm_host->pwr_irq_wait, 157062306a36Sopenharmony_ci msm_host->pwr_irq_flag, 157162306a36Sopenharmony_ci msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) 157262306a36Sopenharmony_ci dev_warn(&msm_host->pdev->dev, 157362306a36Sopenharmony_ci "%s: pwr_irq for req: (%d) timed out\n", 157462306a36Sopenharmony_ci mmc_hostname(host->mmc), req_type); 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc), 157762306a36Sopenharmony_ci __func__, req_type); 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_cistatic void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 158362306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 158462306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = 158562306a36Sopenharmony_ci msm_host->offset; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n", 158862306a36Sopenharmony_ci mmc_hostname(host->mmc), 158962306a36Sopenharmony_ci msm_host_readl(msm_host, host, msm_offset->core_pwrctl_status), 159062306a36Sopenharmony_ci msm_host_readl(msm_host, host, msm_offset->core_pwrctl_mask), 159162306a36Sopenharmony_ci msm_host_readl(msm_host, host, msm_offset->core_pwrctl_ctl)); 159262306a36Sopenharmony_ci} 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_cistatic void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) 159562306a36Sopenharmony_ci{ 159662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 159762306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 159862306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 159962306a36Sopenharmony_ci u32 irq_status, irq_ack = 0; 160062306a36Sopenharmony_ci int retry = 10, ret; 160162306a36Sopenharmony_ci u32 pwr_state = 0, io_level = 0; 160262306a36Sopenharmony_ci u32 config; 160362306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = msm_host->offset; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci irq_status = msm_host_readl(msm_host, host, 160662306a36Sopenharmony_ci msm_offset->core_pwrctl_status); 160762306a36Sopenharmony_ci irq_status &= INT_MASK; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci msm_host_writel(msm_host, irq_status, host, 161062306a36Sopenharmony_ci msm_offset->core_pwrctl_clear); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci /* 161362306a36Sopenharmony_ci * There is a rare HW scenario where the first clear pulse could be 161462306a36Sopenharmony_ci * lost when actual reset and clear/read of status register is 161562306a36Sopenharmony_ci * happening at a time. Hence, retry for at least 10 times to make 161662306a36Sopenharmony_ci * sure status register is cleared. Otherwise, this will result in 161762306a36Sopenharmony_ci * a spurious power IRQ resulting in system instability. 161862306a36Sopenharmony_ci */ 161962306a36Sopenharmony_ci while (irq_status & msm_host_readl(msm_host, host, 162062306a36Sopenharmony_ci msm_offset->core_pwrctl_status)) { 162162306a36Sopenharmony_ci if (retry == 0) { 162262306a36Sopenharmony_ci pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n", 162362306a36Sopenharmony_ci mmc_hostname(host->mmc), irq_status); 162462306a36Sopenharmony_ci sdhci_msm_dump_pwr_ctrl_regs(host); 162562306a36Sopenharmony_ci WARN_ON(1); 162662306a36Sopenharmony_ci break; 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci msm_host_writel(msm_host, irq_status, host, 162962306a36Sopenharmony_ci msm_offset->core_pwrctl_clear); 163062306a36Sopenharmony_ci retry--; 163162306a36Sopenharmony_ci udelay(10); 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci /* Handle BUS ON/OFF*/ 163562306a36Sopenharmony_ci if (irq_status & CORE_PWRCTL_BUS_ON) { 163662306a36Sopenharmony_ci pwr_state = REQ_BUS_ON; 163762306a36Sopenharmony_ci io_level = REQ_IO_HIGH; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci if (irq_status & CORE_PWRCTL_BUS_OFF) { 164062306a36Sopenharmony_ci pwr_state = REQ_BUS_OFF; 164162306a36Sopenharmony_ci io_level = REQ_IO_LOW; 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci if (pwr_state) { 164562306a36Sopenharmony_ci ret = sdhci_msm_set_vmmc(mmc); 164662306a36Sopenharmony_ci if (!ret) 164762306a36Sopenharmony_ci ret = sdhci_msm_set_vqmmc(msm_host, mmc, 164862306a36Sopenharmony_ci pwr_state & REQ_BUS_ON); 164962306a36Sopenharmony_ci if (!ret) 165062306a36Sopenharmony_ci ret = sdhci_msm_set_pincfg(msm_host, 165162306a36Sopenharmony_ci pwr_state & REQ_BUS_ON); 165262306a36Sopenharmony_ci if (!ret) 165362306a36Sopenharmony_ci irq_ack |= CORE_PWRCTL_BUS_SUCCESS; 165462306a36Sopenharmony_ci else 165562306a36Sopenharmony_ci irq_ack |= CORE_PWRCTL_BUS_FAIL; 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* Handle IO LOW/HIGH */ 165962306a36Sopenharmony_ci if (irq_status & CORE_PWRCTL_IO_LOW) 166062306a36Sopenharmony_ci io_level = REQ_IO_LOW; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci if (irq_status & CORE_PWRCTL_IO_HIGH) 166362306a36Sopenharmony_ci io_level = REQ_IO_HIGH; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci if (io_level) 166662306a36Sopenharmony_ci irq_ack |= CORE_PWRCTL_IO_SUCCESS; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci if (io_level && !IS_ERR(mmc->supply.vqmmc) && !pwr_state) { 166962306a36Sopenharmony_ci ret = mmc_regulator_set_vqmmc(mmc, &mmc->ios); 167062306a36Sopenharmony_ci if (ret < 0) { 167162306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "%s: IO_level setting failed(%d). signal_voltage: %d, vdd: %d irq_status: 0x%08x\n", 167262306a36Sopenharmony_ci mmc_hostname(mmc), ret, 167362306a36Sopenharmony_ci mmc->ios.signal_voltage, mmc->ios.vdd, 167462306a36Sopenharmony_ci irq_status); 167562306a36Sopenharmony_ci irq_ack |= CORE_PWRCTL_IO_FAIL; 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci /* 168062306a36Sopenharmony_ci * The driver has to acknowledge the interrupt, switch voltages and 168162306a36Sopenharmony_ci * report back if it succeded or not to this register. The voltage 168262306a36Sopenharmony_ci * switches are handled by the sdhci core, so just report success. 168362306a36Sopenharmony_ci */ 168462306a36Sopenharmony_ci msm_host_writel(msm_host, irq_ack, host, 168562306a36Sopenharmony_ci msm_offset->core_pwrctl_ctl); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci /* 168862306a36Sopenharmony_ci * If we don't have info regarding the voltage levels supported by 168962306a36Sopenharmony_ci * regulators, don't change the IO PAD PWR SWITCH. 169062306a36Sopenharmony_ci */ 169162306a36Sopenharmony_ci if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { 169262306a36Sopenharmony_ci u32 new_config; 169362306a36Sopenharmony_ci /* 169462306a36Sopenharmony_ci * We should unset IO PAD PWR switch only if the register write 169562306a36Sopenharmony_ci * can set IO lines high and the regulator also switches to 3 V. 169662306a36Sopenharmony_ci * Else, we should keep the IO PAD PWR switch set. 169762306a36Sopenharmony_ci * This is applicable to certain targets where eMMC vccq supply 169862306a36Sopenharmony_ci * is only 1.8V. In such targets, even during REQ_IO_HIGH, the 169962306a36Sopenharmony_ci * IO PAD PWR switch must be kept set to reflect actual 170062306a36Sopenharmony_ci * regulator voltage. This way, during initialization of 170162306a36Sopenharmony_ci * controllers with only 1.8V, we will set the IO PAD bit 170262306a36Sopenharmony_ci * without waiting for a REQ_IO_LOW. 170362306a36Sopenharmony_ci */ 170462306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 170562306a36Sopenharmony_ci msm_offset->core_vendor_spec); 170662306a36Sopenharmony_ci new_config = config; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci if ((io_level & REQ_IO_HIGH) && 170962306a36Sopenharmony_ci (msm_host->caps_0 & CORE_3_0V_SUPPORT)) 171062306a36Sopenharmony_ci new_config &= ~CORE_IO_PAD_PWR_SWITCH; 171162306a36Sopenharmony_ci else if ((io_level & REQ_IO_LOW) || 171262306a36Sopenharmony_ci (msm_host->caps_0 & CORE_1_8V_SUPPORT)) 171362306a36Sopenharmony_ci new_config |= CORE_IO_PAD_PWR_SWITCH; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci if (config ^ new_config) 171662306a36Sopenharmony_ci writel_relaxed(new_config, host->ioaddr + 171762306a36Sopenharmony_ci msm_offset->core_vendor_spec); 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci if (pwr_state) 172162306a36Sopenharmony_ci msm_host->curr_pwr_state = pwr_state; 172262306a36Sopenharmony_ci if (io_level) 172362306a36Sopenharmony_ci msm_host->curr_io_level = io_level; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci dev_dbg(mmc_dev(mmc), "%s: %s: Handled IRQ(%d), irq_status=0x%x, ack=0x%x\n", 172662306a36Sopenharmony_ci mmc_hostname(msm_host->mmc), __func__, irq, irq_status, 172762306a36Sopenharmony_ci irq_ack); 172862306a36Sopenharmony_ci} 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_cistatic irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) 173162306a36Sopenharmony_ci{ 173262306a36Sopenharmony_ci struct sdhci_host *host = (struct sdhci_host *)data; 173362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 173462306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci sdhci_msm_handle_pwr_irq(host, irq); 173762306a36Sopenharmony_ci msm_host->pwr_irq_flag = 1; 173862306a36Sopenharmony_ci sdhci_msm_complete_pwr_irq_wait(msm_host); 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci return IRQ_HANDLED; 174262306a36Sopenharmony_ci} 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_cistatic unsigned int sdhci_msm_get_max_clock(struct sdhci_host *host) 174562306a36Sopenharmony_ci{ 174662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 174762306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 174862306a36Sopenharmony_ci struct clk *core_clk = msm_host->bulk_clks[0].clk; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return clk_round_rate(core_clk, ULONG_MAX); 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_cistatic unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) 175462306a36Sopenharmony_ci{ 175562306a36Sopenharmony_ci return SDHCI_MSM_MIN_CLOCK; 175662306a36Sopenharmony_ci} 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci/* 175962306a36Sopenharmony_ci * __sdhci_msm_set_clock - sdhci_msm clock control. 176062306a36Sopenharmony_ci * 176162306a36Sopenharmony_ci * Description: 176262306a36Sopenharmony_ci * MSM controller does not use internal divider and 176362306a36Sopenharmony_ci * instead directly control the GCC clock as per 176462306a36Sopenharmony_ci * HW recommendation. 176562306a36Sopenharmony_ci **/ 176662306a36Sopenharmony_cistatic void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) 176762306a36Sopenharmony_ci{ 176862306a36Sopenharmony_ci u16 clk; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (clock == 0) 177362306a36Sopenharmony_ci return; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci /* 177662306a36Sopenharmony_ci * MSM controller do not use clock divider. 177762306a36Sopenharmony_ci * Thus read SDHCI_CLOCK_CONTROL and only enable 177862306a36Sopenharmony_ci * clock with no divider value programmed. 177962306a36Sopenharmony_ci */ 178062306a36Sopenharmony_ci clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 178162306a36Sopenharmony_ci sdhci_enable_clk(host, clk); 178262306a36Sopenharmony_ci} 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci/* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */ 178562306a36Sopenharmony_cistatic void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) 178662306a36Sopenharmony_ci{ 178762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 178862306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci if (!clock) { 179162306a36Sopenharmony_ci host->mmc->actual_clock = msm_host->clk_rate = 0; 179262306a36Sopenharmony_ci goto out; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci sdhci_msm_hc_select_mode(host); 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci msm_set_clock_rate_for_bus_mode(host, clock); 179862306a36Sopenharmony_ciout: 179962306a36Sopenharmony_ci __sdhci_msm_set_clock(host, clock); 180062306a36Sopenharmony_ci} 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci/*****************************************************************************\ 180362306a36Sopenharmony_ci * * 180462306a36Sopenharmony_ci * Inline Crypto Engine (ICE) support * 180562306a36Sopenharmony_ci * * 180662306a36Sopenharmony_ci\*****************************************************************************/ 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci#ifdef CONFIG_MMC_CRYPTO 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_cistatic int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, 181162306a36Sopenharmony_ci struct cqhci_host *cq_host) 181262306a36Sopenharmony_ci{ 181362306a36Sopenharmony_ci struct mmc_host *mmc = msm_host->mmc; 181462306a36Sopenharmony_ci struct device *dev = mmc_dev(mmc); 181562306a36Sopenharmony_ci struct qcom_ice *ice; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS)) 181862306a36Sopenharmony_ci return 0; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci ice = of_qcom_ice_get(dev); 182162306a36Sopenharmony_ci if (ice == ERR_PTR(-EOPNOTSUPP)) { 182262306a36Sopenharmony_ci dev_warn(dev, "Disabling inline encryption support\n"); 182362306a36Sopenharmony_ci ice = NULL; 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci if (IS_ERR_OR_NULL(ice)) 182762306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(ice); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci msm_host->ice = ice; 183062306a36Sopenharmony_ci mmc->caps2 |= MMC_CAP2_CRYPTO; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci return 0; 183362306a36Sopenharmony_ci} 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_cistatic void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host) 183662306a36Sopenharmony_ci{ 183762306a36Sopenharmony_ci if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO) 183862306a36Sopenharmony_ci qcom_ice_enable(msm_host->ice); 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_cistatic __maybe_unused int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO) 184462306a36Sopenharmony_ci return qcom_ice_resume(msm_host->ice); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci return 0; 184762306a36Sopenharmony_ci} 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_cistatic __maybe_unused int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host) 185062306a36Sopenharmony_ci{ 185162306a36Sopenharmony_ci if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO) 185262306a36Sopenharmony_ci return qcom_ice_suspend(msm_host->ice); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci return 0; 185562306a36Sopenharmony_ci} 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci/* 185862306a36Sopenharmony_ci * Program a key into a QC ICE keyslot, or evict a keyslot. QC ICE requires 185962306a36Sopenharmony_ci * vendor-specific SCM calls for this; it doesn't support the standard way. 186062306a36Sopenharmony_ci */ 186162306a36Sopenharmony_cistatic int sdhci_msm_program_key(struct cqhci_host *cq_host, 186262306a36Sopenharmony_ci const union cqhci_crypto_cfg_entry *cfg, 186362306a36Sopenharmony_ci int slot) 186462306a36Sopenharmony_ci{ 186562306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(cq_host->mmc); 186662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 186762306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 186862306a36Sopenharmony_ci union cqhci_crypto_cap_entry cap; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci /* Only AES-256-XTS has been tested so far. */ 187162306a36Sopenharmony_ci cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx]; 187262306a36Sopenharmony_ci if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS || 187362306a36Sopenharmony_ci cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256) 187462306a36Sopenharmony_ci return -EINVAL; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci if (cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE) 187762306a36Sopenharmony_ci return qcom_ice_program_key(msm_host->ice, 187862306a36Sopenharmony_ci QCOM_ICE_CRYPTO_ALG_AES_XTS, 187962306a36Sopenharmony_ci QCOM_ICE_CRYPTO_KEY_SIZE_256, 188062306a36Sopenharmony_ci cfg->crypto_key, 188162306a36Sopenharmony_ci cfg->data_unit_size, slot); 188262306a36Sopenharmony_ci else 188362306a36Sopenharmony_ci return qcom_ice_evict_key(msm_host->ice, slot); 188462306a36Sopenharmony_ci} 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci#else /* CONFIG_MMC_CRYPTO */ 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_cistatic inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, 188962306a36Sopenharmony_ci struct cqhci_host *cq_host) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci return 0; 189262306a36Sopenharmony_ci} 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_cistatic inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host) 189562306a36Sopenharmony_ci{ 189662306a36Sopenharmony_ci} 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_cistatic inline __maybe_unused int 189962306a36Sopenharmony_cisdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) 190062306a36Sopenharmony_ci{ 190162306a36Sopenharmony_ci return 0; 190262306a36Sopenharmony_ci} 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_cistatic inline __maybe_unused int 190562306a36Sopenharmony_cisdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host) 190662306a36Sopenharmony_ci{ 190762306a36Sopenharmony_ci return 0; 190862306a36Sopenharmony_ci} 190962306a36Sopenharmony_ci#endif /* !CONFIG_MMC_CRYPTO */ 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci/*****************************************************************************\ 191262306a36Sopenharmony_ci * * 191362306a36Sopenharmony_ci * MSM Command Queue Engine (CQE) * 191462306a36Sopenharmony_ci * * 191562306a36Sopenharmony_ci\*****************************************************************************/ 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_cistatic u32 sdhci_msm_cqe_irq(struct sdhci_host *host, u32 intmask) 191862306a36Sopenharmony_ci{ 191962306a36Sopenharmony_ci int cmd_error = 0; 192062306a36Sopenharmony_ci int data_error = 0; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) 192362306a36Sopenharmony_ci return intmask; 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci cqhci_irq(host->mmc, intmask, cmd_error, data_error); 192662306a36Sopenharmony_ci return 0; 192762306a36Sopenharmony_ci} 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_cistatic void sdhci_msm_cqe_enable(struct mmc_host *mmc) 193062306a36Sopenharmony_ci{ 193162306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 193262306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 193362306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci sdhci_cqe_enable(mmc); 193662306a36Sopenharmony_ci sdhci_msm_ice_enable(msm_host); 193762306a36Sopenharmony_ci} 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_cistatic void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 194262306a36Sopenharmony_ci unsigned long flags; 194362306a36Sopenharmony_ci u32 ctrl; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* 194662306a36Sopenharmony_ci * When CQE is halted, the legacy SDHCI path operates only 194762306a36Sopenharmony_ci * on 16-byte descriptors in 64bit mode. 194862306a36Sopenharmony_ci */ 194962306a36Sopenharmony_ci if (host->flags & SDHCI_USE_64_BIT_DMA) 195062306a36Sopenharmony_ci host->desc_sz = 16; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci /* 195562306a36Sopenharmony_ci * During CQE command transfers, command complete bit gets latched. 195662306a36Sopenharmony_ci * So s/w should clear command complete interrupt status when CQE is 195762306a36Sopenharmony_ci * either halted or disabled. Otherwise unexpected SDCHI legacy 195862306a36Sopenharmony_ci * interrupt gets triggered when CQE is halted/disabled. 195962306a36Sopenharmony_ci */ 196062306a36Sopenharmony_ci ctrl = sdhci_readl(host, SDHCI_INT_ENABLE); 196162306a36Sopenharmony_ci ctrl |= SDHCI_INT_RESPONSE; 196262306a36Sopenharmony_ci sdhci_writel(host, ctrl, SDHCI_INT_ENABLE); 196362306a36Sopenharmony_ci sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci sdhci_cqe_disable(mmc, recovery); 196862306a36Sopenharmony_ci} 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_cistatic void sdhci_msm_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) 197162306a36Sopenharmony_ci{ 197262306a36Sopenharmony_ci u32 count, start = 15; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci __sdhci_set_timeout(host, cmd); 197562306a36Sopenharmony_ci count = sdhci_readb(host, SDHCI_TIMEOUT_CONTROL); 197662306a36Sopenharmony_ci /* 197762306a36Sopenharmony_ci * Update software timeout value if its value is less than hardware data 197862306a36Sopenharmony_ci * timeout value. Qcom SoC hardware data timeout value was calculated 197962306a36Sopenharmony_ci * using 4 * MCLK * 2^(count + 13). where MCLK = 1 / host->clock. 198062306a36Sopenharmony_ci */ 198162306a36Sopenharmony_ci if (cmd && cmd->data && host->clock > 400000 && 198262306a36Sopenharmony_ci host->clock <= 50000000 && 198362306a36Sopenharmony_ci ((1 << (count + start)) > (10 * host->clock))) 198462306a36Sopenharmony_ci host->data_timeout = 22LL * NSEC_PER_SEC; 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_cistatic const struct cqhci_host_ops sdhci_msm_cqhci_ops = { 198862306a36Sopenharmony_ci .enable = sdhci_msm_cqe_enable, 198962306a36Sopenharmony_ci .disable = sdhci_msm_cqe_disable, 199062306a36Sopenharmony_ci#ifdef CONFIG_MMC_CRYPTO 199162306a36Sopenharmony_ci .program_key = sdhci_msm_program_key, 199262306a36Sopenharmony_ci#endif 199362306a36Sopenharmony_ci}; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_cistatic int sdhci_msm_cqe_add_host(struct sdhci_host *host, 199662306a36Sopenharmony_ci struct platform_device *pdev) 199762306a36Sopenharmony_ci{ 199862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 199962306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 200062306a36Sopenharmony_ci struct cqhci_host *cq_host; 200162306a36Sopenharmony_ci bool dma64; 200262306a36Sopenharmony_ci u32 cqcfg; 200362306a36Sopenharmony_ci int ret; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci /* 200662306a36Sopenharmony_ci * When CQE is halted, SDHC operates only on 16byte ADMA descriptors. 200762306a36Sopenharmony_ci * So ensure ADMA table is allocated for 16byte descriptors. 200862306a36Sopenharmony_ci */ 200962306a36Sopenharmony_ci if (host->caps & SDHCI_CAN_64BIT) 201062306a36Sopenharmony_ci host->alloc_desc_sz = 16; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci ret = sdhci_setup_host(host); 201362306a36Sopenharmony_ci if (ret) 201462306a36Sopenharmony_ci return ret; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci cq_host = cqhci_pltfm_init(pdev); 201762306a36Sopenharmony_ci if (IS_ERR(cq_host)) { 201862306a36Sopenharmony_ci ret = PTR_ERR(cq_host); 201962306a36Sopenharmony_ci dev_err(&pdev->dev, "cqhci-pltfm init: failed: %d\n", ret); 202062306a36Sopenharmony_ci goto cleanup; 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci msm_host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; 202462306a36Sopenharmony_ci cq_host->ops = &sdhci_msm_cqhci_ops; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci dma64 = host->flags & SDHCI_USE_64_BIT_DMA; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci ret = sdhci_msm_ice_init(msm_host, cq_host); 202962306a36Sopenharmony_ci if (ret) 203062306a36Sopenharmony_ci goto cleanup; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci ret = cqhci_init(cq_host, host->mmc, dma64); 203362306a36Sopenharmony_ci if (ret) { 203462306a36Sopenharmony_ci dev_err(&pdev->dev, "%s: CQE init: failed (%d)\n", 203562306a36Sopenharmony_ci mmc_hostname(host->mmc), ret); 203662306a36Sopenharmony_ci goto cleanup; 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci /* Disable cqe reset due to cqe enable signal */ 204062306a36Sopenharmony_ci cqcfg = cqhci_readl(cq_host, CQHCI_VENDOR_CFG1); 204162306a36Sopenharmony_ci cqcfg |= CQHCI_VENDOR_DIS_RST_ON_CQ_EN; 204262306a36Sopenharmony_ci cqhci_writel(cq_host, cqcfg, CQHCI_VENDOR_CFG1); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci /* 204562306a36Sopenharmony_ci * SDHC expects 12byte ADMA descriptors till CQE is enabled. 204662306a36Sopenharmony_ci * So limit desc_sz to 12 so that the data commands that are sent 204762306a36Sopenharmony_ci * during card initialization (before CQE gets enabled) would 204862306a36Sopenharmony_ci * get executed without any issues. 204962306a36Sopenharmony_ci */ 205062306a36Sopenharmony_ci if (host->flags & SDHCI_USE_64_BIT_DMA) 205162306a36Sopenharmony_ci host->desc_sz = 12; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci ret = __sdhci_add_host(host); 205462306a36Sopenharmony_ci if (ret) 205562306a36Sopenharmony_ci goto cleanup; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci dev_info(&pdev->dev, "%s: CQE init: success\n", 205862306a36Sopenharmony_ci mmc_hostname(host->mmc)); 205962306a36Sopenharmony_ci return ret; 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_cicleanup: 206262306a36Sopenharmony_ci sdhci_cleanup_host(host); 206362306a36Sopenharmony_ci return ret; 206462306a36Sopenharmony_ci} 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci/* 206762306a36Sopenharmony_ci * Platform specific register write functions. This is so that, if any 206862306a36Sopenharmony_ci * register write needs to be followed up by platform specific actions, 206962306a36Sopenharmony_ci * they can be added here. These functions can go to sleep when writes 207062306a36Sopenharmony_ci * to certain registers are done. 207162306a36Sopenharmony_ci * These functions are relying on sdhci_set_ios not using spinlock. 207262306a36Sopenharmony_ci */ 207362306a36Sopenharmony_cistatic int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg) 207462306a36Sopenharmony_ci{ 207562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 207662306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 207762306a36Sopenharmony_ci u32 req_type = 0; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci switch (reg) { 208062306a36Sopenharmony_ci case SDHCI_HOST_CONTROL2: 208162306a36Sopenharmony_ci req_type = (val & SDHCI_CTRL_VDD_180) ? REQ_IO_LOW : 208262306a36Sopenharmony_ci REQ_IO_HIGH; 208362306a36Sopenharmony_ci break; 208462306a36Sopenharmony_ci case SDHCI_SOFTWARE_RESET: 208562306a36Sopenharmony_ci if (host->pwr && (val & SDHCI_RESET_ALL)) 208662306a36Sopenharmony_ci req_type = REQ_BUS_OFF; 208762306a36Sopenharmony_ci break; 208862306a36Sopenharmony_ci case SDHCI_POWER_CONTROL: 208962306a36Sopenharmony_ci req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON; 209062306a36Sopenharmony_ci break; 209162306a36Sopenharmony_ci case SDHCI_TRANSFER_MODE: 209262306a36Sopenharmony_ci msm_host->transfer_mode = val; 209362306a36Sopenharmony_ci break; 209462306a36Sopenharmony_ci case SDHCI_COMMAND: 209562306a36Sopenharmony_ci if (!msm_host->use_cdr) 209662306a36Sopenharmony_ci break; 209762306a36Sopenharmony_ci if ((msm_host->transfer_mode & SDHCI_TRNS_READ) && 209862306a36Sopenharmony_ci !mmc_op_tuning(SDHCI_GET_CMD(val))) 209962306a36Sopenharmony_ci sdhci_msm_set_cdr(host, true); 210062306a36Sopenharmony_ci else 210162306a36Sopenharmony_ci sdhci_msm_set_cdr(host, false); 210262306a36Sopenharmony_ci break; 210362306a36Sopenharmony_ci } 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci if (req_type) { 210662306a36Sopenharmony_ci msm_host->pwr_irq_flag = 0; 210762306a36Sopenharmony_ci /* 210862306a36Sopenharmony_ci * Since this register write may trigger a power irq, ensure 210962306a36Sopenharmony_ci * all previous register writes are complete by this point. 211062306a36Sopenharmony_ci */ 211162306a36Sopenharmony_ci mb(); 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci return req_type; 211462306a36Sopenharmony_ci} 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci/* This function may sleep*/ 211762306a36Sopenharmony_cistatic void sdhci_msm_writew(struct sdhci_host *host, u16 val, int reg) 211862306a36Sopenharmony_ci{ 211962306a36Sopenharmony_ci u32 req_type = 0; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci req_type = __sdhci_msm_check_write(host, val, reg); 212262306a36Sopenharmony_ci writew_relaxed(val, host->ioaddr + reg); 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci if (req_type) 212562306a36Sopenharmony_ci sdhci_msm_check_power_status(host, req_type); 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci/* This function may sleep*/ 212962306a36Sopenharmony_cistatic void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci u32 req_type = 0; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci req_type = __sdhci_msm_check_write(host, val, reg); 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci writeb_relaxed(val, host->ioaddr + reg); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci if (req_type) 213862306a36Sopenharmony_ci sdhci_msm_check_power_status(host, req_type); 213962306a36Sopenharmony_ci} 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_cistatic void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) 214262306a36Sopenharmony_ci{ 214362306a36Sopenharmony_ci struct mmc_host *mmc = msm_host->mmc; 214462306a36Sopenharmony_ci struct regulator *supply = mmc->supply.vqmmc; 214562306a36Sopenharmony_ci u32 caps = 0, config; 214662306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 214762306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = msm_host->offset; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci if (!IS_ERR(mmc->supply.vqmmc)) { 215062306a36Sopenharmony_ci if (regulator_is_supported_voltage(supply, 1700000, 1950000)) 215162306a36Sopenharmony_ci caps |= CORE_1_8V_SUPPORT; 215262306a36Sopenharmony_ci if (regulator_is_supported_voltage(supply, 2700000, 3600000)) 215362306a36Sopenharmony_ci caps |= CORE_3_0V_SUPPORT; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci if (!caps) 215662306a36Sopenharmony_ci pr_warn("%s: 1.8/3V not supported for vqmmc\n", 215762306a36Sopenharmony_ci mmc_hostname(mmc)); 215862306a36Sopenharmony_ci } 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci if (caps) { 216162306a36Sopenharmony_ci /* 216262306a36Sopenharmony_ci * Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH 216362306a36Sopenharmony_ci * bit can be used as required later on. 216462306a36Sopenharmony_ci */ 216562306a36Sopenharmony_ci u32 io_level = msm_host->curr_io_level; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + 216862306a36Sopenharmony_ci msm_offset->core_vendor_spec); 216962306a36Sopenharmony_ci config |= CORE_IO_PAD_PWR_SWITCH_EN; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT)) 217262306a36Sopenharmony_ci config &= ~CORE_IO_PAD_PWR_SWITCH; 217362306a36Sopenharmony_ci else if ((io_level & REQ_IO_LOW) || (caps & CORE_1_8V_SUPPORT)) 217462306a36Sopenharmony_ci config |= CORE_IO_PAD_PWR_SWITCH; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci writel_relaxed(config, 217762306a36Sopenharmony_ci host->ioaddr + msm_offset->core_vendor_spec); 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci msm_host->caps_0 |= caps; 218062306a36Sopenharmony_ci pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); 218162306a36Sopenharmony_ci} 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_cistatic int sdhci_msm_register_vreg(struct sdhci_msm_host *msm_host) 218462306a36Sopenharmony_ci{ 218562306a36Sopenharmony_ci int ret; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci ret = mmc_regulator_get_supply(msm_host->mmc); 218862306a36Sopenharmony_ci if (ret) 218962306a36Sopenharmony_ci return ret; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci sdhci_msm_set_regulator_caps(msm_host); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci return 0; 219462306a36Sopenharmony_ci} 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_cistatic int sdhci_msm_start_signal_voltage_switch(struct mmc_host *mmc, 219762306a36Sopenharmony_ci struct mmc_ios *ios) 219862306a36Sopenharmony_ci{ 219962306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 220062306a36Sopenharmony_ci u16 ctrl, status; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci /* 220362306a36Sopenharmony_ci * Signal Voltage Switching is only applicable for Host Controllers 220462306a36Sopenharmony_ci * v3.00 and above. 220562306a36Sopenharmony_ci */ 220662306a36Sopenharmony_ci if (host->version < SDHCI_SPEC_300) 220762306a36Sopenharmony_ci return 0; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci switch (ios->signal_voltage) { 221262306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_330: 221362306a36Sopenharmony_ci if (!(host->flags & SDHCI_SIGNALING_330)) 221462306a36Sopenharmony_ci return -EINVAL; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci /* Set 1.8V Signal Enable in the Host Control2 register to 0 */ 221762306a36Sopenharmony_ci ctrl &= ~SDHCI_CTRL_VDD_180; 221862306a36Sopenharmony_ci break; 221962306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_180: 222062306a36Sopenharmony_ci if (!(host->flags & SDHCI_SIGNALING_180)) 222162306a36Sopenharmony_ci return -EINVAL; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci /* Enable 1.8V Signal Enable in the Host Control2 register */ 222462306a36Sopenharmony_ci ctrl |= SDHCI_CTRL_VDD_180; 222562306a36Sopenharmony_ci break; 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci default: 222862306a36Sopenharmony_ci return -EINVAL; 222962306a36Sopenharmony_ci } 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci /* Wait for 5ms */ 223462306a36Sopenharmony_ci usleep_range(5000, 5500); 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci /* regulator output should be stable within 5 ms */ 223762306a36Sopenharmony_ci status = ctrl & SDHCI_CTRL_VDD_180; 223862306a36Sopenharmony_ci ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); 223962306a36Sopenharmony_ci if ((ctrl & SDHCI_CTRL_VDD_180) == status) 224062306a36Sopenharmony_ci return 0; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci dev_warn(mmc_dev(mmc), "%s: Regulator output did not became stable\n", 224362306a36Sopenharmony_ci mmc_hostname(mmc)); 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci return -EAGAIN; 224662306a36Sopenharmony_ci} 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci#define DRIVER_NAME "sdhci_msm" 224962306a36Sopenharmony_ci#define SDHCI_MSM_DUMP(f, x...) \ 225062306a36Sopenharmony_ci pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x) 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_cistatic void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) 225362306a36Sopenharmony_ci{ 225462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 225562306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 225662306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset = msm_host->offset; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci SDHCI_MSM_DUMP("----------- VENDOR REGISTER DUMP -----------\n"); 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci SDHCI_MSM_DUMP( 226162306a36Sopenharmony_ci "DLL sts: 0x%08x | DLL cfg: 0x%08x | DLL cfg2: 0x%08x\n", 226262306a36Sopenharmony_ci readl_relaxed(host->ioaddr + msm_offset->core_dll_status), 226362306a36Sopenharmony_ci readl_relaxed(host->ioaddr + msm_offset->core_dll_config), 226462306a36Sopenharmony_ci readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2)); 226562306a36Sopenharmony_ci SDHCI_MSM_DUMP( 226662306a36Sopenharmony_ci "DLL cfg3: 0x%08x | DLL usr ctl: 0x%08x | DDR cfg: 0x%08x\n", 226762306a36Sopenharmony_ci readl_relaxed(host->ioaddr + msm_offset->core_dll_config_3), 226862306a36Sopenharmony_ci readl_relaxed(host->ioaddr + msm_offset->core_dll_usr_ctl), 226962306a36Sopenharmony_ci readl_relaxed(host->ioaddr + msm_offset->core_ddr_config)); 227062306a36Sopenharmony_ci SDHCI_MSM_DUMP( 227162306a36Sopenharmony_ci "Vndr func: 0x%08x | Vndr func2 : 0x%08x Vndr func3: 0x%08x\n", 227262306a36Sopenharmony_ci readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec), 227362306a36Sopenharmony_ci readl_relaxed(host->ioaddr + 227462306a36Sopenharmony_ci msm_offset->core_vendor_spec_func2), 227562306a36Sopenharmony_ci readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3)); 227662306a36Sopenharmony_ci} 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_cistatic const struct sdhci_msm_variant_ops mci_var_ops = { 227962306a36Sopenharmony_ci .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, 228062306a36Sopenharmony_ci .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, 228162306a36Sopenharmony_ci}; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_cistatic const struct sdhci_msm_variant_ops v5_var_ops = { 228462306a36Sopenharmony_ci .msm_readl_relaxed = sdhci_msm_v5_variant_readl_relaxed, 228562306a36Sopenharmony_ci .msm_writel_relaxed = sdhci_msm_v5_variant_writel_relaxed, 228662306a36Sopenharmony_ci}; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_cistatic const struct sdhci_msm_variant_info sdhci_msm_mci_var = { 228962306a36Sopenharmony_ci .var_ops = &mci_var_ops, 229062306a36Sopenharmony_ci .offset = &sdhci_msm_mci_offset, 229162306a36Sopenharmony_ci}; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_cistatic const struct sdhci_msm_variant_info sdhci_msm_v5_var = { 229462306a36Sopenharmony_ci .mci_removed = true, 229562306a36Sopenharmony_ci .var_ops = &v5_var_ops, 229662306a36Sopenharmony_ci .offset = &sdhci_msm_v5_offset, 229762306a36Sopenharmony_ci}; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_cistatic const struct sdhci_msm_variant_info sdm845_sdhci_var = { 230062306a36Sopenharmony_ci .mci_removed = true, 230162306a36Sopenharmony_ci .restore_dll_config = true, 230262306a36Sopenharmony_ci .var_ops = &v5_var_ops, 230362306a36Sopenharmony_ci .offset = &sdhci_msm_v5_offset, 230462306a36Sopenharmony_ci}; 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_cistatic const struct of_device_id sdhci_msm_dt_match[] = { 230762306a36Sopenharmony_ci /* 230862306a36Sopenharmony_ci * Do not add new variants to the driver which are compatible with 230962306a36Sopenharmony_ci * generic ones, unless they need customization. 231062306a36Sopenharmony_ci */ 231162306a36Sopenharmony_ci {.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var}, 231262306a36Sopenharmony_ci {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var}, 231362306a36Sopenharmony_ci {.compatible = "qcom,sdm670-sdhci", .data = &sdm845_sdhci_var}, 231462306a36Sopenharmony_ci {.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var}, 231562306a36Sopenharmony_ci {.compatible = "qcom,sc7180-sdhci", .data = &sdm845_sdhci_var}, 231662306a36Sopenharmony_ci {}, 231762306a36Sopenharmony_ci}; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_cistatic const struct sdhci_ops sdhci_msm_ops = { 232262306a36Sopenharmony_ci .reset = sdhci_and_cqhci_reset, 232362306a36Sopenharmony_ci .set_clock = sdhci_msm_set_clock, 232462306a36Sopenharmony_ci .get_min_clock = sdhci_msm_get_min_clock, 232562306a36Sopenharmony_ci .get_max_clock = sdhci_msm_get_max_clock, 232662306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 232762306a36Sopenharmony_ci .set_uhs_signaling = sdhci_msm_set_uhs_signaling, 232862306a36Sopenharmony_ci .write_w = sdhci_msm_writew, 232962306a36Sopenharmony_ci .write_b = sdhci_msm_writeb, 233062306a36Sopenharmony_ci .irq = sdhci_msm_cqe_irq, 233162306a36Sopenharmony_ci .dump_vendor_regs = sdhci_msm_dump_vendor_regs, 233262306a36Sopenharmony_ci .set_power = sdhci_set_power_noreg, 233362306a36Sopenharmony_ci .set_timeout = sdhci_msm_set_timeout, 233462306a36Sopenharmony_ci}; 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_msm_pdata = { 233762306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | 233862306a36Sopenharmony_ci SDHCI_QUIRK_SINGLE_POWER_WRITE | 233962306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 234062306a36Sopenharmony_ci SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 234362306a36Sopenharmony_ci .ops = &sdhci_msm_ops, 234462306a36Sopenharmony_ci}; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_cistatic inline void sdhci_msm_get_of_property(struct platform_device *pdev, 234762306a36Sopenharmony_ci struct sdhci_host *host) 234862306a36Sopenharmony_ci{ 234962306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 235062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 235162306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci if (of_property_read_u32(node, "qcom,ddr-config", 235462306a36Sopenharmony_ci &msm_host->ddr_config)) 235562306a36Sopenharmony_ci msm_host->ddr_config = DDR_CONFIG_POR_VAL; 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci of_property_read_u32(node, "qcom,dll-config", &msm_host->dll_config); 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci if (of_device_is_compatible(node, "qcom,msm8916-sdhci")) 236062306a36Sopenharmony_ci host->quirks2 |= SDHCI_QUIRK2_BROKEN_64_BIT_DMA; 236162306a36Sopenharmony_ci} 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_cistatic int sdhci_msm_gcc_reset(struct device *dev, struct sdhci_host *host) 236462306a36Sopenharmony_ci{ 236562306a36Sopenharmony_ci struct reset_control *reset; 236662306a36Sopenharmony_ci int ret = 0; 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci reset = reset_control_get_optional_exclusive(dev, NULL); 236962306a36Sopenharmony_ci if (IS_ERR(reset)) 237062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(reset), 237162306a36Sopenharmony_ci "unable to acquire core_reset\n"); 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci if (!reset) 237462306a36Sopenharmony_ci return ret; 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci ret = reset_control_assert(reset); 237762306a36Sopenharmony_ci if (ret) { 237862306a36Sopenharmony_ci reset_control_put(reset); 237962306a36Sopenharmony_ci return dev_err_probe(dev, ret, "core_reset assert failed\n"); 238062306a36Sopenharmony_ci } 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci /* 238362306a36Sopenharmony_ci * The hardware requirement for delay between assert/deassert 238462306a36Sopenharmony_ci * is at least 3-4 sleep clock (32.7KHz) cycles, which comes to 238562306a36Sopenharmony_ci * ~125us (4/32768). To be on the safe side add 200us delay. 238662306a36Sopenharmony_ci */ 238762306a36Sopenharmony_ci usleep_range(200, 210); 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci ret = reset_control_deassert(reset); 239062306a36Sopenharmony_ci if (ret) { 239162306a36Sopenharmony_ci reset_control_put(reset); 239262306a36Sopenharmony_ci return dev_err_probe(dev, ret, "core_reset deassert failed\n"); 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci usleep_range(200, 210); 239662306a36Sopenharmony_ci reset_control_put(reset); 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci return ret; 239962306a36Sopenharmony_ci} 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_cistatic int sdhci_msm_probe(struct platform_device *pdev) 240262306a36Sopenharmony_ci{ 240362306a36Sopenharmony_ci struct sdhci_host *host; 240462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 240562306a36Sopenharmony_ci struct sdhci_msm_host *msm_host; 240662306a36Sopenharmony_ci struct clk *clk; 240762306a36Sopenharmony_ci int ret; 240862306a36Sopenharmony_ci u16 host_version, core_minor; 240962306a36Sopenharmony_ci u32 core_version, config; 241062306a36Sopenharmony_ci u8 core_major; 241162306a36Sopenharmony_ci const struct sdhci_msm_offset *msm_offset; 241262306a36Sopenharmony_ci const struct sdhci_msm_variant_info *var_info; 241362306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); 241662306a36Sopenharmony_ci if (IS_ERR(host)) 241762306a36Sopenharmony_ci return PTR_ERR(host); 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci host->sdma_boundary = 0; 242062306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 242162306a36Sopenharmony_ci msm_host = sdhci_pltfm_priv(pltfm_host); 242262306a36Sopenharmony_ci msm_host->mmc = host->mmc; 242362306a36Sopenharmony_ci msm_host->pdev = pdev; 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci ret = mmc_of_parse(host->mmc); 242662306a36Sopenharmony_ci if (ret) 242762306a36Sopenharmony_ci goto pltfm_free; 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci /* 243062306a36Sopenharmony_ci * Based on the compatible string, load the required msm host info from 243162306a36Sopenharmony_ci * the data associated with the version info. 243262306a36Sopenharmony_ci */ 243362306a36Sopenharmony_ci var_info = of_device_get_match_data(&pdev->dev); 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci msm_host->mci_removed = var_info->mci_removed; 243662306a36Sopenharmony_ci msm_host->restore_dll_config = var_info->restore_dll_config; 243762306a36Sopenharmony_ci msm_host->var_ops = var_info->var_ops; 243862306a36Sopenharmony_ci msm_host->offset = var_info->offset; 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci msm_offset = msm_host->offset; 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ci sdhci_get_of_property(pdev); 244362306a36Sopenharmony_ci sdhci_msm_get_of_property(pdev, host); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci msm_host->saved_tuning_phase = INVALID_TUNING_PHASE; 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci ret = sdhci_msm_gcc_reset(&pdev->dev, host); 244862306a36Sopenharmony_ci if (ret) 244962306a36Sopenharmony_ci goto pltfm_free; 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci /* Setup SDCC bus voter clock. */ 245262306a36Sopenharmony_ci msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus"); 245362306a36Sopenharmony_ci if (!IS_ERR(msm_host->bus_clk)) { 245462306a36Sopenharmony_ci /* Vote for max. clk rate for max. performance */ 245562306a36Sopenharmony_ci ret = clk_set_rate(msm_host->bus_clk, INT_MAX); 245662306a36Sopenharmony_ci if (ret) 245762306a36Sopenharmony_ci goto pltfm_free; 245862306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->bus_clk); 245962306a36Sopenharmony_ci if (ret) 246062306a36Sopenharmony_ci goto pltfm_free; 246162306a36Sopenharmony_ci } 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci /* Setup main peripheral bus clock */ 246462306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, "iface"); 246562306a36Sopenharmony_ci if (IS_ERR(clk)) { 246662306a36Sopenharmony_ci ret = PTR_ERR(clk); 246762306a36Sopenharmony_ci dev_err(&pdev->dev, "Peripheral clk setup failed (%d)\n", ret); 246862306a36Sopenharmony_ci goto bus_clk_disable; 246962306a36Sopenharmony_ci } 247062306a36Sopenharmony_ci msm_host->bulk_clks[1].clk = clk; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci /* Setup SDC MMC clock */ 247362306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, "core"); 247462306a36Sopenharmony_ci if (IS_ERR(clk)) { 247562306a36Sopenharmony_ci ret = PTR_ERR(clk); 247662306a36Sopenharmony_ci dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret); 247762306a36Sopenharmony_ci goto bus_clk_disable; 247862306a36Sopenharmony_ci } 247962306a36Sopenharmony_ci msm_host->bulk_clks[0].clk = clk; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci /* Check for optional interconnect paths */ 248262306a36Sopenharmony_ci ret = dev_pm_opp_of_find_icc_paths(&pdev->dev, NULL); 248362306a36Sopenharmony_ci if (ret) 248462306a36Sopenharmony_ci goto bus_clk_disable; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci ret = devm_pm_opp_set_clkname(&pdev->dev, "core"); 248762306a36Sopenharmony_ci if (ret) 248862306a36Sopenharmony_ci goto bus_clk_disable; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci /* OPP table is optional */ 249162306a36Sopenharmony_ci ret = devm_pm_opp_of_add_table(&pdev->dev); 249262306a36Sopenharmony_ci if (ret && ret != -ENODEV) { 249362306a36Sopenharmony_ci dev_err(&pdev->dev, "Invalid OPP table in Device tree\n"); 249462306a36Sopenharmony_ci goto bus_clk_disable; 249562306a36Sopenharmony_ci } 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci /* Vote for maximum clock rate for maximum performance */ 249862306a36Sopenharmony_ci ret = dev_pm_opp_set_rate(&pdev->dev, INT_MAX); 249962306a36Sopenharmony_ci if (ret) 250062306a36Sopenharmony_ci dev_warn(&pdev->dev, "core clock boost failed\n"); 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, "cal"); 250362306a36Sopenharmony_ci if (IS_ERR(clk)) 250462306a36Sopenharmony_ci clk = NULL; 250562306a36Sopenharmony_ci msm_host->bulk_clks[2].clk = clk; 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, "sleep"); 250862306a36Sopenharmony_ci if (IS_ERR(clk)) 250962306a36Sopenharmony_ci clk = NULL; 251062306a36Sopenharmony_ci msm_host->bulk_clks[3].clk = clk; 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), 251362306a36Sopenharmony_ci msm_host->bulk_clks); 251462306a36Sopenharmony_ci if (ret) 251562306a36Sopenharmony_ci goto bus_clk_disable; 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci /* 251862306a36Sopenharmony_ci * xo clock is needed for FLL feature of cm_dll. 251962306a36Sopenharmony_ci * In case if xo clock is not mentioned in DT, warn and proceed. 252062306a36Sopenharmony_ci */ 252162306a36Sopenharmony_ci msm_host->xo_clk = devm_clk_get(&pdev->dev, "xo"); 252262306a36Sopenharmony_ci if (IS_ERR(msm_host->xo_clk)) { 252362306a36Sopenharmony_ci ret = PTR_ERR(msm_host->xo_clk); 252462306a36Sopenharmony_ci dev_warn(&pdev->dev, "TCXO clk not present (%d)\n", ret); 252562306a36Sopenharmony_ci } 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci if (!msm_host->mci_removed) { 252862306a36Sopenharmony_ci msm_host->core_mem = devm_platform_ioremap_resource(pdev, 1); 252962306a36Sopenharmony_ci if (IS_ERR(msm_host->core_mem)) { 253062306a36Sopenharmony_ci ret = PTR_ERR(msm_host->core_mem); 253162306a36Sopenharmony_ci goto clk_disable; 253262306a36Sopenharmony_ci } 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci /* Reset the vendor spec register to power on reset state */ 253662306a36Sopenharmony_ci writel_relaxed(CORE_VENDOR_SPEC_POR_VAL, 253762306a36Sopenharmony_ci host->ioaddr + msm_offset->core_vendor_spec); 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci if (!msm_host->mci_removed) { 254062306a36Sopenharmony_ci /* Set HC_MODE_EN bit in HC_MODE register */ 254162306a36Sopenharmony_ci msm_host_writel(msm_host, HC_MODE_EN, host, 254262306a36Sopenharmony_ci msm_offset->core_hc_mode); 254362306a36Sopenharmony_ci config = msm_host_readl(msm_host, host, 254462306a36Sopenharmony_ci msm_offset->core_hc_mode); 254562306a36Sopenharmony_ci config |= FF_CLK_SW_RST_DIS; 254662306a36Sopenharmony_ci msm_host_writel(msm_host, config, host, 254762306a36Sopenharmony_ci msm_offset->core_hc_mode); 254862306a36Sopenharmony_ci } 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); 255162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", 255262306a36Sopenharmony_ci host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> 255362306a36Sopenharmony_ci SDHCI_VENDOR_VER_SHIFT)); 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci core_version = msm_host_readl(msm_host, host, 255662306a36Sopenharmony_ci msm_offset->core_mci_version); 255762306a36Sopenharmony_ci core_major = (core_version & CORE_VERSION_MAJOR_MASK) >> 255862306a36Sopenharmony_ci CORE_VERSION_MAJOR_SHIFT; 255962306a36Sopenharmony_ci core_minor = core_version & CORE_VERSION_MINOR_MASK; 256062306a36Sopenharmony_ci dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n", 256162306a36Sopenharmony_ci core_version, core_major, core_minor); 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci if (core_major == 1 && core_minor >= 0x42) 256462306a36Sopenharmony_ci msm_host->use_14lpp_dll_reset = true; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci /* 256762306a36Sopenharmony_ci * SDCC 5 controller with major version 1, minor version 0x34 and later 256862306a36Sopenharmony_ci * with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL. 256962306a36Sopenharmony_ci */ 257062306a36Sopenharmony_ci if (core_major == 1 && core_minor < 0x34) 257162306a36Sopenharmony_ci msm_host->use_cdclp533 = true; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci /* 257462306a36Sopenharmony_ci * Support for some capabilities is not advertised by newer 257562306a36Sopenharmony_ci * controller versions and must be explicitly enabled. 257662306a36Sopenharmony_ci */ 257762306a36Sopenharmony_ci if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) { 257862306a36Sopenharmony_ci config = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES); 257962306a36Sopenharmony_ci config |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; 258062306a36Sopenharmony_ci writel_relaxed(config, host->ioaddr + 258162306a36Sopenharmony_ci msm_offset->core_vendor_spec_capabilities0); 258262306a36Sopenharmony_ci } 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci if (core_major == 1 && core_minor >= 0x49) 258562306a36Sopenharmony_ci msm_host->updated_ddr_cfg = true; 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci if (core_major == 1 && core_minor >= 0x71) 258862306a36Sopenharmony_ci msm_host->uses_tassadar_dll = true; 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci ret = sdhci_msm_register_vreg(msm_host); 259162306a36Sopenharmony_ci if (ret) 259262306a36Sopenharmony_ci goto clk_disable; 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci /* 259562306a36Sopenharmony_ci * Power on reset state may trigger power irq if previous status of 259662306a36Sopenharmony_ci * PWRCTL was either BUS_ON or IO_HIGH_V. So before enabling pwr irq 259762306a36Sopenharmony_ci * interrupt in GIC, any pending power irq interrupt should be 259862306a36Sopenharmony_ci * acknowledged. Otherwise power irq interrupt handler would be 259962306a36Sopenharmony_ci * fired prematurely. 260062306a36Sopenharmony_ci */ 260162306a36Sopenharmony_ci sdhci_msm_handle_pwr_irq(host, 0); 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci /* 260462306a36Sopenharmony_ci * Ensure that above writes are propogated before interrupt enablement 260562306a36Sopenharmony_ci * in GIC. 260662306a36Sopenharmony_ci */ 260762306a36Sopenharmony_ci mb(); 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci /* Setup IRQ for handling power/voltage tasks with PMIC */ 261062306a36Sopenharmony_ci msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); 261162306a36Sopenharmony_ci if (msm_host->pwr_irq < 0) { 261262306a36Sopenharmony_ci ret = msm_host->pwr_irq; 261362306a36Sopenharmony_ci goto clk_disable; 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci sdhci_msm_init_pwr_irq_wait(msm_host); 261762306a36Sopenharmony_ci /* Enable pwr irq interrupts */ 261862306a36Sopenharmony_ci msm_host_writel(msm_host, INT_MASK, host, 261962306a36Sopenharmony_ci msm_offset->core_pwrctl_mask); 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, 262262306a36Sopenharmony_ci sdhci_msm_pwr_irq, IRQF_ONESHOT, 262362306a36Sopenharmony_ci dev_name(&pdev->dev), host); 262462306a36Sopenharmony_ci if (ret) { 262562306a36Sopenharmony_ci dev_err(&pdev->dev, "Request IRQ failed (%d)\n", ret); 262662306a36Sopenharmony_ci goto clk_disable; 262762306a36Sopenharmony_ci } 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci /* Set the timeout value to max possible */ 263262306a36Sopenharmony_ci host->max_timeout_count = 0xF; 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 263562306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 263662306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 263762306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 263862306a36Sopenharmony_ci MSM_MMC_AUTOSUSPEND_DELAY_MS); 263962306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci host->mmc_host_ops.start_signal_voltage_switch = 264262306a36Sopenharmony_ci sdhci_msm_start_signal_voltage_switch; 264362306a36Sopenharmony_ci host->mmc_host_ops.execute_tuning = sdhci_msm_execute_tuning; 264462306a36Sopenharmony_ci if (of_property_read_bool(node, "supports-cqe")) 264562306a36Sopenharmony_ci ret = sdhci_msm_cqe_add_host(host, pdev); 264662306a36Sopenharmony_ci else 264762306a36Sopenharmony_ci ret = sdhci_add_host(host); 264862306a36Sopenharmony_ci if (ret) 264962306a36Sopenharmony_ci goto pm_runtime_disable; 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 265262306a36Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci return 0; 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_cipm_runtime_disable: 265762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 265862306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 265962306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 266062306a36Sopenharmony_ciclk_disable: 266162306a36Sopenharmony_ci clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks), 266262306a36Sopenharmony_ci msm_host->bulk_clks); 266362306a36Sopenharmony_cibus_clk_disable: 266462306a36Sopenharmony_ci if (!IS_ERR(msm_host->bus_clk)) 266562306a36Sopenharmony_ci clk_disable_unprepare(msm_host->bus_clk); 266662306a36Sopenharmony_cipltfm_free: 266762306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 266862306a36Sopenharmony_ci return ret; 266962306a36Sopenharmony_ci} 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_cistatic void sdhci_msm_remove(struct platform_device *pdev) 267262306a36Sopenharmony_ci{ 267362306a36Sopenharmony_ci struct sdhci_host *host = platform_get_drvdata(pdev); 267462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 267562306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 267662306a36Sopenharmony_ci int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 267762306a36Sopenharmony_ci 0xffffffff); 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci sdhci_remove_host(host, dead); 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 268262306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 268362306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks), 268662306a36Sopenharmony_ci msm_host->bulk_clks); 268762306a36Sopenharmony_ci if (!IS_ERR(msm_host->bus_clk)) 268862306a36Sopenharmony_ci clk_disable_unprepare(msm_host->bus_clk); 268962306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 269062306a36Sopenharmony_ci} 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_cistatic __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) 269362306a36Sopenharmony_ci{ 269462306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 269562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 269662306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci /* Drop the performance vote */ 269962306a36Sopenharmony_ci dev_pm_opp_set_rate(dev, 0); 270062306a36Sopenharmony_ci clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks), 270162306a36Sopenharmony_ci msm_host->bulk_clks); 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci return sdhci_msm_ice_suspend(msm_host); 270462306a36Sopenharmony_ci} 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_cistatic __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) 270762306a36Sopenharmony_ci{ 270862306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 270962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 271062306a36Sopenharmony_ci struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); 271162306a36Sopenharmony_ci int ret; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), 271462306a36Sopenharmony_ci msm_host->bulk_clks); 271562306a36Sopenharmony_ci if (ret) 271662306a36Sopenharmony_ci return ret; 271762306a36Sopenharmony_ci /* 271862306a36Sopenharmony_ci * Whenever core-clock is gated dynamically, it's needed to 271962306a36Sopenharmony_ci * restore the SDR DLL settings when the clock is ungated. 272062306a36Sopenharmony_ci */ 272162306a36Sopenharmony_ci if (msm_host->restore_dll_config && msm_host->clk_rate) { 272262306a36Sopenharmony_ci ret = sdhci_msm_restore_sdr_dll_config(host); 272362306a36Sopenharmony_ci if (ret) 272462306a36Sopenharmony_ci return ret; 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci dev_pm_opp_set_rate(dev, msm_host->clk_rate); 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci return sdhci_msm_ice_resume(msm_host); 273062306a36Sopenharmony_ci} 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_cistatic const struct dev_pm_ops sdhci_msm_pm_ops = { 273362306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 273462306a36Sopenharmony_ci pm_runtime_force_resume) 273562306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, 273662306a36Sopenharmony_ci sdhci_msm_runtime_resume, 273762306a36Sopenharmony_ci NULL) 273862306a36Sopenharmony_ci}; 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_cistatic struct platform_driver sdhci_msm_driver = { 274162306a36Sopenharmony_ci .probe = sdhci_msm_probe, 274262306a36Sopenharmony_ci .remove_new = sdhci_msm_remove, 274362306a36Sopenharmony_ci .driver = { 274462306a36Sopenharmony_ci .name = "sdhci_msm", 274562306a36Sopenharmony_ci .of_match_table = sdhci_msm_dt_match, 274662306a36Sopenharmony_ci .pm = &sdhci_msm_pm_ops, 274762306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 274862306a36Sopenharmony_ci }, 274962306a36Sopenharmony_ci}; 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_cimodule_platform_driver(sdhci_msm_driver); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver"); 275462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2755