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" 293794c542SZach Brown #include <linux/of.h> 30e3ec3a3dSSoren Brinkmann 31e3ec3a3dSSoren Brinkmann #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c 32a05c8465SShawn Lin #define SDHCI_ARASAN_VENDOR_REGISTER 0x78 33e3ec3a3dSSoren Brinkmann 34a05c8465SShawn Lin #define VENDOR_ENHANCED_STROBE BIT(0) 35e3ec3a3dSSoren Brinkmann #define CLK_CTRL_TIMEOUT_SHIFT 16 36e3ec3a3dSSoren Brinkmann #define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) 37e3ec3a3dSSoren Brinkmann #define CLK_CTRL_TIMEOUT_MIN_EXP 13 38e3ec3a3dSSoren Brinkmann 39b2db9c67SDouglas Anderson #define PHY_CLK_TOO_SLOW_HZ 400000 40b2db9c67SDouglas Anderson 413ea4666eSDouglas Anderson /* 423ea4666eSDouglas Anderson * On some SoCs the syscon area has a feature where the upper 16-bits of 433ea4666eSDouglas Anderson * each 32-bit register act as a write mask for the lower 16-bits. This allows 443ea4666eSDouglas Anderson * atomic updates of the register without locking. This macro is used on SoCs 453ea4666eSDouglas Anderson * that have that feature. 463ea4666eSDouglas Anderson */ 473ea4666eSDouglas Anderson #define HIWORD_UPDATE(val, mask, shift) \ 483ea4666eSDouglas Anderson ((val) << (shift) | (mask) << ((shift) + 16)) 493ea4666eSDouglas Anderson 503ea4666eSDouglas Anderson /** 513ea4666eSDouglas Anderson * struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map 523ea4666eSDouglas Anderson * 533ea4666eSDouglas Anderson * @reg: Offset within the syscon of the register containing this field 543ea4666eSDouglas Anderson * @width: Number of bits for this field 553ea4666eSDouglas Anderson * @shift: Bit offset within @reg of this field (or -1 if not avail) 563ea4666eSDouglas Anderson */ 573ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_field { 583ea4666eSDouglas Anderson u32 reg; 593ea4666eSDouglas Anderson u16 width; 603ea4666eSDouglas Anderson s16 shift; 613ea4666eSDouglas Anderson }; 623ea4666eSDouglas Anderson 633ea4666eSDouglas Anderson /** 643ea4666eSDouglas Anderson * struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers 653ea4666eSDouglas Anderson * 663ea4666eSDouglas Anderson * It's up to the licensee of the Arsan IP block to make these available 673ea4666eSDouglas Anderson * somewhere if needed. Presumably these will be scattered somewhere that's 683ea4666eSDouglas Anderson * accessible via the syscon API. 693ea4666eSDouglas Anderson * 703ea4666eSDouglas Anderson * @baseclkfreq: Where to find corecfg_baseclkfreq 71b2ca77c9SShawn Lin * @clockmultiplier: Where to find corecfg_clockmultiplier 723ea4666eSDouglas Anderson * @hiword_update: If true, use HIWORD_UPDATE to access the syscon 733ea4666eSDouglas Anderson */ 743ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_map { 753ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_field baseclkfreq; 76b2ca77c9SShawn Lin struct sdhci_arasan_soc_ctl_field clockmultiplier; 773ea4666eSDouglas Anderson bool hiword_update; 783ea4666eSDouglas Anderson }; 793ea4666eSDouglas Anderson 80e3ec3a3dSSoren Brinkmann /** 81e3ec3a3dSSoren Brinkmann * struct sdhci_arasan_data 82c390f211SDouglas Anderson * @host: Pointer to the main SDHCI host structure. 83e3ec3a3dSSoren Brinkmann * @clk_ahb: Pointer to the AHB clock 8491aa3661SShawn Lin * @phy: Pointer to the generic phy 85b2db9c67SDouglas Anderson * @is_phy_on: True if the PHY is on; false if not. 86c390f211SDouglas Anderson * @sdcardclk_hw: Struct for the clock we might provide to a PHY. 87c390f211SDouglas Anderson * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. 883ea4666eSDouglas Anderson * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. 893ea4666eSDouglas Anderson * @soc_ctl_map: Map to get offsets into soc_ctl registers. 90e3ec3a3dSSoren Brinkmann */ 91e3ec3a3dSSoren Brinkmann struct sdhci_arasan_data { 92c390f211SDouglas Anderson struct sdhci_host *host; 93e3ec3a3dSSoren Brinkmann struct clk *clk_ahb; 9491aa3661SShawn Lin struct phy *phy; 95b2db9c67SDouglas Anderson bool is_phy_on; 963ea4666eSDouglas Anderson 97c390f211SDouglas Anderson struct clk_hw sdcardclk_hw; 98c390f211SDouglas Anderson struct clk *sdcardclk; 99c390f211SDouglas Anderson 1003ea4666eSDouglas Anderson struct regmap *soc_ctl_base; 1013ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; 1023794c542SZach Brown unsigned int quirks; /* Arasan deviations from spec */ 1033794c542SZach Brown 1043794c542SZach Brown /* Controller does not have CD wired and will not function normally without */ 1053794c542SZach Brown #define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0) 106e3ec3a3dSSoren Brinkmann }; 107e3ec3a3dSSoren Brinkmann 1083ea4666eSDouglas Anderson static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { 1093ea4666eSDouglas Anderson .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 }, 110b2ca77c9SShawn Lin .clockmultiplier = { .reg = 0xf02c, .width = 8, .shift = 0}, 1113ea4666eSDouglas Anderson .hiword_update = true, 1123ea4666eSDouglas Anderson }; 1133ea4666eSDouglas Anderson 1143ea4666eSDouglas Anderson /** 1153ea4666eSDouglas Anderson * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers 1163ea4666eSDouglas Anderson * 1173ea4666eSDouglas Anderson * This function allows writing to fields in sdhci_arasan_soc_ctl_map. 1183ea4666eSDouglas Anderson * Note that if a field is specified as not available (shift < 0) then 1193ea4666eSDouglas Anderson * this function will silently return an error code. It will be noisy 1203ea4666eSDouglas Anderson * and print errors for any other (unexpected) errors. 1213ea4666eSDouglas Anderson * 1223ea4666eSDouglas Anderson * @host: The sdhci_host 1233ea4666eSDouglas Anderson * @fld: The field to write to 1243ea4666eSDouglas Anderson * @val: The value to write 1253ea4666eSDouglas Anderson */ 1263ea4666eSDouglas Anderson static int sdhci_arasan_syscon_write(struct sdhci_host *host, 1273ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_field *fld, 1283ea4666eSDouglas Anderson u32 val) 1293ea4666eSDouglas Anderson { 1303ea4666eSDouglas Anderson struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1313ea4666eSDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 1323ea4666eSDouglas Anderson struct regmap *soc_ctl_base = sdhci_arasan->soc_ctl_base; 1333ea4666eSDouglas Anderson u32 reg = fld->reg; 1343ea4666eSDouglas Anderson u16 width = fld->width; 1353ea4666eSDouglas Anderson s16 shift = fld->shift; 1363ea4666eSDouglas Anderson int ret; 1373ea4666eSDouglas Anderson 1383ea4666eSDouglas Anderson /* 1393ea4666eSDouglas Anderson * Silently return errors for shift < 0 so caller doesn't have 1403ea4666eSDouglas Anderson * to check for fields which are optional. For fields that 1413ea4666eSDouglas Anderson * are required then caller needs to do something special 1423ea4666eSDouglas Anderson * anyway. 1433ea4666eSDouglas Anderson */ 1443ea4666eSDouglas Anderson if (shift < 0) 1453ea4666eSDouglas Anderson return -EINVAL; 1463ea4666eSDouglas Anderson 1473ea4666eSDouglas Anderson if (sdhci_arasan->soc_ctl_map->hiword_update) 1483ea4666eSDouglas Anderson ret = regmap_write(soc_ctl_base, reg, 1493ea4666eSDouglas Anderson HIWORD_UPDATE(val, GENMASK(width, 0), 1503ea4666eSDouglas Anderson shift)); 1513ea4666eSDouglas Anderson else 1523ea4666eSDouglas Anderson ret = regmap_update_bits(soc_ctl_base, reg, 1533ea4666eSDouglas Anderson GENMASK(shift + width, shift), 1543ea4666eSDouglas Anderson val << shift); 1553ea4666eSDouglas Anderson 1563ea4666eSDouglas Anderson /* Yell about (unexpected) regmap errors */ 1573ea4666eSDouglas Anderson if (ret) 1583ea4666eSDouglas Anderson pr_warn("%s: Regmap write fail: %d\n", 1593ea4666eSDouglas Anderson mmc_hostname(host->mmc), ret); 1603ea4666eSDouglas Anderson 1613ea4666eSDouglas Anderson return ret; 1623ea4666eSDouglas Anderson } 1633ea4666eSDouglas Anderson 164e3ec3a3dSSoren Brinkmann static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) 165e3ec3a3dSSoren Brinkmann { 166e3ec3a3dSSoren Brinkmann u32 div; 167e3ec3a3dSSoren Brinkmann unsigned long freq; 168e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 169e3ec3a3dSSoren Brinkmann 170e3ec3a3dSSoren Brinkmann div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET); 171e3ec3a3dSSoren Brinkmann div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT; 172e3ec3a3dSSoren Brinkmann 173e3ec3a3dSSoren Brinkmann freq = clk_get_rate(pltfm_host->clk); 174e3ec3a3dSSoren Brinkmann freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div); 175e3ec3a3dSSoren Brinkmann 176e3ec3a3dSSoren Brinkmann return freq; 177e3ec3a3dSSoren Brinkmann } 178e3ec3a3dSSoren Brinkmann 179802ac39aSShawn Lin static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) 180802ac39aSShawn Lin { 181802ac39aSShawn Lin struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 182802ac39aSShawn Lin struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 1836fc09244SDouglas Anderson bool ctrl_phy = false; 184802ac39aSShawn Lin 185b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy)) { 186b2db9c67SDouglas Anderson if (!sdhci_arasan->is_phy_on && clock <= PHY_CLK_TOO_SLOW_HZ) { 187b2db9c67SDouglas Anderson /* 188b2db9c67SDouglas Anderson * If PHY off, set clock to max speed and power PHY on. 189b2db9c67SDouglas Anderson * 190b2db9c67SDouglas Anderson * Although PHY docs apparently suggest power cycling 191b2db9c67SDouglas Anderson * when changing the clock the PHY doesn't like to be 192b2db9c67SDouglas Anderson * powered on while at low speeds like those used in ID 193b2db9c67SDouglas Anderson * mode. Even worse is powering the PHY on while the 194b2db9c67SDouglas Anderson * clock is off. 195b2db9c67SDouglas Anderson * 196b2db9c67SDouglas Anderson * To workaround the PHY limitations, the best we can 197b2db9c67SDouglas Anderson * do is to power it on at a faster speed and then slam 198b2db9c67SDouglas Anderson * through low speeds without power cycling. 199b2db9c67SDouglas Anderson */ 200b2db9c67SDouglas Anderson sdhci_set_clock(host, host->max_clk); 201b2db9c67SDouglas Anderson spin_unlock_irq(&host->lock); 202b2db9c67SDouglas Anderson phy_power_on(sdhci_arasan->phy); 203b2db9c67SDouglas Anderson spin_lock_irq(&host->lock); 204b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 205802ac39aSShawn Lin 206b2db9c67SDouglas Anderson /* 207b2db9c67SDouglas Anderson * We'll now fall through to the below case with 208b2db9c67SDouglas Anderson * ctrl_phy = false (so we won't turn off/on). The 209b2db9c67SDouglas Anderson * sdhci_set_clock() will set the real clock. 210b2db9c67SDouglas Anderson */ 211b2db9c67SDouglas Anderson } else if (clock > PHY_CLK_TOO_SLOW_HZ) { 212b2db9c67SDouglas Anderson /* 213b2db9c67SDouglas Anderson * At higher clock speeds the PHY is fine being power 214b2db9c67SDouglas Anderson * cycled and docs say you _should_ power cycle when 215b2db9c67SDouglas Anderson * changing clock speeds. 216b2db9c67SDouglas Anderson */ 217b2db9c67SDouglas Anderson ctrl_phy = true; 218b2db9c67SDouglas Anderson } 219b2db9c67SDouglas Anderson } 220b2db9c67SDouglas Anderson 221b2db9c67SDouglas Anderson if (ctrl_phy && sdhci_arasan->is_phy_on) { 222802ac39aSShawn Lin spin_unlock_irq(&host->lock); 223802ac39aSShawn Lin phy_power_off(sdhci_arasan->phy); 224802ac39aSShawn Lin spin_lock_irq(&host->lock); 225b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = false; 226802ac39aSShawn Lin } 227802ac39aSShawn Lin 228802ac39aSShawn Lin sdhci_set_clock(host, clock); 229802ac39aSShawn Lin 2306fc09244SDouglas Anderson if (ctrl_phy) { 231802ac39aSShawn Lin spin_unlock_irq(&host->lock); 232802ac39aSShawn Lin phy_power_on(sdhci_arasan->phy); 233802ac39aSShawn Lin spin_lock_irq(&host->lock); 234b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 235802ac39aSShawn Lin } 236802ac39aSShawn Lin } 237802ac39aSShawn Lin 238a05c8465SShawn Lin static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc, 239a05c8465SShawn Lin struct mmc_ios *ios) 240a05c8465SShawn Lin { 241a05c8465SShawn Lin u32 vendor; 242a05c8465SShawn Lin struct sdhci_host *host = mmc_priv(mmc); 243a05c8465SShawn Lin 244a05c8465SShawn Lin vendor = readl(host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); 245a05c8465SShawn Lin if (ios->enhanced_strobe) 246a05c8465SShawn Lin vendor |= VENDOR_ENHANCED_STROBE; 247a05c8465SShawn Lin else 248a05c8465SShawn Lin vendor &= ~VENDOR_ENHANCED_STROBE; 249a05c8465SShawn Lin 250a05c8465SShawn Lin writel(vendor, host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); 251a05c8465SShawn Lin } 252a05c8465SShawn Lin 25313d62fd2SWei Yongjun static void sdhci_arasan_reset(struct sdhci_host *host, u8 mask) 2543794c542SZach Brown { 2553794c542SZach Brown u8 ctrl; 2563794c542SZach Brown struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2573794c542SZach Brown struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 2583794c542SZach Brown 2593794c542SZach Brown sdhci_reset(host, mask); 2603794c542SZach Brown 2613794c542SZach Brown if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) { 2623794c542SZach Brown ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 2633794c542SZach Brown ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN; 2643794c542SZach Brown sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 2653794c542SZach Brown } 2663794c542SZach Brown } 2673794c542SZach Brown 2688a3bee9bSShawn Lin static int sdhci_arasan_voltage_switch(struct mmc_host *mmc, 2698a3bee9bSShawn Lin struct mmc_ios *ios) 2708a3bee9bSShawn Lin { 2718a3bee9bSShawn Lin switch (ios->signal_voltage) { 2728a3bee9bSShawn Lin case MMC_SIGNAL_VOLTAGE_180: 2738a3bee9bSShawn Lin /* 2748a3bee9bSShawn Lin * Plese don't switch to 1V8 as arasan,5.1 doesn't 2758a3bee9bSShawn Lin * actually refer to this setting to indicate the 2768a3bee9bSShawn Lin * signal voltage and the state machine will be broken 2778a3bee9bSShawn Lin * actually if we force to enable 1V8. That's something 2788a3bee9bSShawn Lin * like broken quirk but we could work around here. 2798a3bee9bSShawn Lin */ 2808a3bee9bSShawn Lin return 0; 2818a3bee9bSShawn Lin case MMC_SIGNAL_VOLTAGE_330: 2828a3bee9bSShawn Lin case MMC_SIGNAL_VOLTAGE_120: 2838a3bee9bSShawn Lin /* We don't support 3V3 and 1V2 */ 2848a3bee9bSShawn Lin break; 2858a3bee9bSShawn Lin } 2868a3bee9bSShawn Lin 2878a3bee9bSShawn Lin return -EINVAL; 2888a3bee9bSShawn Lin } 2898a3bee9bSShawn Lin 290e3ec3a3dSSoren Brinkmann static struct sdhci_ops sdhci_arasan_ops = { 291802ac39aSShawn Lin .set_clock = sdhci_arasan_set_clock, 292e3ec3a3dSSoren Brinkmann .get_max_clock = sdhci_pltfm_clk_get_max_clock, 293e3ec3a3dSSoren Brinkmann .get_timeout_clock = sdhci_arasan_get_timeout_clock, 2942317f56cSRussell King .set_bus_width = sdhci_set_bus_width, 2953794c542SZach Brown .reset = sdhci_arasan_reset, 29696d7b78cSRussell King .set_uhs_signaling = sdhci_set_uhs_signaling, 297e3ec3a3dSSoren Brinkmann }; 298e3ec3a3dSSoren Brinkmann 299e3ec3a3dSSoren Brinkmann static struct sdhci_pltfm_data sdhci_arasan_pdata = { 300e3ec3a3dSSoren Brinkmann .ops = &sdhci_arasan_ops, 3012d532d45SSuneel Garapati .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 3022d532d45SSuneel Garapati .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 3032d532d45SSuneel Garapati SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, 304e3ec3a3dSSoren Brinkmann }; 305e3ec3a3dSSoren Brinkmann 306e3ec3a3dSSoren Brinkmann #ifdef CONFIG_PM_SLEEP 307e3ec3a3dSSoren Brinkmann /** 308e3ec3a3dSSoren Brinkmann * sdhci_arasan_suspend - Suspend method for the driver 309e3ec3a3dSSoren Brinkmann * @dev: Address of the device structure 310e3ec3a3dSSoren Brinkmann * Returns 0 on success and error value on error 311e3ec3a3dSSoren Brinkmann * 312e3ec3a3dSSoren Brinkmann * Put the device in a low power state. 313e3ec3a3dSSoren Brinkmann */ 314e3ec3a3dSSoren Brinkmann static int sdhci_arasan_suspend(struct device *dev) 315e3ec3a3dSSoren Brinkmann { 316e3ec3a3dSSoren Brinkmann struct platform_device *pdev = to_platform_device(dev); 317e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 318e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 31989211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 320e3ec3a3dSSoren Brinkmann int ret; 321e3ec3a3dSSoren Brinkmann 322e3ec3a3dSSoren Brinkmann ret = sdhci_suspend_host(host); 323e3ec3a3dSSoren Brinkmann if (ret) 324e3ec3a3dSSoren Brinkmann return ret; 325e3ec3a3dSSoren Brinkmann 326b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy) && sdhci_arasan->is_phy_on) { 32791aa3661SShawn Lin ret = phy_power_off(sdhci_arasan->phy); 32891aa3661SShawn Lin if (ret) { 32991aa3661SShawn Lin dev_err(dev, "Cannot power off phy.\n"); 33091aa3661SShawn Lin sdhci_resume_host(host); 33191aa3661SShawn Lin return ret; 33291aa3661SShawn Lin } 333b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = false; 33491aa3661SShawn Lin } 33591aa3661SShawn Lin 336e3ec3a3dSSoren Brinkmann clk_disable(pltfm_host->clk); 337e3ec3a3dSSoren Brinkmann clk_disable(sdhci_arasan->clk_ahb); 338e3ec3a3dSSoren Brinkmann 339e3ec3a3dSSoren Brinkmann return 0; 340e3ec3a3dSSoren Brinkmann } 341e3ec3a3dSSoren Brinkmann 342e3ec3a3dSSoren Brinkmann /** 343e3ec3a3dSSoren Brinkmann * sdhci_arasan_resume - Resume method for the driver 344e3ec3a3dSSoren Brinkmann * @dev: Address of the device structure 345e3ec3a3dSSoren Brinkmann * Returns 0 on success and error value on error 346e3ec3a3dSSoren Brinkmann * 347e3ec3a3dSSoren Brinkmann * Resume operation after suspend 348e3ec3a3dSSoren Brinkmann */ 349e3ec3a3dSSoren Brinkmann static int sdhci_arasan_resume(struct device *dev) 350e3ec3a3dSSoren Brinkmann { 351e3ec3a3dSSoren Brinkmann struct platform_device *pdev = to_platform_device(dev); 352e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 353e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 35489211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 355e3ec3a3dSSoren Brinkmann int ret; 356e3ec3a3dSSoren Brinkmann 357e3ec3a3dSSoren Brinkmann ret = clk_enable(sdhci_arasan->clk_ahb); 358e3ec3a3dSSoren Brinkmann if (ret) { 359e3ec3a3dSSoren Brinkmann dev_err(dev, "Cannot enable AHB clock.\n"); 360e3ec3a3dSSoren Brinkmann return ret; 361e3ec3a3dSSoren Brinkmann } 362e3ec3a3dSSoren Brinkmann 363e3ec3a3dSSoren Brinkmann ret = clk_enable(pltfm_host->clk); 364e3ec3a3dSSoren Brinkmann if (ret) { 365e3ec3a3dSSoren Brinkmann dev_err(dev, "Cannot enable SD clock.\n"); 366e3ec3a3dSSoren Brinkmann return ret; 367e3ec3a3dSSoren Brinkmann } 368e3ec3a3dSSoren Brinkmann 369b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy) && host->mmc->actual_clock) { 37091aa3661SShawn Lin ret = phy_power_on(sdhci_arasan->phy); 37191aa3661SShawn Lin if (ret) { 37291aa3661SShawn Lin dev_err(dev, "Cannot power on phy.\n"); 37391aa3661SShawn Lin return ret; 37491aa3661SShawn Lin } 375b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 37691aa3661SShawn Lin } 37791aa3661SShawn Lin 378e3ec3a3dSSoren Brinkmann return sdhci_resume_host(host); 379e3ec3a3dSSoren Brinkmann } 380e3ec3a3dSSoren Brinkmann #endif /* ! CONFIG_PM_SLEEP */ 381e3ec3a3dSSoren Brinkmann 382e3ec3a3dSSoren Brinkmann static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, 383e3ec3a3dSSoren Brinkmann sdhci_arasan_resume); 384e3ec3a3dSSoren Brinkmann 3853ea4666eSDouglas Anderson static const struct of_device_id sdhci_arasan_of_match[] = { 3863ea4666eSDouglas Anderson /* SoC-specific compatible strings w/ soc_ctl_map */ 3873ea4666eSDouglas Anderson { 3883ea4666eSDouglas Anderson .compatible = "rockchip,rk3399-sdhci-5.1", 3893ea4666eSDouglas Anderson .data = &rk3399_soc_ctl_map, 3903ea4666eSDouglas Anderson }, 3913ea4666eSDouglas Anderson 3923ea4666eSDouglas Anderson /* Generic compatible below here */ 3933ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-8.9a" }, 3943ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-5.1" }, 3953ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-4.9a" }, 3963ea4666eSDouglas Anderson 3973ea4666eSDouglas Anderson { /* sentinel */ } 3983ea4666eSDouglas Anderson }; 3993ea4666eSDouglas Anderson MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); 4003ea4666eSDouglas Anderson 4013ea4666eSDouglas Anderson /** 402c390f211SDouglas Anderson * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate 403c390f211SDouglas Anderson * 404c390f211SDouglas Anderson * Return the current actual rate of the SD card clock. This can be used 405c390f211SDouglas Anderson * to communicate with out PHY. 406c390f211SDouglas Anderson * 407c390f211SDouglas Anderson * @hw: Pointer to the hardware clock structure. 408c390f211SDouglas Anderson * @parent_rate The parent rate (should be rate of clk_xin). 409c390f211SDouglas Anderson * Returns the card clock rate. 410c390f211SDouglas Anderson */ 411c390f211SDouglas Anderson static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw, 412c390f211SDouglas Anderson unsigned long parent_rate) 413c390f211SDouglas Anderson 414c390f211SDouglas Anderson { 415c390f211SDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = 416c390f211SDouglas Anderson container_of(hw, struct sdhci_arasan_data, sdcardclk_hw); 417c390f211SDouglas Anderson struct sdhci_host *host = sdhci_arasan->host; 418c390f211SDouglas Anderson 419c390f211SDouglas Anderson return host->mmc->actual_clock; 420c390f211SDouglas Anderson } 421c390f211SDouglas Anderson 422c390f211SDouglas Anderson static const struct clk_ops arasan_sdcardclk_ops = { 423c390f211SDouglas Anderson .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, 424c390f211SDouglas Anderson }; 425c390f211SDouglas Anderson 426c390f211SDouglas Anderson /** 427b2ca77c9SShawn Lin * sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier 428b2ca77c9SShawn Lin * 429b2ca77c9SShawn Lin * The corecfg_clockmultiplier is supposed to contain clock multiplier 430b2ca77c9SShawn Lin * value of programmable clock generator. 431b2ca77c9SShawn Lin * 432b2ca77c9SShawn Lin * NOTES: 433b2ca77c9SShawn Lin * - Many existing devices don't seem to do this and work fine. To keep 434b2ca77c9SShawn Lin * compatibility for old hardware where the device tree doesn't provide a 435b2ca77c9SShawn Lin * register map, this function is a noop if a soc_ctl_map hasn't been provided 436b2ca77c9SShawn Lin * for this platform. 437b2ca77c9SShawn Lin * - The value of corecfg_clockmultiplier should sync with that of corresponding 438b2ca77c9SShawn Lin * value reading from sdhci_capability_register. So this function is called 439b2ca77c9SShawn Lin * once at probe time and never called again. 440b2ca77c9SShawn Lin * 441b2ca77c9SShawn Lin * @host: The sdhci_host 442b2ca77c9SShawn Lin */ 443b2ca77c9SShawn Lin static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host, 444b2ca77c9SShawn Lin u32 value) 445b2ca77c9SShawn Lin { 446b2ca77c9SShawn Lin struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 447b2ca77c9SShawn Lin struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 448b2ca77c9SShawn Lin const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = 449b2ca77c9SShawn Lin sdhci_arasan->soc_ctl_map; 450b2ca77c9SShawn Lin 451b2ca77c9SShawn Lin /* Having a map is optional */ 452b2ca77c9SShawn Lin if (!soc_ctl_map) 453b2ca77c9SShawn Lin return; 454b2ca77c9SShawn Lin 455b2ca77c9SShawn Lin /* If we have a map, we expect to have a syscon */ 456b2ca77c9SShawn Lin if (!sdhci_arasan->soc_ctl_base) { 457b2ca77c9SShawn Lin pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", 458b2ca77c9SShawn Lin mmc_hostname(host->mmc)); 459b2ca77c9SShawn Lin return; 460b2ca77c9SShawn Lin } 461b2ca77c9SShawn Lin 462b2ca77c9SShawn Lin sdhci_arasan_syscon_write(host, &soc_ctl_map->clockmultiplier, value); 463b2ca77c9SShawn Lin } 464b2ca77c9SShawn Lin 465b2ca77c9SShawn Lin /** 4663ea4666eSDouglas Anderson * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq 4673ea4666eSDouglas Anderson * 4683ea4666eSDouglas Anderson * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This 4693ea4666eSDouglas Anderson * function can be used to make that happen. 4703ea4666eSDouglas Anderson * 4713ea4666eSDouglas Anderson * NOTES: 4723ea4666eSDouglas Anderson * - Many existing devices don't seem to do this and work fine. To keep 4733ea4666eSDouglas Anderson * compatibility for old hardware where the device tree doesn't provide a 4743ea4666eSDouglas Anderson * register map, this function is a noop if a soc_ctl_map hasn't been provided 4753ea4666eSDouglas Anderson * for this platform. 4763ea4666eSDouglas Anderson * - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider 4773ea4666eSDouglas Anderson * to achieve lower clock rates. That means that this function is called once 4783ea4666eSDouglas Anderson * at probe time and never called again. 4793ea4666eSDouglas Anderson * 4803ea4666eSDouglas Anderson * @host: The sdhci_host 4813ea4666eSDouglas Anderson */ 4823ea4666eSDouglas Anderson static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host) 4833ea4666eSDouglas Anderson { 4843ea4666eSDouglas Anderson struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4853ea4666eSDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 4863ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = 4873ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_map; 4883ea4666eSDouglas Anderson u32 mhz = DIV_ROUND_CLOSEST(clk_get_rate(pltfm_host->clk), 1000000); 4893ea4666eSDouglas Anderson 4903ea4666eSDouglas Anderson /* Having a map is optional */ 4913ea4666eSDouglas Anderson if (!soc_ctl_map) 4923ea4666eSDouglas Anderson return; 4933ea4666eSDouglas Anderson 4943ea4666eSDouglas Anderson /* If we have a map, we expect to have a syscon */ 4953ea4666eSDouglas Anderson if (!sdhci_arasan->soc_ctl_base) { 4963ea4666eSDouglas Anderson pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", 4973ea4666eSDouglas Anderson mmc_hostname(host->mmc)); 4983ea4666eSDouglas Anderson return; 4993ea4666eSDouglas Anderson } 5003ea4666eSDouglas Anderson 5013ea4666eSDouglas Anderson sdhci_arasan_syscon_write(host, &soc_ctl_map->baseclkfreq, mhz); 5023ea4666eSDouglas Anderson } 5033ea4666eSDouglas Anderson 504c390f211SDouglas Anderson /** 505c390f211SDouglas Anderson * sdhci_arasan_register_sdclk - Register the sdclk for a PHY to use 506c390f211SDouglas Anderson * 507c390f211SDouglas Anderson * Some PHY devices need to know what the actual card clock is. In order for 508c390f211SDouglas Anderson * them to find out, we'll provide a clock through the common clock framework 509c390f211SDouglas Anderson * for them to query. 510c390f211SDouglas Anderson * 511c390f211SDouglas Anderson * Note: without seriously re-architecting SDHCI's clock code and testing on 512c390f211SDouglas Anderson * all platforms, there's no way to create a totally beautiful clock here 513c390f211SDouglas Anderson * with all clock ops implemented. Instead, we'll just create a clock that can 514c390f211SDouglas Anderson * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock 515c390f211SDouglas Anderson * framework that we're doing things behind its back. This should be sufficient 516c390f211SDouglas Anderson * to create nice clean device tree bindings and later (if needed) we can try 517c390f211SDouglas Anderson * re-architecting SDHCI if we see some benefit to it. 518c390f211SDouglas Anderson * 519c390f211SDouglas Anderson * @sdhci_arasan: Our private data structure. 520c390f211SDouglas Anderson * @clk_xin: Pointer to the functional clock 521c390f211SDouglas Anderson * @dev: Pointer to our struct device. 522c390f211SDouglas Anderson * Returns 0 on success and error value on error 523c390f211SDouglas Anderson */ 524c390f211SDouglas Anderson static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan, 525c390f211SDouglas Anderson struct clk *clk_xin, 526c390f211SDouglas Anderson struct device *dev) 527c390f211SDouglas Anderson { 528c390f211SDouglas Anderson struct device_node *np = dev->of_node; 529c390f211SDouglas Anderson struct clk_init_data sdcardclk_init; 530c390f211SDouglas Anderson const char *parent_clk_name; 531c390f211SDouglas Anderson int ret; 532c390f211SDouglas Anderson 533c390f211SDouglas Anderson /* Providing a clock to the PHY is optional; no error if missing */ 534c390f211SDouglas Anderson if (!of_find_property(np, "#clock-cells", NULL)) 535c390f211SDouglas Anderson return 0; 536c390f211SDouglas Anderson 537c390f211SDouglas Anderson ret = of_property_read_string_index(np, "clock-output-names", 0, 538c390f211SDouglas Anderson &sdcardclk_init.name); 539c390f211SDouglas Anderson if (ret) { 540c390f211SDouglas Anderson dev_err(dev, "DT has #clock-cells but no clock-output-names\n"); 541c390f211SDouglas Anderson return ret; 542c390f211SDouglas Anderson } 543c390f211SDouglas Anderson 544c390f211SDouglas Anderson parent_clk_name = __clk_get_name(clk_xin); 545c390f211SDouglas Anderson sdcardclk_init.parent_names = &parent_clk_name; 546c390f211SDouglas Anderson sdcardclk_init.num_parents = 1; 547c390f211SDouglas Anderson sdcardclk_init.flags = CLK_GET_RATE_NOCACHE; 548c390f211SDouglas Anderson sdcardclk_init.ops = &arasan_sdcardclk_ops; 549c390f211SDouglas Anderson 550c390f211SDouglas Anderson sdhci_arasan->sdcardclk_hw.init = &sdcardclk_init; 551c390f211SDouglas Anderson sdhci_arasan->sdcardclk = 552c390f211SDouglas Anderson devm_clk_register(dev, &sdhci_arasan->sdcardclk_hw); 553c390f211SDouglas Anderson sdhci_arasan->sdcardclk_hw.init = NULL; 554c390f211SDouglas Anderson 555c390f211SDouglas Anderson ret = of_clk_add_provider(np, of_clk_src_simple_get, 556c390f211SDouglas Anderson sdhci_arasan->sdcardclk); 557c390f211SDouglas Anderson if (ret) 558c390f211SDouglas Anderson dev_err(dev, "Failed to add clock provider\n"); 559c390f211SDouglas Anderson 560c390f211SDouglas Anderson return ret; 561c390f211SDouglas Anderson } 562c390f211SDouglas Anderson 563c390f211SDouglas Anderson /** 564c390f211SDouglas Anderson * sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk() 565c390f211SDouglas Anderson * 566c390f211SDouglas Anderson * Should be called any time we're exiting and sdhci_arasan_register_sdclk() 567c390f211SDouglas Anderson * returned success. 568c390f211SDouglas Anderson * 569c390f211SDouglas Anderson * @dev: Pointer to our struct device. 570c390f211SDouglas Anderson */ 571c390f211SDouglas Anderson static void sdhci_arasan_unregister_sdclk(struct device *dev) 572c390f211SDouglas Anderson { 573c390f211SDouglas Anderson struct device_node *np = dev->of_node; 574c390f211SDouglas Anderson 575c390f211SDouglas Anderson if (!of_find_property(np, "#clock-cells", NULL)) 576c390f211SDouglas Anderson return; 577c390f211SDouglas Anderson 578c390f211SDouglas Anderson of_clk_del_provider(dev->of_node); 579c390f211SDouglas Anderson } 580c390f211SDouglas Anderson 581e3ec3a3dSSoren Brinkmann static int sdhci_arasan_probe(struct platform_device *pdev) 582e3ec3a3dSSoren Brinkmann { 583e3ec3a3dSSoren Brinkmann int ret; 5843ea4666eSDouglas Anderson const struct of_device_id *match; 5853ea4666eSDouglas Anderson struct device_node *node; 586e3ec3a3dSSoren Brinkmann struct clk *clk_xin; 587e3ec3a3dSSoren Brinkmann struct sdhci_host *host; 588e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host; 589e3ec3a3dSSoren Brinkmann struct sdhci_arasan_data *sdhci_arasan; 5903794c542SZach Brown struct device_node *np = pdev->dev.of_node; 591e3ec3a3dSSoren Brinkmann 59289211418SJisheng Zhang host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 59389211418SJisheng Zhang sizeof(*sdhci_arasan)); 59489211418SJisheng Zhang if (IS_ERR(host)) 59589211418SJisheng Zhang return PTR_ERR(host); 59689211418SJisheng Zhang 59789211418SJisheng Zhang pltfm_host = sdhci_priv(host); 59889211418SJisheng Zhang sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 599c390f211SDouglas Anderson sdhci_arasan->host = host; 600e3ec3a3dSSoren Brinkmann 6013ea4666eSDouglas Anderson match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node); 6023ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_map = match->data; 6033ea4666eSDouglas Anderson 6043ea4666eSDouglas Anderson node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0); 6053ea4666eSDouglas Anderson if (node) { 6063ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node); 6073ea4666eSDouglas Anderson of_node_put(node); 6083ea4666eSDouglas Anderson 6093ea4666eSDouglas Anderson if (IS_ERR(sdhci_arasan->soc_ctl_base)) { 6103ea4666eSDouglas Anderson ret = PTR_ERR(sdhci_arasan->soc_ctl_base); 6113ea4666eSDouglas Anderson if (ret != -EPROBE_DEFER) 6123ea4666eSDouglas Anderson dev_err(&pdev->dev, "Can't get syscon: %d\n", 6133ea4666eSDouglas Anderson ret); 6143ea4666eSDouglas Anderson goto err_pltfm_free; 6153ea4666eSDouglas Anderson } 6163ea4666eSDouglas Anderson } 6173ea4666eSDouglas Anderson 618e3ec3a3dSSoren Brinkmann sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); 619e3ec3a3dSSoren Brinkmann if (IS_ERR(sdhci_arasan->clk_ahb)) { 620e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "clk_ahb clock not found.\n"); 621278d0962SShawn Lin ret = PTR_ERR(sdhci_arasan->clk_ahb); 622278d0962SShawn Lin goto err_pltfm_free; 623e3ec3a3dSSoren Brinkmann } 624e3ec3a3dSSoren Brinkmann 625e3ec3a3dSSoren Brinkmann clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); 626e3ec3a3dSSoren Brinkmann if (IS_ERR(clk_xin)) { 627e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "clk_xin clock not found.\n"); 628278d0962SShawn Lin ret = PTR_ERR(clk_xin); 629278d0962SShawn Lin goto err_pltfm_free; 630e3ec3a3dSSoren Brinkmann } 631e3ec3a3dSSoren Brinkmann 632e3ec3a3dSSoren Brinkmann ret = clk_prepare_enable(sdhci_arasan->clk_ahb); 633e3ec3a3dSSoren Brinkmann if (ret) { 634e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); 635278d0962SShawn Lin goto err_pltfm_free; 636e3ec3a3dSSoren Brinkmann } 637e3ec3a3dSSoren Brinkmann 638e3ec3a3dSSoren Brinkmann ret = clk_prepare_enable(clk_xin); 639e3ec3a3dSSoren Brinkmann if (ret) { 640e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "Unable to enable SD clock.\n"); 641e3ec3a3dSSoren Brinkmann goto clk_dis_ahb; 642e3ec3a3dSSoren Brinkmann } 643e3ec3a3dSSoren Brinkmann 644e3ec3a3dSSoren Brinkmann sdhci_get_of_property(pdev); 6453794c542SZach Brown 6463794c542SZach Brown if (of_property_read_bool(np, "xlnx,fails-without-test-cd")) 6473794c542SZach Brown sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST; 6483794c542SZach Brown 649e3ec3a3dSSoren Brinkmann pltfm_host->clk = clk_xin; 650e3ec3a3dSSoren Brinkmann 651b2ca77c9SShawn Lin if (of_device_is_compatible(pdev->dev.of_node, 652b2ca77c9SShawn Lin "rockchip,rk3399-sdhci-5.1")) 653b2ca77c9SShawn Lin sdhci_arasan_update_clockmultiplier(host, 0x0); 654b2ca77c9SShawn Lin 6553ea4666eSDouglas Anderson sdhci_arasan_update_baseclkfreq(host); 6563ea4666eSDouglas Anderson 657c390f211SDouglas Anderson ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev); 658c390f211SDouglas Anderson if (ret) 659c390f211SDouglas Anderson goto clk_disable_all; 660c390f211SDouglas Anderson 66116b23787SMichal Simek ret = mmc_of_parse(host->mmc); 66216b23787SMichal Simek if (ret) { 66316b23787SMichal Simek dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret); 664c390f211SDouglas Anderson goto unreg_clk; 66516b23787SMichal Simek } 66616b23787SMichal Simek 66791aa3661SShawn Lin sdhci_arasan->phy = ERR_PTR(-ENODEV); 66891aa3661SShawn Lin if (of_device_is_compatible(pdev->dev.of_node, 66991aa3661SShawn Lin "arasan,sdhci-5.1")) { 67091aa3661SShawn Lin sdhci_arasan->phy = devm_phy_get(&pdev->dev, 67191aa3661SShawn Lin "phy_arasan"); 67291aa3661SShawn Lin if (IS_ERR(sdhci_arasan->phy)) { 67391aa3661SShawn Lin ret = PTR_ERR(sdhci_arasan->phy); 67491aa3661SShawn Lin dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n"); 675c390f211SDouglas Anderson goto unreg_clk; 67691aa3661SShawn Lin } 67791aa3661SShawn Lin 67891aa3661SShawn Lin ret = phy_init(sdhci_arasan->phy); 67991aa3661SShawn Lin if (ret < 0) { 68091aa3661SShawn Lin dev_err(&pdev->dev, "phy_init err.\n"); 681c390f211SDouglas Anderson goto unreg_clk; 68291aa3661SShawn Lin } 68391aa3661SShawn Lin 684a05c8465SShawn Lin host->mmc_host_ops.hs400_enhanced_strobe = 685a05c8465SShawn Lin sdhci_arasan_hs400_enhanced_strobe; 6868a3bee9bSShawn Lin host->mmc_host_ops.start_signal_voltage_switch = 6878a3bee9bSShawn Lin sdhci_arasan_voltage_switch; 68891aa3661SShawn Lin } 68991aa3661SShawn Lin 690e3ec3a3dSSoren Brinkmann ret = sdhci_add_host(host); 691b1df9de7SMike Looijmans if (ret) 69291aa3661SShawn Lin goto err_add_host; 693e3ec3a3dSSoren Brinkmann 694e3ec3a3dSSoren Brinkmann return 0; 695e3ec3a3dSSoren Brinkmann 69691aa3661SShawn Lin err_add_host: 69791aa3661SShawn Lin if (!IS_ERR(sdhci_arasan->phy)) 69891aa3661SShawn Lin phy_exit(sdhci_arasan->phy); 699c390f211SDouglas Anderson unreg_clk: 700c390f211SDouglas Anderson sdhci_arasan_unregister_sdclk(&pdev->dev); 701e3ec3a3dSSoren Brinkmann clk_disable_all: 702e3ec3a3dSSoren Brinkmann clk_disable_unprepare(clk_xin); 703e3ec3a3dSSoren Brinkmann clk_dis_ahb: 704e3ec3a3dSSoren Brinkmann clk_disable_unprepare(sdhci_arasan->clk_ahb); 705278d0962SShawn Lin err_pltfm_free: 706278d0962SShawn Lin sdhci_pltfm_free(pdev); 707e3ec3a3dSSoren Brinkmann return ret; 708e3ec3a3dSSoren Brinkmann } 709e3ec3a3dSSoren Brinkmann 710e3ec3a3dSSoren Brinkmann static int sdhci_arasan_remove(struct platform_device *pdev) 711e3ec3a3dSSoren Brinkmann { 7120c7fe32eSJisheng Zhang int ret; 713e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 714e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 71589211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 71689211418SJisheng Zhang struct clk *clk_ahb = sdhci_arasan->clk_ahb; 717e3ec3a3dSSoren Brinkmann 71891aa3661SShawn Lin if (!IS_ERR(sdhci_arasan->phy)) { 719b2db9c67SDouglas Anderson if (sdhci_arasan->is_phy_on) 72091aa3661SShawn Lin phy_power_off(sdhci_arasan->phy); 72191aa3661SShawn Lin phy_exit(sdhci_arasan->phy); 72291aa3661SShawn Lin } 72391aa3661SShawn Lin 724c390f211SDouglas Anderson sdhci_arasan_unregister_sdclk(&pdev->dev); 725c390f211SDouglas Anderson 7260c7fe32eSJisheng Zhang ret = sdhci_pltfm_unregister(pdev); 7270c7fe32eSJisheng Zhang 72889211418SJisheng Zhang clk_disable_unprepare(clk_ahb); 729e3ec3a3dSSoren Brinkmann 7300c7fe32eSJisheng Zhang return ret; 731e3ec3a3dSSoren Brinkmann } 732e3ec3a3dSSoren Brinkmann 733e3ec3a3dSSoren Brinkmann static struct platform_driver sdhci_arasan_driver = { 734e3ec3a3dSSoren Brinkmann .driver = { 735e3ec3a3dSSoren Brinkmann .name = "sdhci-arasan", 736e3ec3a3dSSoren Brinkmann .of_match_table = sdhci_arasan_of_match, 737e3ec3a3dSSoren Brinkmann .pm = &sdhci_arasan_dev_pm_ops, 738e3ec3a3dSSoren Brinkmann }, 739e3ec3a3dSSoren Brinkmann .probe = sdhci_arasan_probe, 740e3ec3a3dSSoren Brinkmann .remove = sdhci_arasan_remove, 741e3ec3a3dSSoren Brinkmann }; 742e3ec3a3dSSoren Brinkmann 743e3ec3a3dSSoren Brinkmann module_platform_driver(sdhci_arasan_driver); 744e3ec3a3dSSoren Brinkmann 745e3ec3a3dSSoren Brinkmann MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); 746e3ec3a3dSSoren Brinkmann MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>"); 747e3ec3a3dSSoren Brinkmann MODULE_LICENSE("GPL"); 748