1e3ec3a3dSSoren Brinkmann /* 2e3ec3a3dSSoren Brinkmann * Arasan Secure Digital Host Controller Interface. 3e3ec3a3dSSoren Brinkmann * Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu> 4e3ec3a3dSSoren Brinkmann * Copyright (c) 2012 Wind River Systems, Inc. 5e3ec3a3dSSoren Brinkmann * Copyright (C) 2013 Pengutronix e.K. 6e3ec3a3dSSoren Brinkmann * Copyright (C) 2013 Xilinx Inc. 7e3ec3a3dSSoren Brinkmann * 8e3ec3a3dSSoren Brinkmann * Based on sdhci-of-esdhc.c 9e3ec3a3dSSoren Brinkmann * 10e3ec3a3dSSoren Brinkmann * Copyright (c) 2007 Freescale Semiconductor, Inc. 11e3ec3a3dSSoren Brinkmann * Copyright (c) 2009 MontaVista Software, Inc. 12e3ec3a3dSSoren Brinkmann * 13e3ec3a3dSSoren Brinkmann * Authors: Xiaobo Xie <X.Xie@freescale.com> 14e3ec3a3dSSoren Brinkmann * Anton Vorontsov <avorontsov@ru.mvista.com> 15e3ec3a3dSSoren Brinkmann * 16e3ec3a3dSSoren Brinkmann * This program is free software; you can redistribute it and/or modify 17e3ec3a3dSSoren Brinkmann * it under the terms of the GNU General Public License as published by 18e3ec3a3dSSoren Brinkmann * the Free Software Foundation; either version 2 of the License, or (at 19e3ec3a3dSSoren Brinkmann * your option) any later version. 20e3ec3a3dSSoren Brinkmann */ 21e3ec3a3dSSoren Brinkmann 22c390f211SDouglas Anderson #include <linux/clk-provider.h> 233ea4666eSDouglas Anderson #include <linux/mfd/syscon.h> 24e3ec3a3dSSoren Brinkmann #include <linux/module.h> 25308f3f8dSSuman Tripathi #include <linux/of_device.h> 2691aa3661SShawn Lin #include <linux/phy/phy.h> 273ea4666eSDouglas Anderson #include <linux/regmap.h> 28e3ec3a3dSSoren Brinkmann #include "sdhci-pltfm.h" 29e3ec3a3dSSoren Brinkmann 30e3ec3a3dSSoren Brinkmann #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c 31a05c8465SShawn Lin #define SDHCI_ARASAN_VENDOR_REGISTER 0x78 32e3ec3a3dSSoren Brinkmann 33a05c8465SShawn Lin #define VENDOR_ENHANCED_STROBE BIT(0) 34e3ec3a3dSSoren Brinkmann #define CLK_CTRL_TIMEOUT_SHIFT 16 35e3ec3a3dSSoren Brinkmann #define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) 36e3ec3a3dSSoren Brinkmann #define CLK_CTRL_TIMEOUT_MIN_EXP 13 37e3ec3a3dSSoren Brinkmann 38b2db9c67SDouglas Anderson #define PHY_CLK_TOO_SLOW_HZ 400000 39b2db9c67SDouglas Anderson 403ea4666eSDouglas Anderson /* 413ea4666eSDouglas Anderson * On some SoCs the syscon area has a feature where the upper 16-bits of 423ea4666eSDouglas Anderson * each 32-bit register act as a write mask for the lower 16-bits. This allows 433ea4666eSDouglas Anderson * atomic updates of the register without locking. This macro is used on SoCs 443ea4666eSDouglas Anderson * that have that feature. 453ea4666eSDouglas Anderson */ 463ea4666eSDouglas Anderson #define HIWORD_UPDATE(val, mask, shift) \ 473ea4666eSDouglas Anderson ((val) << (shift) | (mask) << ((shift) + 16)) 483ea4666eSDouglas Anderson 493ea4666eSDouglas Anderson /** 503ea4666eSDouglas Anderson * struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map 513ea4666eSDouglas Anderson * 523ea4666eSDouglas Anderson * @reg: Offset within the syscon of the register containing this field 533ea4666eSDouglas Anderson * @width: Number of bits for this field 543ea4666eSDouglas Anderson * @shift: Bit offset within @reg of this field (or -1 if not avail) 553ea4666eSDouglas Anderson */ 563ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_field { 573ea4666eSDouglas Anderson u32 reg; 583ea4666eSDouglas Anderson u16 width; 593ea4666eSDouglas Anderson s16 shift; 603ea4666eSDouglas Anderson }; 613ea4666eSDouglas Anderson 623ea4666eSDouglas Anderson /** 633ea4666eSDouglas Anderson * struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers 643ea4666eSDouglas Anderson * 653ea4666eSDouglas Anderson * It's up to the licensee of the Arsan IP block to make these available 663ea4666eSDouglas Anderson * somewhere if needed. Presumably these will be scattered somewhere that's 673ea4666eSDouglas Anderson * accessible via the syscon API. 683ea4666eSDouglas Anderson * 693ea4666eSDouglas Anderson * @baseclkfreq: Where to find corecfg_baseclkfreq 703ea4666eSDouglas Anderson * @hiword_update: If true, use HIWORD_UPDATE to access the syscon 713ea4666eSDouglas Anderson */ 723ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_map { 733ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_field baseclkfreq; 743ea4666eSDouglas Anderson bool hiword_update; 753ea4666eSDouglas Anderson }; 763ea4666eSDouglas Anderson 77e3ec3a3dSSoren Brinkmann /** 78e3ec3a3dSSoren Brinkmann * struct sdhci_arasan_data 79c390f211SDouglas Anderson * @host: Pointer to the main SDHCI host structure. 80e3ec3a3dSSoren Brinkmann * @clk_ahb: Pointer to the AHB clock 8191aa3661SShawn Lin * @phy: Pointer to the generic phy 82b2db9c67SDouglas Anderson * @is_phy_on: True if the PHY is on; false if not. 83c390f211SDouglas Anderson * @sdcardclk_hw: Struct for the clock we might provide to a PHY. 84c390f211SDouglas Anderson * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. 853ea4666eSDouglas Anderson * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. 863ea4666eSDouglas Anderson * @soc_ctl_map: Map to get offsets into soc_ctl registers. 87e3ec3a3dSSoren Brinkmann */ 88e3ec3a3dSSoren Brinkmann struct sdhci_arasan_data { 89c390f211SDouglas Anderson struct sdhci_host *host; 90e3ec3a3dSSoren Brinkmann struct clk *clk_ahb; 9191aa3661SShawn Lin struct phy *phy; 92b2db9c67SDouglas Anderson bool is_phy_on; 933ea4666eSDouglas Anderson 94c390f211SDouglas Anderson struct clk_hw sdcardclk_hw; 95c390f211SDouglas Anderson struct clk *sdcardclk; 96c390f211SDouglas Anderson 973ea4666eSDouglas Anderson struct regmap *soc_ctl_base; 983ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; 99e3ec3a3dSSoren Brinkmann }; 100e3ec3a3dSSoren Brinkmann 1013ea4666eSDouglas Anderson static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { 1023ea4666eSDouglas Anderson .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 }, 1033ea4666eSDouglas Anderson .hiword_update = true, 1043ea4666eSDouglas Anderson }; 1053ea4666eSDouglas Anderson 1063ea4666eSDouglas Anderson /** 1073ea4666eSDouglas Anderson * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers 1083ea4666eSDouglas Anderson * 1093ea4666eSDouglas Anderson * This function allows writing to fields in sdhci_arasan_soc_ctl_map. 1103ea4666eSDouglas Anderson * Note that if a field is specified as not available (shift < 0) then 1113ea4666eSDouglas Anderson * this function will silently return an error code. It will be noisy 1123ea4666eSDouglas Anderson * and print errors for any other (unexpected) errors. 1133ea4666eSDouglas Anderson * 1143ea4666eSDouglas Anderson * @host: The sdhci_host 1153ea4666eSDouglas Anderson * @fld: The field to write to 1163ea4666eSDouglas Anderson * @val: The value to write 1173ea4666eSDouglas Anderson */ 1183ea4666eSDouglas Anderson static int sdhci_arasan_syscon_write(struct sdhci_host *host, 1193ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_field *fld, 1203ea4666eSDouglas Anderson u32 val) 1213ea4666eSDouglas Anderson { 1223ea4666eSDouglas Anderson struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1233ea4666eSDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 1243ea4666eSDouglas Anderson struct regmap *soc_ctl_base = sdhci_arasan->soc_ctl_base; 1253ea4666eSDouglas Anderson u32 reg = fld->reg; 1263ea4666eSDouglas Anderson u16 width = fld->width; 1273ea4666eSDouglas Anderson s16 shift = fld->shift; 1283ea4666eSDouglas Anderson int ret; 1293ea4666eSDouglas Anderson 1303ea4666eSDouglas Anderson /* 1313ea4666eSDouglas Anderson * Silently return errors for shift < 0 so caller doesn't have 1323ea4666eSDouglas Anderson * to check for fields which are optional. For fields that 1333ea4666eSDouglas Anderson * are required then caller needs to do something special 1343ea4666eSDouglas Anderson * anyway. 1353ea4666eSDouglas Anderson */ 1363ea4666eSDouglas Anderson if (shift < 0) 1373ea4666eSDouglas Anderson return -EINVAL; 1383ea4666eSDouglas Anderson 1393ea4666eSDouglas Anderson if (sdhci_arasan->soc_ctl_map->hiword_update) 1403ea4666eSDouglas Anderson ret = regmap_write(soc_ctl_base, reg, 1413ea4666eSDouglas Anderson HIWORD_UPDATE(val, GENMASK(width, 0), 1423ea4666eSDouglas Anderson shift)); 1433ea4666eSDouglas Anderson else 1443ea4666eSDouglas Anderson ret = regmap_update_bits(soc_ctl_base, reg, 1453ea4666eSDouglas Anderson GENMASK(shift + width, shift), 1463ea4666eSDouglas Anderson val << shift); 1473ea4666eSDouglas Anderson 1483ea4666eSDouglas Anderson /* Yell about (unexpected) regmap errors */ 1493ea4666eSDouglas Anderson if (ret) 1503ea4666eSDouglas Anderson pr_warn("%s: Regmap write fail: %d\n", 1513ea4666eSDouglas Anderson mmc_hostname(host->mmc), ret); 1523ea4666eSDouglas Anderson 1533ea4666eSDouglas Anderson return ret; 1543ea4666eSDouglas Anderson } 1553ea4666eSDouglas Anderson 156e3ec3a3dSSoren Brinkmann static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) 157e3ec3a3dSSoren Brinkmann { 158e3ec3a3dSSoren Brinkmann u32 div; 159e3ec3a3dSSoren Brinkmann unsigned long freq; 160e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 161e3ec3a3dSSoren Brinkmann 162e3ec3a3dSSoren Brinkmann div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET); 163e3ec3a3dSSoren Brinkmann div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT; 164e3ec3a3dSSoren Brinkmann 165e3ec3a3dSSoren Brinkmann freq = clk_get_rate(pltfm_host->clk); 166e3ec3a3dSSoren Brinkmann freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div); 167e3ec3a3dSSoren Brinkmann 168e3ec3a3dSSoren Brinkmann return freq; 169e3ec3a3dSSoren Brinkmann } 170e3ec3a3dSSoren Brinkmann 171802ac39aSShawn Lin static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) 172802ac39aSShawn Lin { 173802ac39aSShawn Lin struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 174802ac39aSShawn Lin struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 1756fc09244SDouglas Anderson bool ctrl_phy = false; 176802ac39aSShawn Lin 177b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy)) { 178b2db9c67SDouglas Anderson if (!sdhci_arasan->is_phy_on && clock <= PHY_CLK_TOO_SLOW_HZ) { 179b2db9c67SDouglas Anderson /* 180b2db9c67SDouglas Anderson * If PHY off, set clock to max speed and power PHY on. 181b2db9c67SDouglas Anderson * 182b2db9c67SDouglas Anderson * Although PHY docs apparently suggest power cycling 183b2db9c67SDouglas Anderson * when changing the clock the PHY doesn't like to be 184b2db9c67SDouglas Anderson * powered on while at low speeds like those used in ID 185b2db9c67SDouglas Anderson * mode. Even worse is powering the PHY on while the 186b2db9c67SDouglas Anderson * clock is off. 187b2db9c67SDouglas Anderson * 188b2db9c67SDouglas Anderson * To workaround the PHY limitations, the best we can 189b2db9c67SDouglas Anderson * do is to power it on at a faster speed and then slam 190b2db9c67SDouglas Anderson * through low speeds without power cycling. 191b2db9c67SDouglas Anderson */ 192b2db9c67SDouglas Anderson sdhci_set_clock(host, host->max_clk); 193b2db9c67SDouglas Anderson spin_unlock_irq(&host->lock); 194b2db9c67SDouglas Anderson phy_power_on(sdhci_arasan->phy); 195b2db9c67SDouglas Anderson spin_lock_irq(&host->lock); 196b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 197802ac39aSShawn Lin 198b2db9c67SDouglas Anderson /* 199b2db9c67SDouglas Anderson * We'll now fall through to the below case with 200b2db9c67SDouglas Anderson * ctrl_phy = false (so we won't turn off/on). The 201b2db9c67SDouglas Anderson * sdhci_set_clock() will set the real clock. 202b2db9c67SDouglas Anderson */ 203b2db9c67SDouglas Anderson } else if (clock > PHY_CLK_TOO_SLOW_HZ) { 204b2db9c67SDouglas Anderson /* 205b2db9c67SDouglas Anderson * At higher clock speeds the PHY is fine being power 206b2db9c67SDouglas Anderson * cycled and docs say you _should_ power cycle when 207b2db9c67SDouglas Anderson * changing clock speeds. 208b2db9c67SDouglas Anderson */ 209b2db9c67SDouglas Anderson ctrl_phy = true; 210b2db9c67SDouglas Anderson } 211b2db9c67SDouglas Anderson } 212b2db9c67SDouglas Anderson 213b2db9c67SDouglas Anderson if (ctrl_phy && sdhci_arasan->is_phy_on) { 214802ac39aSShawn Lin spin_unlock_irq(&host->lock); 215802ac39aSShawn Lin phy_power_off(sdhci_arasan->phy); 216802ac39aSShawn Lin spin_lock_irq(&host->lock); 217b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = false; 218802ac39aSShawn Lin } 219802ac39aSShawn Lin 220802ac39aSShawn Lin sdhci_set_clock(host, clock); 221802ac39aSShawn Lin 2226fc09244SDouglas Anderson if (ctrl_phy) { 223802ac39aSShawn Lin spin_unlock_irq(&host->lock); 224802ac39aSShawn Lin phy_power_on(sdhci_arasan->phy); 225802ac39aSShawn Lin spin_lock_irq(&host->lock); 226b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 227802ac39aSShawn Lin } 228802ac39aSShawn Lin } 229802ac39aSShawn Lin 230a05c8465SShawn Lin static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc, 231a05c8465SShawn Lin struct mmc_ios *ios) 232a05c8465SShawn Lin { 233a05c8465SShawn Lin u32 vendor; 234a05c8465SShawn Lin struct sdhci_host *host = mmc_priv(mmc); 235a05c8465SShawn Lin 236a05c8465SShawn Lin vendor = readl(host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); 237a05c8465SShawn Lin if (ios->enhanced_strobe) 238a05c8465SShawn Lin vendor |= VENDOR_ENHANCED_STROBE; 239a05c8465SShawn Lin else 240a05c8465SShawn Lin vendor &= ~VENDOR_ENHANCED_STROBE; 241a05c8465SShawn Lin 242a05c8465SShawn Lin writel(vendor, host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); 243a05c8465SShawn Lin } 244a05c8465SShawn Lin 245e3ec3a3dSSoren Brinkmann static struct sdhci_ops sdhci_arasan_ops = { 246802ac39aSShawn Lin .set_clock = sdhci_arasan_set_clock, 247e3ec3a3dSSoren Brinkmann .get_max_clock = sdhci_pltfm_clk_get_max_clock, 248e3ec3a3dSSoren Brinkmann .get_timeout_clock = sdhci_arasan_get_timeout_clock, 2492317f56cSRussell King .set_bus_width = sdhci_set_bus_width, 25003231f9bSRussell King .reset = sdhci_reset, 25196d7b78cSRussell King .set_uhs_signaling = sdhci_set_uhs_signaling, 252e3ec3a3dSSoren Brinkmann }; 253e3ec3a3dSSoren Brinkmann 254e3ec3a3dSSoren Brinkmann static struct sdhci_pltfm_data sdhci_arasan_pdata = { 255e3ec3a3dSSoren Brinkmann .ops = &sdhci_arasan_ops, 2562d532d45SSuneel Garapati .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 2572d532d45SSuneel Garapati .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 2582d532d45SSuneel Garapati SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, 259e3ec3a3dSSoren Brinkmann }; 260e3ec3a3dSSoren Brinkmann 261e3ec3a3dSSoren Brinkmann #ifdef CONFIG_PM_SLEEP 262e3ec3a3dSSoren Brinkmann /** 263e3ec3a3dSSoren Brinkmann * sdhci_arasan_suspend - Suspend method for the driver 264e3ec3a3dSSoren Brinkmann * @dev: Address of the device structure 265e3ec3a3dSSoren Brinkmann * Returns 0 on success and error value on error 266e3ec3a3dSSoren Brinkmann * 267e3ec3a3dSSoren Brinkmann * Put the device in a low power state. 268e3ec3a3dSSoren Brinkmann */ 269e3ec3a3dSSoren Brinkmann static int sdhci_arasan_suspend(struct device *dev) 270e3ec3a3dSSoren Brinkmann { 271e3ec3a3dSSoren Brinkmann struct platform_device *pdev = to_platform_device(dev); 272e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 273e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 27489211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 275e3ec3a3dSSoren Brinkmann int ret; 276e3ec3a3dSSoren Brinkmann 277e3ec3a3dSSoren Brinkmann ret = sdhci_suspend_host(host); 278e3ec3a3dSSoren Brinkmann if (ret) 279e3ec3a3dSSoren Brinkmann return ret; 280e3ec3a3dSSoren Brinkmann 281b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy) && sdhci_arasan->is_phy_on) { 28291aa3661SShawn Lin ret = phy_power_off(sdhci_arasan->phy); 28391aa3661SShawn Lin if (ret) { 28491aa3661SShawn Lin dev_err(dev, "Cannot power off phy.\n"); 28591aa3661SShawn Lin sdhci_resume_host(host); 28691aa3661SShawn Lin return ret; 28791aa3661SShawn Lin } 288b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = false; 28991aa3661SShawn Lin } 29091aa3661SShawn Lin 291e3ec3a3dSSoren Brinkmann clk_disable(pltfm_host->clk); 292e3ec3a3dSSoren Brinkmann clk_disable(sdhci_arasan->clk_ahb); 293e3ec3a3dSSoren Brinkmann 294e3ec3a3dSSoren Brinkmann return 0; 295e3ec3a3dSSoren Brinkmann } 296e3ec3a3dSSoren Brinkmann 297e3ec3a3dSSoren Brinkmann /** 298e3ec3a3dSSoren Brinkmann * sdhci_arasan_resume - Resume method for the driver 299e3ec3a3dSSoren Brinkmann * @dev: Address of the device structure 300e3ec3a3dSSoren Brinkmann * Returns 0 on success and error value on error 301e3ec3a3dSSoren Brinkmann * 302e3ec3a3dSSoren Brinkmann * Resume operation after suspend 303e3ec3a3dSSoren Brinkmann */ 304e3ec3a3dSSoren Brinkmann static int sdhci_arasan_resume(struct device *dev) 305e3ec3a3dSSoren Brinkmann { 306e3ec3a3dSSoren Brinkmann struct platform_device *pdev = to_platform_device(dev); 307e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 308e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 30989211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 310e3ec3a3dSSoren Brinkmann int ret; 311e3ec3a3dSSoren Brinkmann 312e3ec3a3dSSoren Brinkmann ret = clk_enable(sdhci_arasan->clk_ahb); 313e3ec3a3dSSoren Brinkmann if (ret) { 314e3ec3a3dSSoren Brinkmann dev_err(dev, "Cannot enable AHB clock.\n"); 315e3ec3a3dSSoren Brinkmann return ret; 316e3ec3a3dSSoren Brinkmann } 317e3ec3a3dSSoren Brinkmann 318e3ec3a3dSSoren Brinkmann ret = clk_enable(pltfm_host->clk); 319e3ec3a3dSSoren Brinkmann if (ret) { 320e3ec3a3dSSoren Brinkmann dev_err(dev, "Cannot enable SD clock.\n"); 321e3ec3a3dSSoren Brinkmann return ret; 322e3ec3a3dSSoren Brinkmann } 323e3ec3a3dSSoren Brinkmann 324b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy) && host->mmc->actual_clock) { 32591aa3661SShawn Lin ret = phy_power_on(sdhci_arasan->phy); 32691aa3661SShawn Lin if (ret) { 32791aa3661SShawn Lin dev_err(dev, "Cannot power on phy.\n"); 32891aa3661SShawn Lin return ret; 32991aa3661SShawn Lin } 330b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 33191aa3661SShawn Lin } 33291aa3661SShawn Lin 333e3ec3a3dSSoren Brinkmann return sdhci_resume_host(host); 334e3ec3a3dSSoren Brinkmann } 335e3ec3a3dSSoren Brinkmann #endif /* ! CONFIG_PM_SLEEP */ 336e3ec3a3dSSoren Brinkmann 337e3ec3a3dSSoren Brinkmann static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, 338e3ec3a3dSSoren Brinkmann sdhci_arasan_resume); 339e3ec3a3dSSoren Brinkmann 3403ea4666eSDouglas Anderson static const struct of_device_id sdhci_arasan_of_match[] = { 3413ea4666eSDouglas Anderson /* SoC-specific compatible strings w/ soc_ctl_map */ 3423ea4666eSDouglas Anderson { 3433ea4666eSDouglas Anderson .compatible = "rockchip,rk3399-sdhci-5.1", 3443ea4666eSDouglas Anderson .data = &rk3399_soc_ctl_map, 3453ea4666eSDouglas Anderson }, 3463ea4666eSDouglas Anderson 3473ea4666eSDouglas Anderson /* Generic compatible below here */ 3483ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-8.9a" }, 3493ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-5.1" }, 3503ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-4.9a" }, 3513ea4666eSDouglas Anderson 3523ea4666eSDouglas Anderson { /* sentinel */ } 3533ea4666eSDouglas Anderson }; 3543ea4666eSDouglas Anderson MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); 3553ea4666eSDouglas Anderson 3563ea4666eSDouglas Anderson /** 357c390f211SDouglas Anderson * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate 358c390f211SDouglas Anderson * 359c390f211SDouglas Anderson * Return the current actual rate of the SD card clock. This can be used 360c390f211SDouglas Anderson * to communicate with out PHY. 361c390f211SDouglas Anderson * 362c390f211SDouglas Anderson * @hw: Pointer to the hardware clock structure. 363c390f211SDouglas Anderson * @parent_rate The parent rate (should be rate of clk_xin). 364c390f211SDouglas Anderson * Returns the card clock rate. 365c390f211SDouglas Anderson */ 366c390f211SDouglas Anderson static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw, 367c390f211SDouglas Anderson unsigned long parent_rate) 368c390f211SDouglas Anderson 369c390f211SDouglas Anderson { 370c390f211SDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = 371c390f211SDouglas Anderson container_of(hw, struct sdhci_arasan_data, sdcardclk_hw); 372c390f211SDouglas Anderson struct sdhci_host *host = sdhci_arasan->host; 373c390f211SDouglas Anderson 374c390f211SDouglas Anderson return host->mmc->actual_clock; 375c390f211SDouglas Anderson } 376c390f211SDouglas Anderson 377c390f211SDouglas Anderson static const struct clk_ops arasan_sdcardclk_ops = { 378c390f211SDouglas Anderson .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, 379c390f211SDouglas Anderson }; 380c390f211SDouglas Anderson 381c390f211SDouglas Anderson /** 3823ea4666eSDouglas Anderson * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq 3833ea4666eSDouglas Anderson * 3843ea4666eSDouglas Anderson * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This 3853ea4666eSDouglas Anderson * function can be used to make that happen. 3863ea4666eSDouglas Anderson * 3873ea4666eSDouglas Anderson * NOTES: 3883ea4666eSDouglas Anderson * - Many existing devices don't seem to do this and work fine. To keep 3893ea4666eSDouglas Anderson * compatibility for old hardware where the device tree doesn't provide a 3903ea4666eSDouglas Anderson * register map, this function is a noop if a soc_ctl_map hasn't been provided 3913ea4666eSDouglas Anderson * for this platform. 3923ea4666eSDouglas Anderson * - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider 3933ea4666eSDouglas Anderson * to achieve lower clock rates. That means that this function is called once 3943ea4666eSDouglas Anderson * at probe time and never called again. 3953ea4666eSDouglas Anderson * 3963ea4666eSDouglas Anderson * @host: The sdhci_host 3973ea4666eSDouglas Anderson */ 3983ea4666eSDouglas Anderson static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host) 3993ea4666eSDouglas Anderson { 4003ea4666eSDouglas Anderson struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4013ea4666eSDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 4023ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = 4033ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_map; 4043ea4666eSDouglas Anderson u32 mhz = DIV_ROUND_CLOSEST(clk_get_rate(pltfm_host->clk), 1000000); 4053ea4666eSDouglas Anderson 4063ea4666eSDouglas Anderson /* Having a map is optional */ 4073ea4666eSDouglas Anderson if (!soc_ctl_map) 4083ea4666eSDouglas Anderson return; 4093ea4666eSDouglas Anderson 4103ea4666eSDouglas Anderson /* If we have a map, we expect to have a syscon */ 4113ea4666eSDouglas Anderson if (!sdhci_arasan->soc_ctl_base) { 4123ea4666eSDouglas Anderson pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", 4133ea4666eSDouglas Anderson mmc_hostname(host->mmc)); 4143ea4666eSDouglas Anderson return; 4153ea4666eSDouglas Anderson } 4163ea4666eSDouglas Anderson 4173ea4666eSDouglas Anderson sdhci_arasan_syscon_write(host, &soc_ctl_map->baseclkfreq, mhz); 4183ea4666eSDouglas Anderson } 4193ea4666eSDouglas Anderson 420c390f211SDouglas Anderson /** 421c390f211SDouglas Anderson * sdhci_arasan_register_sdclk - Register the sdclk for a PHY to use 422c390f211SDouglas Anderson * 423c390f211SDouglas Anderson * Some PHY devices need to know what the actual card clock is. In order for 424c390f211SDouglas Anderson * them to find out, we'll provide a clock through the common clock framework 425c390f211SDouglas Anderson * for them to query. 426c390f211SDouglas Anderson * 427c390f211SDouglas Anderson * Note: without seriously re-architecting SDHCI's clock code and testing on 428c390f211SDouglas Anderson * all platforms, there's no way to create a totally beautiful clock here 429c390f211SDouglas Anderson * with all clock ops implemented. Instead, we'll just create a clock that can 430c390f211SDouglas Anderson * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock 431c390f211SDouglas Anderson * framework that we're doing things behind its back. This should be sufficient 432c390f211SDouglas Anderson * to create nice clean device tree bindings and later (if needed) we can try 433c390f211SDouglas Anderson * re-architecting SDHCI if we see some benefit to it. 434c390f211SDouglas Anderson * 435c390f211SDouglas Anderson * @sdhci_arasan: Our private data structure. 436c390f211SDouglas Anderson * @clk_xin: Pointer to the functional clock 437c390f211SDouglas Anderson * @dev: Pointer to our struct device. 438c390f211SDouglas Anderson * Returns 0 on success and error value on error 439c390f211SDouglas Anderson */ 440c390f211SDouglas Anderson static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan, 441c390f211SDouglas Anderson struct clk *clk_xin, 442c390f211SDouglas Anderson struct device *dev) 443c390f211SDouglas Anderson { 444c390f211SDouglas Anderson struct device_node *np = dev->of_node; 445c390f211SDouglas Anderson struct clk_init_data sdcardclk_init; 446c390f211SDouglas Anderson const char *parent_clk_name; 447c390f211SDouglas Anderson int ret; 448c390f211SDouglas Anderson 449c390f211SDouglas Anderson /* Providing a clock to the PHY is optional; no error if missing */ 450c390f211SDouglas Anderson if (!of_find_property(np, "#clock-cells", NULL)) 451c390f211SDouglas Anderson return 0; 452c390f211SDouglas Anderson 453c390f211SDouglas Anderson ret = of_property_read_string_index(np, "clock-output-names", 0, 454c390f211SDouglas Anderson &sdcardclk_init.name); 455c390f211SDouglas Anderson if (ret) { 456c390f211SDouglas Anderson dev_err(dev, "DT has #clock-cells but no clock-output-names\n"); 457c390f211SDouglas Anderson return ret; 458c390f211SDouglas Anderson } 459c390f211SDouglas Anderson 460c390f211SDouglas Anderson parent_clk_name = __clk_get_name(clk_xin); 461c390f211SDouglas Anderson sdcardclk_init.parent_names = &parent_clk_name; 462c390f211SDouglas Anderson sdcardclk_init.num_parents = 1; 463c390f211SDouglas Anderson sdcardclk_init.flags = CLK_GET_RATE_NOCACHE; 464c390f211SDouglas Anderson sdcardclk_init.ops = &arasan_sdcardclk_ops; 465c390f211SDouglas Anderson 466c390f211SDouglas Anderson sdhci_arasan->sdcardclk_hw.init = &sdcardclk_init; 467c390f211SDouglas Anderson sdhci_arasan->sdcardclk = 468c390f211SDouglas Anderson devm_clk_register(dev, &sdhci_arasan->sdcardclk_hw); 469c390f211SDouglas Anderson sdhci_arasan->sdcardclk_hw.init = NULL; 470c390f211SDouglas Anderson 471c390f211SDouglas Anderson ret = of_clk_add_provider(np, of_clk_src_simple_get, 472c390f211SDouglas Anderson sdhci_arasan->sdcardclk); 473c390f211SDouglas Anderson if (ret) 474c390f211SDouglas Anderson dev_err(dev, "Failed to add clock provider\n"); 475c390f211SDouglas Anderson 476c390f211SDouglas Anderson return ret; 477c390f211SDouglas Anderson } 478c390f211SDouglas Anderson 479c390f211SDouglas Anderson /** 480c390f211SDouglas Anderson * sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk() 481c390f211SDouglas Anderson * 482c390f211SDouglas Anderson * Should be called any time we're exiting and sdhci_arasan_register_sdclk() 483c390f211SDouglas Anderson * returned success. 484c390f211SDouglas Anderson * 485c390f211SDouglas Anderson * @dev: Pointer to our struct device. 486c390f211SDouglas Anderson */ 487c390f211SDouglas Anderson static void sdhci_arasan_unregister_sdclk(struct device *dev) 488c390f211SDouglas Anderson { 489c390f211SDouglas Anderson struct device_node *np = dev->of_node; 490c390f211SDouglas Anderson 491c390f211SDouglas Anderson if (!of_find_property(np, "#clock-cells", NULL)) 492c390f211SDouglas Anderson return; 493c390f211SDouglas Anderson 494c390f211SDouglas Anderson of_clk_del_provider(dev->of_node); 495c390f211SDouglas Anderson } 496c390f211SDouglas Anderson 497e3ec3a3dSSoren Brinkmann static int sdhci_arasan_probe(struct platform_device *pdev) 498e3ec3a3dSSoren Brinkmann { 499e3ec3a3dSSoren Brinkmann int ret; 5003ea4666eSDouglas Anderson const struct of_device_id *match; 5013ea4666eSDouglas Anderson struct device_node *node; 502e3ec3a3dSSoren Brinkmann struct clk *clk_xin; 503e3ec3a3dSSoren Brinkmann struct sdhci_host *host; 504e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host; 505e3ec3a3dSSoren Brinkmann struct sdhci_arasan_data *sdhci_arasan; 506e3ec3a3dSSoren Brinkmann 50789211418SJisheng Zhang host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 50889211418SJisheng Zhang sizeof(*sdhci_arasan)); 50989211418SJisheng Zhang if (IS_ERR(host)) 51089211418SJisheng Zhang return PTR_ERR(host); 51189211418SJisheng Zhang 51289211418SJisheng Zhang pltfm_host = sdhci_priv(host); 51389211418SJisheng Zhang sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 514c390f211SDouglas Anderson sdhci_arasan->host = host; 515e3ec3a3dSSoren Brinkmann 5163ea4666eSDouglas Anderson match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node); 5173ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_map = match->data; 5183ea4666eSDouglas Anderson 5193ea4666eSDouglas Anderson node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0); 5203ea4666eSDouglas Anderson if (node) { 5213ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node); 5223ea4666eSDouglas Anderson of_node_put(node); 5233ea4666eSDouglas Anderson 5243ea4666eSDouglas Anderson if (IS_ERR(sdhci_arasan->soc_ctl_base)) { 5253ea4666eSDouglas Anderson ret = PTR_ERR(sdhci_arasan->soc_ctl_base); 5263ea4666eSDouglas Anderson if (ret != -EPROBE_DEFER) 5273ea4666eSDouglas Anderson dev_err(&pdev->dev, "Can't get syscon: %d\n", 5283ea4666eSDouglas Anderson ret); 5293ea4666eSDouglas Anderson goto err_pltfm_free; 5303ea4666eSDouglas Anderson } 5313ea4666eSDouglas Anderson } 5323ea4666eSDouglas Anderson 533e3ec3a3dSSoren Brinkmann sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); 534e3ec3a3dSSoren Brinkmann if (IS_ERR(sdhci_arasan->clk_ahb)) { 535e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "clk_ahb clock not found.\n"); 536278d0962SShawn Lin ret = PTR_ERR(sdhci_arasan->clk_ahb); 537278d0962SShawn Lin goto err_pltfm_free; 538e3ec3a3dSSoren Brinkmann } 539e3ec3a3dSSoren Brinkmann 540e3ec3a3dSSoren Brinkmann clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); 541e3ec3a3dSSoren Brinkmann if (IS_ERR(clk_xin)) { 542e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "clk_xin clock not found.\n"); 543278d0962SShawn Lin ret = PTR_ERR(clk_xin); 544278d0962SShawn Lin goto err_pltfm_free; 545e3ec3a3dSSoren Brinkmann } 546e3ec3a3dSSoren Brinkmann 547e3ec3a3dSSoren Brinkmann ret = clk_prepare_enable(sdhci_arasan->clk_ahb); 548e3ec3a3dSSoren Brinkmann if (ret) { 549e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); 550278d0962SShawn Lin goto err_pltfm_free; 551e3ec3a3dSSoren Brinkmann } 552e3ec3a3dSSoren Brinkmann 553e3ec3a3dSSoren Brinkmann ret = clk_prepare_enable(clk_xin); 554e3ec3a3dSSoren Brinkmann if (ret) { 555e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "Unable to enable SD clock.\n"); 556e3ec3a3dSSoren Brinkmann goto clk_dis_ahb; 557e3ec3a3dSSoren Brinkmann } 558e3ec3a3dSSoren Brinkmann 559e3ec3a3dSSoren Brinkmann sdhci_get_of_property(pdev); 560e3ec3a3dSSoren Brinkmann pltfm_host->clk = clk_xin; 561e3ec3a3dSSoren Brinkmann 5623ea4666eSDouglas Anderson sdhci_arasan_update_baseclkfreq(host); 5633ea4666eSDouglas Anderson 564c390f211SDouglas Anderson ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev); 565c390f211SDouglas Anderson if (ret) 566c390f211SDouglas Anderson goto clk_disable_all; 567c390f211SDouglas Anderson 56816b23787SMichal Simek ret = mmc_of_parse(host->mmc); 56916b23787SMichal Simek if (ret) { 57016b23787SMichal Simek dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret); 571c390f211SDouglas Anderson goto unreg_clk; 57216b23787SMichal Simek } 57316b23787SMichal Simek 57491aa3661SShawn Lin sdhci_arasan->phy = ERR_PTR(-ENODEV); 57591aa3661SShawn Lin if (of_device_is_compatible(pdev->dev.of_node, 57691aa3661SShawn Lin "arasan,sdhci-5.1")) { 57791aa3661SShawn Lin sdhci_arasan->phy = devm_phy_get(&pdev->dev, 57891aa3661SShawn Lin "phy_arasan"); 57991aa3661SShawn Lin if (IS_ERR(sdhci_arasan->phy)) { 58091aa3661SShawn Lin ret = PTR_ERR(sdhci_arasan->phy); 58191aa3661SShawn Lin dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n"); 582c390f211SDouglas Anderson goto unreg_clk; 58391aa3661SShawn Lin } 58491aa3661SShawn Lin 58591aa3661SShawn Lin ret = phy_init(sdhci_arasan->phy); 58691aa3661SShawn Lin if (ret < 0) { 58791aa3661SShawn Lin dev_err(&pdev->dev, "phy_init err.\n"); 588c390f211SDouglas Anderson goto unreg_clk; 58991aa3661SShawn Lin } 59091aa3661SShawn Lin 591a05c8465SShawn Lin host->mmc_host_ops.hs400_enhanced_strobe = 592a05c8465SShawn Lin sdhci_arasan_hs400_enhanced_strobe; 59391aa3661SShawn Lin } 59491aa3661SShawn Lin 595e3ec3a3dSSoren Brinkmann ret = sdhci_add_host(host); 596b1df9de7SMike Looijmans if (ret) 59791aa3661SShawn Lin goto err_add_host; 598e3ec3a3dSSoren Brinkmann 599e3ec3a3dSSoren Brinkmann return 0; 600e3ec3a3dSSoren Brinkmann 60191aa3661SShawn Lin err_add_host: 60291aa3661SShawn Lin if (!IS_ERR(sdhci_arasan->phy)) 60391aa3661SShawn Lin phy_exit(sdhci_arasan->phy); 604c390f211SDouglas Anderson unreg_clk: 605c390f211SDouglas Anderson sdhci_arasan_unregister_sdclk(&pdev->dev); 606e3ec3a3dSSoren Brinkmann clk_disable_all: 607e3ec3a3dSSoren Brinkmann clk_disable_unprepare(clk_xin); 608e3ec3a3dSSoren Brinkmann clk_dis_ahb: 609e3ec3a3dSSoren Brinkmann clk_disable_unprepare(sdhci_arasan->clk_ahb); 610278d0962SShawn Lin err_pltfm_free: 611278d0962SShawn Lin sdhci_pltfm_free(pdev); 612e3ec3a3dSSoren Brinkmann return ret; 613e3ec3a3dSSoren Brinkmann } 614e3ec3a3dSSoren Brinkmann 615e3ec3a3dSSoren Brinkmann static int sdhci_arasan_remove(struct platform_device *pdev) 616e3ec3a3dSSoren Brinkmann { 6170c7fe32eSJisheng Zhang int ret; 618e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 619e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 62089211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 62189211418SJisheng Zhang struct clk *clk_ahb = sdhci_arasan->clk_ahb; 622e3ec3a3dSSoren Brinkmann 62391aa3661SShawn Lin if (!IS_ERR(sdhci_arasan->phy)) { 624b2db9c67SDouglas Anderson if (sdhci_arasan->is_phy_on) 62591aa3661SShawn Lin phy_power_off(sdhci_arasan->phy); 62691aa3661SShawn Lin phy_exit(sdhci_arasan->phy); 62791aa3661SShawn Lin } 62891aa3661SShawn Lin 629c390f211SDouglas Anderson sdhci_arasan_unregister_sdclk(&pdev->dev); 630c390f211SDouglas Anderson 6310c7fe32eSJisheng Zhang ret = sdhci_pltfm_unregister(pdev); 6320c7fe32eSJisheng Zhang 63389211418SJisheng Zhang clk_disable_unprepare(clk_ahb); 634e3ec3a3dSSoren Brinkmann 6350c7fe32eSJisheng Zhang return ret; 636e3ec3a3dSSoren Brinkmann } 637e3ec3a3dSSoren Brinkmann 638e3ec3a3dSSoren Brinkmann static struct platform_driver sdhci_arasan_driver = { 639e3ec3a3dSSoren Brinkmann .driver = { 640e3ec3a3dSSoren Brinkmann .name = "sdhci-arasan", 641e3ec3a3dSSoren Brinkmann .of_match_table = sdhci_arasan_of_match, 642e3ec3a3dSSoren Brinkmann .pm = &sdhci_arasan_dev_pm_ops, 643e3ec3a3dSSoren Brinkmann }, 644e3ec3a3dSSoren Brinkmann .probe = sdhci_arasan_probe, 645e3ec3a3dSSoren Brinkmann .remove = sdhci_arasan_remove, 646e3ec3a3dSSoren Brinkmann }; 647e3ec3a3dSSoren Brinkmann 648e3ec3a3dSSoren Brinkmann module_platform_driver(sdhci_arasan_driver); 649e3ec3a3dSSoren Brinkmann 650e3ec3a3dSSoren Brinkmann MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); 651e3ec3a3dSSoren Brinkmann MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>"); 652e3ec3a3dSSoren Brinkmann MODULE_LICENSE("GPL"); 653