162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sdhci_am654.c - SDHCI driver for TI's AM654 SOCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/iopoll.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1362306a36Sopenharmony_ci#include <linux/property.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/sys_soc.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "cqhci.h" 1862306a36Sopenharmony_ci#include "sdhci-cqhci.h" 1962306a36Sopenharmony_ci#include "sdhci-pltfm.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* CTL_CFG Registers */ 2262306a36Sopenharmony_ci#define CTL_CFG_2 0x14 2362306a36Sopenharmony_ci#define CTL_CFG_3 0x18 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define SLOTTYPE_MASK GENMASK(31, 30) 2662306a36Sopenharmony_ci#define SLOTTYPE_EMBEDDED BIT(30) 2762306a36Sopenharmony_ci#define TUNINGFORSDR50_MASK BIT(13) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* PHY Registers */ 3062306a36Sopenharmony_ci#define PHY_CTRL1 0x100 3162306a36Sopenharmony_ci#define PHY_CTRL2 0x104 3262306a36Sopenharmony_ci#define PHY_CTRL3 0x108 3362306a36Sopenharmony_ci#define PHY_CTRL4 0x10C 3462306a36Sopenharmony_ci#define PHY_CTRL5 0x110 3562306a36Sopenharmony_ci#define PHY_CTRL6 0x114 3662306a36Sopenharmony_ci#define PHY_STAT1 0x130 3762306a36Sopenharmony_ci#define PHY_STAT2 0x134 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define IOMUX_ENABLE_SHIFT 31 4062306a36Sopenharmony_ci#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT) 4162306a36Sopenharmony_ci#define OTAPDLYENA_SHIFT 20 4262306a36Sopenharmony_ci#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT) 4362306a36Sopenharmony_ci#define OTAPDLYSEL_SHIFT 12 4462306a36Sopenharmony_ci#define OTAPDLYSEL_MASK GENMASK(15, 12) 4562306a36Sopenharmony_ci#define STRBSEL_SHIFT 24 4662306a36Sopenharmony_ci#define STRBSEL_4BIT_MASK GENMASK(27, 24) 4762306a36Sopenharmony_ci#define STRBSEL_8BIT_MASK GENMASK(31, 24) 4862306a36Sopenharmony_ci#define SEL50_SHIFT 8 4962306a36Sopenharmony_ci#define SEL50_MASK BIT(SEL50_SHIFT) 5062306a36Sopenharmony_ci#define SEL100_SHIFT 9 5162306a36Sopenharmony_ci#define SEL100_MASK BIT(SEL100_SHIFT) 5262306a36Sopenharmony_ci#define FREQSEL_SHIFT 8 5362306a36Sopenharmony_ci#define FREQSEL_MASK GENMASK(10, 8) 5462306a36Sopenharmony_ci#define CLKBUFSEL_SHIFT 0 5562306a36Sopenharmony_ci#define CLKBUFSEL_MASK GENMASK(2, 0) 5662306a36Sopenharmony_ci#define DLL_TRIM_ICP_SHIFT 4 5762306a36Sopenharmony_ci#define DLL_TRIM_ICP_MASK GENMASK(7, 4) 5862306a36Sopenharmony_ci#define DR_TY_SHIFT 20 5962306a36Sopenharmony_ci#define DR_TY_MASK GENMASK(22, 20) 6062306a36Sopenharmony_ci#define ENDLL_SHIFT 1 6162306a36Sopenharmony_ci#define ENDLL_MASK BIT(ENDLL_SHIFT) 6262306a36Sopenharmony_ci#define DLLRDY_SHIFT 0 6362306a36Sopenharmony_ci#define DLLRDY_MASK BIT(DLLRDY_SHIFT) 6462306a36Sopenharmony_ci#define PDB_SHIFT 0 6562306a36Sopenharmony_ci#define PDB_MASK BIT(PDB_SHIFT) 6662306a36Sopenharmony_ci#define CALDONE_SHIFT 1 6762306a36Sopenharmony_ci#define CALDONE_MASK BIT(CALDONE_SHIFT) 6862306a36Sopenharmony_ci#define RETRIM_SHIFT 17 6962306a36Sopenharmony_ci#define RETRIM_MASK BIT(RETRIM_SHIFT) 7062306a36Sopenharmony_ci#define SELDLYTXCLK_SHIFT 17 7162306a36Sopenharmony_ci#define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT) 7262306a36Sopenharmony_ci#define SELDLYRXCLK_SHIFT 16 7362306a36Sopenharmony_ci#define SELDLYRXCLK_MASK BIT(SELDLYRXCLK_SHIFT) 7462306a36Sopenharmony_ci#define ITAPDLYSEL_SHIFT 0 7562306a36Sopenharmony_ci#define ITAPDLYSEL_MASK GENMASK(4, 0) 7662306a36Sopenharmony_ci#define ITAPDLYENA_SHIFT 8 7762306a36Sopenharmony_ci#define ITAPDLYENA_MASK BIT(ITAPDLYENA_SHIFT) 7862306a36Sopenharmony_ci#define ITAPCHGWIN_SHIFT 9 7962306a36Sopenharmony_ci#define ITAPCHGWIN_MASK BIT(ITAPCHGWIN_SHIFT) 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define DRIVER_STRENGTH_50_OHM 0x0 8262306a36Sopenharmony_ci#define DRIVER_STRENGTH_33_OHM 0x1 8362306a36Sopenharmony_ci#define DRIVER_STRENGTH_66_OHM 0x2 8462306a36Sopenharmony_ci#define DRIVER_STRENGTH_100_OHM 0x3 8562306a36Sopenharmony_ci#define DRIVER_STRENGTH_40_OHM 0x4 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define CLOCK_TOO_SLOW_HZ 50000000 8862306a36Sopenharmony_ci#define SDHCI_AM654_AUTOSUSPEND_DELAY -1 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* Command Queue Host Controller Interface Base address */ 9162306a36Sopenharmony_ci#define SDHCI_AM654_CQE_BASE_ADDR 0x200 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic struct regmap_config sdhci_am654_regmap_config = { 9462306a36Sopenharmony_ci .reg_bits = 32, 9562306a36Sopenharmony_ci .val_bits = 32, 9662306a36Sopenharmony_ci .reg_stride = 4, 9762306a36Sopenharmony_ci .fast_io = true, 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct timing_data { 10162306a36Sopenharmony_ci const char *otap_binding; 10262306a36Sopenharmony_ci const char *itap_binding; 10362306a36Sopenharmony_ci u32 capability; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic const struct timing_data td[] = { 10762306a36Sopenharmony_ci [MMC_TIMING_LEGACY] = {"ti,otap-del-sel-legacy", 10862306a36Sopenharmony_ci "ti,itap-del-sel-legacy", 10962306a36Sopenharmony_ci 0}, 11062306a36Sopenharmony_ci [MMC_TIMING_MMC_HS] = {"ti,otap-del-sel-mmc-hs", 11162306a36Sopenharmony_ci "ti,itap-del-sel-mmc-hs", 11262306a36Sopenharmony_ci MMC_CAP_MMC_HIGHSPEED}, 11362306a36Sopenharmony_ci [MMC_TIMING_SD_HS] = {"ti,otap-del-sel-sd-hs", 11462306a36Sopenharmony_ci "ti,itap-del-sel-sd-hs", 11562306a36Sopenharmony_ci MMC_CAP_SD_HIGHSPEED}, 11662306a36Sopenharmony_ci [MMC_TIMING_UHS_SDR12] = {"ti,otap-del-sel-sdr12", 11762306a36Sopenharmony_ci "ti,itap-del-sel-sdr12", 11862306a36Sopenharmony_ci MMC_CAP_UHS_SDR12}, 11962306a36Sopenharmony_ci [MMC_TIMING_UHS_SDR25] = {"ti,otap-del-sel-sdr25", 12062306a36Sopenharmony_ci "ti,itap-del-sel-sdr25", 12162306a36Sopenharmony_ci MMC_CAP_UHS_SDR25}, 12262306a36Sopenharmony_ci [MMC_TIMING_UHS_SDR50] = {"ti,otap-del-sel-sdr50", 12362306a36Sopenharmony_ci NULL, 12462306a36Sopenharmony_ci MMC_CAP_UHS_SDR50}, 12562306a36Sopenharmony_ci [MMC_TIMING_UHS_SDR104] = {"ti,otap-del-sel-sdr104", 12662306a36Sopenharmony_ci NULL, 12762306a36Sopenharmony_ci MMC_CAP_UHS_SDR104}, 12862306a36Sopenharmony_ci [MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50", 12962306a36Sopenharmony_ci NULL, 13062306a36Sopenharmony_ci MMC_CAP_UHS_DDR50}, 13162306a36Sopenharmony_ci [MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52", 13262306a36Sopenharmony_ci "ti,itap-del-sel-ddr52", 13362306a36Sopenharmony_ci MMC_CAP_DDR}, 13462306a36Sopenharmony_ci [MMC_TIMING_MMC_HS200] = {"ti,otap-del-sel-hs200", 13562306a36Sopenharmony_ci NULL, 13662306a36Sopenharmony_ci MMC_CAP2_HS200}, 13762306a36Sopenharmony_ci [MMC_TIMING_MMC_HS400] = {"ti,otap-del-sel-hs400", 13862306a36Sopenharmony_ci NULL, 13962306a36Sopenharmony_ci MMC_CAP2_HS400}, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistruct sdhci_am654_data { 14362306a36Sopenharmony_ci struct regmap *base; 14462306a36Sopenharmony_ci bool legacy_otapdly; 14562306a36Sopenharmony_ci int otap_del_sel[ARRAY_SIZE(td)]; 14662306a36Sopenharmony_ci int itap_del_sel[ARRAY_SIZE(td)]; 14762306a36Sopenharmony_ci int clkbuf_sel; 14862306a36Sopenharmony_ci int trm_icp; 14962306a36Sopenharmony_ci int drv_strength; 15062306a36Sopenharmony_ci int strb_sel; 15162306a36Sopenharmony_ci u32 flags; 15262306a36Sopenharmony_ci u32 quirks; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0) 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistruct sdhci_am654_driver_data { 15862306a36Sopenharmony_ci const struct sdhci_pltfm_data *pdata; 15962306a36Sopenharmony_ci u32 flags; 16062306a36Sopenharmony_ci#define IOMUX_PRESENT (1 << 0) 16162306a36Sopenharmony_ci#define FREQSEL_2_BIT (1 << 1) 16262306a36Sopenharmony_ci#define STRBSEL_4_BIT (1 << 2) 16362306a36Sopenharmony_ci#define DLL_PRESENT (1 << 3) 16462306a36Sopenharmony_ci#define DLL_CALIB (1 << 4) 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 17062306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); 17162306a36Sopenharmony_ci int sel50, sel100, freqsel; 17262306a36Sopenharmony_ci u32 mask, val; 17362306a36Sopenharmony_ci int ret; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Disable delay chain mode */ 17662306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL5, 17762306a36Sopenharmony_ci SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (sdhci_am654->flags & FREQSEL_2_BIT) { 18062306a36Sopenharmony_ci switch (clock) { 18162306a36Sopenharmony_ci case 200000000: 18262306a36Sopenharmony_ci sel50 = 0; 18362306a36Sopenharmony_ci sel100 = 0; 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci case 100000000: 18662306a36Sopenharmony_ci sel50 = 0; 18762306a36Sopenharmony_ci sel100 = 1; 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci default: 19062306a36Sopenharmony_ci sel50 = 1; 19162306a36Sopenharmony_ci sel100 = 0; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Configure PHY DLL frequency */ 19562306a36Sopenharmony_ci mask = SEL50_MASK | SEL100_MASK; 19662306a36Sopenharmony_ci val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT); 19762306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, val); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci switch (clock) { 20162306a36Sopenharmony_ci case 200000000: 20262306a36Sopenharmony_ci freqsel = 0x0; 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci default: 20562306a36Sopenharmony_ci freqsel = 0x4; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL5, FREQSEL_MASK, 20962306a36Sopenharmony_ci freqsel << FREQSEL_SHIFT); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci /* Configure DLL TRIM */ 21262306a36Sopenharmony_ci mask = DLL_TRIM_ICP_MASK; 21362306a36Sopenharmony_ci val = sdhci_am654->trm_icp << DLL_TRIM_ICP_SHIFT; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Configure DLL driver strength */ 21662306a36Sopenharmony_ci mask |= DR_TY_MASK; 21762306a36Sopenharmony_ci val |= sdhci_am654->drv_strength << DR_TY_SHIFT; 21862306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL1, mask, val); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Enable DLL */ 22162306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 22262306a36Sopenharmony_ci 0x1 << ENDLL_SHIFT); 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * Poll for DLL ready. Use a one second timeout. 22562306a36Sopenharmony_ci * Works in all experiments done so far 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci ret = regmap_read_poll_timeout(sdhci_am654->base, PHY_STAT1, val, 22862306a36Sopenharmony_ci val & DLLRDY_MASK, 1000, 1000000); 22962306a36Sopenharmony_ci if (ret) { 23062306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "DLL failed to relock\n"); 23162306a36Sopenharmony_ci return; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void sdhci_am654_write_itapdly(struct sdhci_am654_data *sdhci_am654, 23662306a36Sopenharmony_ci u32 itapdly) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci /* Set ITAPCHGWIN before writing to ITAPDLY */ 23962306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, 24062306a36Sopenharmony_ci 1 << ITAPCHGWIN_SHIFT); 24162306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYSEL_MASK, 24262306a36Sopenharmony_ci itapdly << ITAPDLYSEL_SHIFT); 24362306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void sdhci_am654_setup_delay_chain(struct sdhci_am654_data *sdhci_am654, 24762306a36Sopenharmony_ci unsigned char timing) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci u32 mask, val; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT; 25462306a36Sopenharmony_ci mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK; 25562306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, val); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci sdhci_am654_write_itapdly(sdhci_am654, 25862306a36Sopenharmony_ci sdhci_am654->itap_del_sel[timing]); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 26462306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); 26562306a36Sopenharmony_ci unsigned char timing = host->mmc->ios.timing; 26662306a36Sopenharmony_ci u32 otap_del_sel; 26762306a36Sopenharmony_ci u32 otap_del_ena; 26862306a36Sopenharmony_ci u32 mask, val; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci sdhci_set_clock(host, clock); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Setup DLL Output TAP delay */ 27562306a36Sopenharmony_ci if (sdhci_am654->legacy_otapdly) 27662306a36Sopenharmony_ci otap_del_sel = sdhci_am654->otap_del_sel[0]; 27762306a36Sopenharmony_ci else 27862306a36Sopenharmony_ci otap_del_sel = sdhci_am654->otap_del_sel[timing]; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci otap_del_ena = (timing > MMC_TIMING_UHS_SDR25) ? 1 : 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; 28362306a36Sopenharmony_ci val = (otap_del_ena << OTAPDLYENA_SHIFT) | 28462306a36Sopenharmony_ci (otap_del_sel << OTAPDLYSEL_SHIFT); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Write to STRBSEL for HS400 speed mode */ 28762306a36Sopenharmony_ci if (timing == MMC_TIMING_MMC_HS400) { 28862306a36Sopenharmony_ci if (sdhci_am654->flags & STRBSEL_4_BIT) 28962306a36Sopenharmony_ci mask |= STRBSEL_4BIT_MASK; 29062306a36Sopenharmony_ci else 29162306a36Sopenharmony_ci mask |= STRBSEL_8BIT_MASK; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci val |= sdhci_am654->strb_sel << STRBSEL_SHIFT; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (timing > MMC_TIMING_UHS_SDR25 && clock >= CLOCK_TOO_SLOW_HZ) 29962306a36Sopenharmony_ci sdhci_am654_setup_dll(host, clock); 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci sdhci_am654_setup_delay_chain(sdhci_am654, timing); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK, 30462306a36Sopenharmony_ci sdhci_am654->clkbuf_sel); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void sdhci_j721e_4bit_set_clock(struct sdhci_host *host, 30862306a36Sopenharmony_ci unsigned int clock) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 31162306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); 31262306a36Sopenharmony_ci unsigned char timing = host->mmc->ios.timing; 31362306a36Sopenharmony_ci u32 otap_del_sel; 31462306a36Sopenharmony_ci u32 mask, val; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Setup DLL Output TAP delay */ 31762306a36Sopenharmony_ci if (sdhci_am654->legacy_otapdly) 31862306a36Sopenharmony_ci otap_del_sel = sdhci_am654->otap_del_sel[0]; 31962306a36Sopenharmony_ci else 32062306a36Sopenharmony_ci otap_del_sel = sdhci_am654->otap_del_sel[timing]; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; 32362306a36Sopenharmony_ci val = (0x1 << OTAPDLYENA_SHIFT) | 32462306a36Sopenharmony_ci (otap_del_sel << OTAPDLYSEL_SHIFT); 32562306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK, 32862306a36Sopenharmony_ci sdhci_am654->clkbuf_sel); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci sdhci_set_clock(host, clock); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic u8 sdhci_am654_write_power_on(struct sdhci_host *host, u8 val, int reg) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci writeb(val, host->ioaddr + reg); 33662306a36Sopenharmony_ci usleep_range(1000, 10000); 33762306a36Sopenharmony_ci return readb(host->ioaddr + reg); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci#define MAX_POWER_ON_TIMEOUT 1500000 /* us */ 34162306a36Sopenharmony_cistatic void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci unsigned char timing = host->mmc->ios.timing; 34462306a36Sopenharmony_ci u8 pwr; 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (reg == SDHCI_HOST_CONTROL) { 34862306a36Sopenharmony_ci switch (timing) { 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * According to the data manual, HISPD bit 35162306a36Sopenharmony_ci * should not be set in these speed modes. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci case MMC_TIMING_SD_HS: 35462306a36Sopenharmony_ci case MMC_TIMING_MMC_HS: 35562306a36Sopenharmony_ci val &= ~SDHCI_CTRL_HISPD; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci writeb(val, host->ioaddr + reg); 36062306a36Sopenharmony_ci if (reg == SDHCI_POWER_CONTROL && (val & SDHCI_POWER_ON)) { 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * Power on will not happen until the card detect debounce 36362306a36Sopenharmony_ci * timer expires. Wait at least 1.5 seconds for the power on 36462306a36Sopenharmony_ci * bit to be set 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci ret = read_poll_timeout(sdhci_am654_write_power_on, pwr, 36762306a36Sopenharmony_ci pwr & SDHCI_POWER_ON, 0, 36862306a36Sopenharmony_ci MAX_POWER_ON_TIMEOUT, false, host, val, 36962306a36Sopenharmony_ci reg); 37062306a36Sopenharmony_ci if (ret) 37162306a36Sopenharmony_ci dev_info(mmc_dev(host->mmc), "Power on failed\n"); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void sdhci_am654_reset(struct sdhci_host *host, u8 mask) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci u8 ctrl; 37862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 37962306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci sdhci_and_cqhci_reset(host, mask); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (sdhci_am654->quirks & SDHCI_AM654_QUIRK_FORCE_CDTEST) { 38462306a36Sopenharmony_ci ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 38562306a36Sopenharmony_ci ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN; 38662306a36Sopenharmony_ci sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int sdhci_am654_execute_tuning(struct mmc_host *mmc, u32 opcode) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 39362306a36Sopenharmony_ci int err = sdhci_execute_tuning(mmc, opcode); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (err) 39662306a36Sopenharmony_ci return err; 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Tuning data remains in the buffer after tuning. 39962306a36Sopenharmony_ci * Do a command and data reset to get rid of it 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci int cmd_error = 0; 40962306a36Sopenharmony_ci int data_error = 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) 41262306a36Sopenharmony_ci return intmask; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci cqhci_irq(host->mmc, intmask, cmd_error, data_error); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci#define ITAP_MAX 32 42062306a36Sopenharmony_cistatic int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, 42162306a36Sopenharmony_ci u32 opcode) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 42462306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); 42562306a36Sopenharmony_ci int cur_val, prev_val = 1, fail_len = 0, pass_window = 0, pass_len; 42662306a36Sopenharmony_ci u32 itap; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Enable ITAPDLY */ 42962306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYENA_MASK, 43062306a36Sopenharmony_ci 1 << ITAPDLYENA_SHIFT); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci for (itap = 0; itap < ITAP_MAX; itap++) { 43362306a36Sopenharmony_ci sdhci_am654_write_itapdly(sdhci_am654, itap); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci cur_val = !mmc_send_tuning(host->mmc, opcode, NULL); 43662306a36Sopenharmony_ci if (cur_val && !prev_val) 43762306a36Sopenharmony_ci pass_window = itap; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!cur_val) 44062306a36Sopenharmony_ci fail_len++; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci prev_val = cur_val; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci /* 44562306a36Sopenharmony_ci * Having determined the length of the failing window and start of 44662306a36Sopenharmony_ci * the passing window calculate the length of the passing window and 44762306a36Sopenharmony_ci * set the final value halfway through it considering the range as a 44862306a36Sopenharmony_ci * circular buffer 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci pass_len = ITAP_MAX - fail_len; 45162306a36Sopenharmony_ci itap = (pass_window + (pass_len >> 1)) % ITAP_MAX; 45262306a36Sopenharmony_ci sdhci_am654_write_itapdly(sdhci_am654, itap); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic struct sdhci_ops sdhci_am654_ops = { 45862306a36Sopenharmony_ci .platform_execute_tuning = sdhci_am654_platform_execute_tuning, 45962306a36Sopenharmony_ci .get_max_clock = sdhci_pltfm_clk_get_max_clock, 46062306a36Sopenharmony_ci .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, 46162306a36Sopenharmony_ci .set_uhs_signaling = sdhci_set_uhs_signaling, 46262306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 46362306a36Sopenharmony_ci .set_power = sdhci_set_power_and_bus_voltage, 46462306a36Sopenharmony_ci .set_clock = sdhci_am654_set_clock, 46562306a36Sopenharmony_ci .write_b = sdhci_am654_write_b, 46662306a36Sopenharmony_ci .irq = sdhci_am654_cqhci_irq, 46762306a36Sopenharmony_ci .reset = sdhci_and_cqhci_reset, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_am654_pdata = { 47162306a36Sopenharmony_ci .ops = &sdhci_am654_ops, 47262306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, 47362306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 47462306a36Sopenharmony_ci}; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic const struct sdhci_am654_driver_data sdhci_am654_sr1_drvdata = { 47762306a36Sopenharmony_ci .pdata = &sdhci_am654_pdata, 47862306a36Sopenharmony_ci .flags = IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT | DLL_PRESENT | 47962306a36Sopenharmony_ci DLL_CALIB, 48062306a36Sopenharmony_ci}; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic const struct sdhci_am654_driver_data sdhci_am654_drvdata = { 48362306a36Sopenharmony_ci .pdata = &sdhci_am654_pdata, 48462306a36Sopenharmony_ci .flags = IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT | DLL_PRESENT, 48562306a36Sopenharmony_ci}; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic struct sdhci_ops sdhci_j721e_8bit_ops = { 48862306a36Sopenharmony_ci .platform_execute_tuning = sdhci_am654_platform_execute_tuning, 48962306a36Sopenharmony_ci .get_max_clock = sdhci_pltfm_clk_get_max_clock, 49062306a36Sopenharmony_ci .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, 49162306a36Sopenharmony_ci .set_uhs_signaling = sdhci_set_uhs_signaling, 49262306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 49362306a36Sopenharmony_ci .set_power = sdhci_set_power_and_bus_voltage, 49462306a36Sopenharmony_ci .set_clock = sdhci_am654_set_clock, 49562306a36Sopenharmony_ci .write_b = sdhci_am654_write_b, 49662306a36Sopenharmony_ci .irq = sdhci_am654_cqhci_irq, 49762306a36Sopenharmony_ci .reset = sdhci_and_cqhci_reset, 49862306a36Sopenharmony_ci}; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_j721e_8bit_pdata = { 50162306a36Sopenharmony_ci .ops = &sdhci_j721e_8bit_ops, 50262306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, 50362306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic const struct sdhci_am654_driver_data sdhci_j721e_8bit_drvdata = { 50762306a36Sopenharmony_ci .pdata = &sdhci_j721e_8bit_pdata, 50862306a36Sopenharmony_ci .flags = DLL_PRESENT | DLL_CALIB, 50962306a36Sopenharmony_ci}; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic struct sdhci_ops sdhci_j721e_4bit_ops = { 51262306a36Sopenharmony_ci .platform_execute_tuning = sdhci_am654_platform_execute_tuning, 51362306a36Sopenharmony_ci .get_max_clock = sdhci_pltfm_clk_get_max_clock, 51462306a36Sopenharmony_ci .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, 51562306a36Sopenharmony_ci .set_uhs_signaling = sdhci_set_uhs_signaling, 51662306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 51762306a36Sopenharmony_ci .set_power = sdhci_set_power_and_bus_voltage, 51862306a36Sopenharmony_ci .set_clock = sdhci_j721e_4bit_set_clock, 51962306a36Sopenharmony_ci .write_b = sdhci_am654_write_b, 52062306a36Sopenharmony_ci .irq = sdhci_am654_cqhci_irq, 52162306a36Sopenharmony_ci .reset = sdhci_am654_reset, 52262306a36Sopenharmony_ci}; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_j721e_4bit_pdata = { 52562306a36Sopenharmony_ci .ops = &sdhci_j721e_4bit_ops, 52662306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, 52762306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 52862306a36Sopenharmony_ci}; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = { 53162306a36Sopenharmony_ci .pdata = &sdhci_j721e_4bit_pdata, 53262306a36Sopenharmony_ci .flags = IOMUX_PRESENT, 53362306a36Sopenharmony_ci}; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic const struct soc_device_attribute sdhci_am654_devices[] = { 53662306a36Sopenharmony_ci { .family = "AM65X", 53762306a36Sopenharmony_ci .revision = "SR1.0", 53862306a36Sopenharmony_ci .data = &sdhci_am654_sr1_drvdata 53962306a36Sopenharmony_ci }, 54062306a36Sopenharmony_ci {/* sentinel */} 54162306a36Sopenharmony_ci}; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic void sdhci_am654_dumpregs(struct mmc_host *mmc) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci sdhci_dumpregs(mmc_priv(mmc)); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic const struct cqhci_host_ops sdhci_am654_cqhci_ops = { 54962306a36Sopenharmony_ci .enable = sdhci_cqe_enable, 55062306a36Sopenharmony_ci .disable = sdhci_cqe_disable, 55162306a36Sopenharmony_ci .dumpregs = sdhci_am654_dumpregs, 55262306a36Sopenharmony_ci}; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic int sdhci_am654_cqe_add_host(struct sdhci_host *host) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct cqhci_host *cq_host; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci cq_host = devm_kzalloc(mmc_dev(host->mmc), sizeof(struct cqhci_host), 55962306a36Sopenharmony_ci GFP_KERNEL); 56062306a36Sopenharmony_ci if (!cq_host) 56162306a36Sopenharmony_ci return -ENOMEM; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci cq_host->mmio = host->ioaddr + SDHCI_AM654_CQE_BASE_ADDR; 56462306a36Sopenharmony_ci cq_host->quirks |= CQHCI_QUIRK_SHORT_TXFR_DESC_SZ; 56562306a36Sopenharmony_ci cq_host->caps |= CQHCI_TASK_DESC_SZ_128; 56662306a36Sopenharmony_ci cq_host->ops = &sdhci_am654_cqhci_ops; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci host->mmc->caps2 |= MMC_CAP2_CQE; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return cqhci_init(cq_host, host->mmc, 1); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int sdhci_am654_get_otap_delay(struct sdhci_host *host, 57462306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct device *dev = mmc_dev(host->mmc); 57762306a36Sopenharmony_ci int i; 57862306a36Sopenharmony_ci int ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ret = device_property_read_u32(dev, td[MMC_TIMING_LEGACY].otap_binding, 58162306a36Sopenharmony_ci &sdhci_am654->otap_del_sel[MMC_TIMING_LEGACY]); 58262306a36Sopenharmony_ci if (ret) { 58362306a36Sopenharmony_ci /* 58462306a36Sopenharmony_ci * ti,otap-del-sel-legacy is mandatory, look for old binding 58562306a36Sopenharmony_ci * if not found. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci ret = device_property_read_u32(dev, "ti,otap-del-sel", 58862306a36Sopenharmony_ci &sdhci_am654->otap_del_sel[0]); 58962306a36Sopenharmony_ci if (ret) { 59062306a36Sopenharmony_ci dev_err(dev, "Couldn't find otap-del-sel\n"); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci dev_info(dev, "Using legacy binding ti,otap-del-sel\n"); 59662306a36Sopenharmony_ci sdhci_am654->legacy_otapdly = true; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return 0; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci for (i = MMC_TIMING_LEGACY; i <= MMC_TIMING_MMC_HS400; i++) { 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ret = device_property_read_u32(dev, td[i].otap_binding, 60462306a36Sopenharmony_ci &sdhci_am654->otap_del_sel[i]); 60562306a36Sopenharmony_ci if (ret) { 60662306a36Sopenharmony_ci dev_dbg(dev, "Couldn't find %s\n", 60762306a36Sopenharmony_ci td[i].otap_binding); 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * Remove the corresponding capability 61062306a36Sopenharmony_ci * if an otap-del-sel value is not found 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci if (i <= MMC_TIMING_MMC_DDR52) 61362306a36Sopenharmony_ci host->mmc->caps &= ~td[i].capability; 61462306a36Sopenharmony_ci else 61562306a36Sopenharmony_ci host->mmc->caps2 &= ~td[i].capability; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (td[i].itap_binding) 61962306a36Sopenharmony_ci device_property_read_u32(dev, td[i].itap_binding, 62062306a36Sopenharmony_ci &sdhci_am654->itap_del_sel[i]); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci return 0; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic int sdhci_am654_init(struct sdhci_host *host) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 62962306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); 63062306a36Sopenharmony_ci u32 ctl_cfg_2 = 0; 63162306a36Sopenharmony_ci u32 mask; 63262306a36Sopenharmony_ci u32 val; 63362306a36Sopenharmony_ci int ret; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Reset OTAP to default value */ 63662306a36Sopenharmony_ci mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; 63762306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, 0x0); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (sdhci_am654->flags & DLL_CALIB) { 64062306a36Sopenharmony_ci regmap_read(sdhci_am654->base, PHY_STAT1, &val); 64162306a36Sopenharmony_ci if (~val & CALDONE_MASK) { 64262306a36Sopenharmony_ci /* Calibrate IO lines */ 64362306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL1, 64462306a36Sopenharmony_ci PDB_MASK, PDB_MASK); 64562306a36Sopenharmony_ci ret = regmap_read_poll_timeout(sdhci_am654->base, 64662306a36Sopenharmony_ci PHY_STAT1, val, 64762306a36Sopenharmony_ci val & CALDONE_MASK, 64862306a36Sopenharmony_ci 1, 20); 64962306a36Sopenharmony_ci if (ret) 65062306a36Sopenharmony_ci return ret; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* Enable pins by setting IO mux to 0 */ 65562306a36Sopenharmony_ci if (sdhci_am654->flags & IOMUX_PRESENT) 65662306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL1, 65762306a36Sopenharmony_ci IOMUX_ENABLE_MASK, 0); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* Set slot type based on SD or eMMC */ 66062306a36Sopenharmony_ci if (host->mmc->caps & MMC_CAP_NONREMOVABLE) 66162306a36Sopenharmony_ci ctl_cfg_2 = SLOTTYPE_EMBEDDED; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, CTL_CFG_2, SLOTTYPE_MASK, 66462306a36Sopenharmony_ci ctl_cfg_2); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* Enable tuning for SDR50 */ 66762306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK, 66862306a36Sopenharmony_ci TUNINGFORSDR50_MASK); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = sdhci_setup_host(host); 67162306a36Sopenharmony_ci if (ret) 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci ret = sdhci_am654_cqe_add_host(host); 67562306a36Sopenharmony_ci if (ret) 67662306a36Sopenharmony_ci goto err_cleanup_host; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = sdhci_am654_get_otap_delay(host, sdhci_am654); 67962306a36Sopenharmony_ci if (ret) 68062306a36Sopenharmony_ci goto err_cleanup_host; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = __sdhci_add_host(host); 68362306a36Sopenharmony_ci if (ret) 68462306a36Sopenharmony_ci goto err_cleanup_host; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cierr_cleanup_host: 68962306a36Sopenharmony_ci sdhci_cleanup_host(host); 69062306a36Sopenharmony_ci return ret; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic int sdhci_am654_get_of_property(struct platform_device *pdev, 69462306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 69762306a36Sopenharmony_ci int drv_strength; 69862306a36Sopenharmony_ci int ret; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (sdhci_am654->flags & DLL_PRESENT) { 70162306a36Sopenharmony_ci ret = device_property_read_u32(dev, "ti,trm-icp", 70262306a36Sopenharmony_ci &sdhci_am654->trm_icp); 70362306a36Sopenharmony_ci if (ret) 70462306a36Sopenharmony_ci return ret; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci ret = device_property_read_u32(dev, "ti,driver-strength-ohm", 70762306a36Sopenharmony_ci &drv_strength); 70862306a36Sopenharmony_ci if (ret) 70962306a36Sopenharmony_ci return ret; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci switch (drv_strength) { 71262306a36Sopenharmony_ci case 50: 71362306a36Sopenharmony_ci sdhci_am654->drv_strength = DRIVER_STRENGTH_50_OHM; 71462306a36Sopenharmony_ci break; 71562306a36Sopenharmony_ci case 33: 71662306a36Sopenharmony_ci sdhci_am654->drv_strength = DRIVER_STRENGTH_33_OHM; 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci case 66: 71962306a36Sopenharmony_ci sdhci_am654->drv_strength = DRIVER_STRENGTH_66_OHM; 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci case 100: 72262306a36Sopenharmony_ci sdhci_am654->drv_strength = DRIVER_STRENGTH_100_OHM; 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci case 40: 72562306a36Sopenharmony_ci sdhci_am654->drv_strength = DRIVER_STRENGTH_40_OHM; 72662306a36Sopenharmony_ci break; 72762306a36Sopenharmony_ci default: 72862306a36Sopenharmony_ci dev_err(dev, "Invalid driver strength\n"); 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci device_property_read_u32(dev, "ti,strobe-sel", &sdhci_am654->strb_sel); 73462306a36Sopenharmony_ci device_property_read_u32(dev, "ti,clkbuf-sel", 73562306a36Sopenharmony_ci &sdhci_am654->clkbuf_sel); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (device_property_read_bool(dev, "ti,fails-without-test-cd")) 73862306a36Sopenharmony_ci sdhci_am654->quirks |= SDHCI_AM654_QUIRK_FORCE_CDTEST; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci sdhci_get_of_property(pdev); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic const struct of_device_id sdhci_am654_of_match[] = { 74662306a36Sopenharmony_ci { 74762306a36Sopenharmony_ci .compatible = "ti,am654-sdhci-5.1", 74862306a36Sopenharmony_ci .data = &sdhci_am654_drvdata, 74962306a36Sopenharmony_ci }, 75062306a36Sopenharmony_ci { 75162306a36Sopenharmony_ci .compatible = "ti,j721e-sdhci-8bit", 75262306a36Sopenharmony_ci .data = &sdhci_j721e_8bit_drvdata, 75362306a36Sopenharmony_ci }, 75462306a36Sopenharmony_ci { 75562306a36Sopenharmony_ci .compatible = "ti,j721e-sdhci-4bit", 75662306a36Sopenharmony_ci .data = &sdhci_j721e_4bit_drvdata, 75762306a36Sopenharmony_ci }, 75862306a36Sopenharmony_ci { 75962306a36Sopenharmony_ci .compatible = "ti,am64-sdhci-8bit", 76062306a36Sopenharmony_ci .data = &sdhci_j721e_8bit_drvdata, 76162306a36Sopenharmony_ci }, 76262306a36Sopenharmony_ci { 76362306a36Sopenharmony_ci .compatible = "ti,am64-sdhci-4bit", 76462306a36Sopenharmony_ci .data = &sdhci_j721e_4bit_drvdata, 76562306a36Sopenharmony_ci }, 76662306a36Sopenharmony_ci { 76762306a36Sopenharmony_ci .compatible = "ti,am62-sdhci", 76862306a36Sopenharmony_ci .data = &sdhci_j721e_4bit_drvdata, 76962306a36Sopenharmony_ci }, 77062306a36Sopenharmony_ci { /* sentinel */ } 77162306a36Sopenharmony_ci}; 77262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sdhci_am654_of_match); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int sdhci_am654_probe(struct platform_device *pdev) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci const struct sdhci_am654_driver_data *drvdata; 77762306a36Sopenharmony_ci const struct soc_device_attribute *soc; 77862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 77962306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654; 78062306a36Sopenharmony_ci const struct of_device_id *match; 78162306a36Sopenharmony_ci struct sdhci_host *host; 78262306a36Sopenharmony_ci struct clk *clk_xin; 78362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 78462306a36Sopenharmony_ci void __iomem *base; 78562306a36Sopenharmony_ci int ret; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci match = of_match_node(sdhci_am654_of_match, pdev->dev.of_node); 78862306a36Sopenharmony_ci drvdata = match->data; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* Update drvdata based on SoC revision */ 79162306a36Sopenharmony_ci soc = soc_device_match(sdhci_am654_devices); 79262306a36Sopenharmony_ci if (soc && soc->data) 79362306a36Sopenharmony_ci drvdata = soc->data; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci host = sdhci_pltfm_init(pdev, drvdata->pdata, sizeof(*sdhci_am654)); 79662306a36Sopenharmony_ci if (IS_ERR(host)) 79762306a36Sopenharmony_ci return PTR_ERR(host); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 80062306a36Sopenharmony_ci sdhci_am654 = sdhci_pltfm_priv(pltfm_host); 80162306a36Sopenharmony_ci sdhci_am654->flags = drvdata->flags; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci clk_xin = devm_clk_get(dev, "clk_xin"); 80462306a36Sopenharmony_ci if (IS_ERR(clk_xin)) { 80562306a36Sopenharmony_ci dev_err(dev, "clk_xin clock not found.\n"); 80662306a36Sopenharmony_ci ret = PTR_ERR(clk_xin); 80762306a36Sopenharmony_ci goto err_pltfm_free; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci pltfm_host->clk = clk_xin; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 1); 81362306a36Sopenharmony_ci if (IS_ERR(base)) { 81462306a36Sopenharmony_ci ret = PTR_ERR(base); 81562306a36Sopenharmony_ci goto err_pltfm_free; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci sdhci_am654->base = devm_regmap_init_mmio(dev, base, 81962306a36Sopenharmony_ci &sdhci_am654_regmap_config); 82062306a36Sopenharmony_ci if (IS_ERR(sdhci_am654->base)) { 82162306a36Sopenharmony_ci dev_err(dev, "Failed to initialize regmap\n"); 82262306a36Sopenharmony_ci ret = PTR_ERR(sdhci_am654->base); 82362306a36Sopenharmony_ci goto err_pltfm_free; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci ret = sdhci_am654_get_of_property(pdev, sdhci_am654); 82762306a36Sopenharmony_ci if (ret) 82862306a36Sopenharmony_ci goto err_pltfm_free; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci ret = mmc_of_parse(host->mmc); 83162306a36Sopenharmony_ci if (ret) { 83262306a36Sopenharmony_ci dev_err_probe(dev, ret, "parsing dt failed\n"); 83362306a36Sopenharmony_ci goto err_pltfm_free; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci pm_runtime_get_noresume(dev); 83962306a36Sopenharmony_ci ret = pm_runtime_set_active(dev); 84062306a36Sopenharmony_ci if (ret) 84162306a36Sopenharmony_ci goto pm_put; 84262306a36Sopenharmony_ci pm_runtime_enable(dev); 84362306a36Sopenharmony_ci ret = clk_prepare_enable(pltfm_host->clk); 84462306a36Sopenharmony_ci if (ret) 84562306a36Sopenharmony_ci goto pm_disable; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci ret = sdhci_am654_init(host); 84862306a36Sopenharmony_ci if (ret) 84962306a36Sopenharmony_ci goto clk_disable; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* Setting up autosuspend */ 85262306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, SDHCI_AM654_AUTOSUSPEND_DELAY); 85362306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 85462306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 85562306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 85662306a36Sopenharmony_ci return 0; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ciclk_disable: 85962306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 86062306a36Sopenharmony_cipm_disable: 86162306a36Sopenharmony_ci pm_runtime_disable(dev); 86262306a36Sopenharmony_cipm_put: 86362306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 86462306a36Sopenharmony_cierr_pltfm_free: 86562306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 86662306a36Sopenharmony_ci return ret; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic void sdhci_am654_remove(struct platform_device *pdev) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct sdhci_host *host = platform_get_drvdata(pdev); 87262306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 87362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 87462306a36Sopenharmony_ci int ret; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev); 87762306a36Sopenharmony_ci if (ret < 0) 87862306a36Sopenharmony_ci dev_err(dev, "pm_runtime_get_sync() Failed\n"); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci sdhci_remove_host(host, true); 88162306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 88262306a36Sopenharmony_ci pm_runtime_disable(dev); 88362306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 88462306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci#ifdef CONFIG_PM 88862306a36Sopenharmony_cistatic int sdhci_am654_restore(struct sdhci_host *host) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 89162306a36Sopenharmony_ci struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); 89262306a36Sopenharmony_ci u32 ctl_cfg_2 = 0; 89362306a36Sopenharmony_ci u32 val; 89462306a36Sopenharmony_ci int ret; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (sdhci_am654->flags & DLL_CALIB) { 89762306a36Sopenharmony_ci regmap_read(sdhci_am654->base, PHY_STAT1, &val); 89862306a36Sopenharmony_ci if (~val & CALDONE_MASK) { 89962306a36Sopenharmony_ci /* Calibrate IO lines */ 90062306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL1, 90162306a36Sopenharmony_ci PDB_MASK, PDB_MASK); 90262306a36Sopenharmony_ci ret = regmap_read_poll_timeout(sdhci_am654->base, 90362306a36Sopenharmony_ci PHY_STAT1, val, 90462306a36Sopenharmony_ci val & CALDONE_MASK, 90562306a36Sopenharmony_ci 1, 20); 90662306a36Sopenharmony_ci if (ret) 90762306a36Sopenharmony_ci return ret; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* Enable pins by setting IO mux to 0 */ 91262306a36Sopenharmony_ci if (sdhci_am654->flags & IOMUX_PRESENT) 91362306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, PHY_CTRL1, 91462306a36Sopenharmony_ci IOMUX_ENABLE_MASK, 0); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Set slot type based on SD or eMMC */ 91762306a36Sopenharmony_ci if (host->mmc->caps & MMC_CAP_NONREMOVABLE) 91862306a36Sopenharmony_ci ctl_cfg_2 = SLOTTYPE_EMBEDDED; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, CTL_CFG_2, SLOTTYPE_MASK, 92162306a36Sopenharmony_ci ctl_cfg_2); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci regmap_read(sdhci_am654->base, CTL_CFG_3, &val); 92462306a36Sopenharmony_ci if (~val & TUNINGFORSDR50_MASK) 92562306a36Sopenharmony_ci /* Enable tuning for SDR50 */ 92662306a36Sopenharmony_ci regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK, 92762306a36Sopenharmony_ci TUNINGFORSDR50_MASK); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return 0; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic int sdhci_am654_runtime_suspend(struct device *dev) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 93562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 93662306a36Sopenharmony_ci int ret; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (host->tuning_mode != SDHCI_TUNING_MODE_3) 93962306a36Sopenharmony_ci mmc_retune_needed(host->mmc); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci ret = cqhci_suspend(host->mmc); 94262306a36Sopenharmony_ci if (ret) 94362306a36Sopenharmony_ci return ret; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci ret = sdhci_runtime_suspend_host(host); 94662306a36Sopenharmony_ci if (ret) 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* disable the clock */ 95062306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 95162306a36Sopenharmony_ci return 0; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic int sdhci_am654_runtime_resume(struct device *dev) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 95762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 95862306a36Sopenharmony_ci int ret; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* Enable the clock */ 96162306a36Sopenharmony_ci ret = clk_prepare_enable(pltfm_host->clk); 96262306a36Sopenharmony_ci if (ret) 96362306a36Sopenharmony_ci return ret; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci ret = sdhci_am654_restore(host); 96662306a36Sopenharmony_ci if (ret) 96762306a36Sopenharmony_ci return ret; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci ret = sdhci_runtime_resume_host(host, 0); 97062306a36Sopenharmony_ci if (ret) 97162306a36Sopenharmony_ci return ret; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci ret = cqhci_resume(host->mmc); 97462306a36Sopenharmony_ci if (ret) 97562306a36Sopenharmony_ci return ret; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci#endif 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic const struct dev_pm_ops sdhci_am654_dev_pm_ops = { 98262306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(sdhci_am654_runtime_suspend, 98362306a36Sopenharmony_ci sdhci_am654_runtime_resume, NULL) 98462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 98562306a36Sopenharmony_ci pm_runtime_force_resume) 98662306a36Sopenharmony_ci}; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic struct platform_driver sdhci_am654_driver = { 98962306a36Sopenharmony_ci .driver = { 99062306a36Sopenharmony_ci .name = "sdhci-am654", 99162306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 99262306a36Sopenharmony_ci .pm = &sdhci_am654_dev_pm_ops, 99362306a36Sopenharmony_ci .of_match_table = sdhci_am654_of_match, 99462306a36Sopenharmony_ci }, 99562306a36Sopenharmony_ci .probe = sdhci_am654_probe, 99662306a36Sopenharmony_ci .remove_new = sdhci_am654_remove, 99762306a36Sopenharmony_ci}; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cimodule_platform_driver(sdhci_am654_driver); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for SDHCI Controller on TI's AM654 devices"); 100262306a36Sopenharmony_ciMODULE_AUTHOR("Faiz Abbas <faiz_abbas@ti.com>"); 100362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1004