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 31a05c8465SShawn Lin #define SDHCI_ARASAN_VENDOR_REGISTER 0x78 32e3ec3a3dSSoren Brinkmann 33a05c8465SShawn Lin #define VENDOR_ENHANCED_STROBE BIT(0) 34e3ec3a3dSSoren Brinkmann 35b2db9c67SDouglas Anderson #define PHY_CLK_TOO_SLOW_HZ 400000 36b2db9c67SDouglas Anderson 373ea4666eSDouglas Anderson /* 383ea4666eSDouglas Anderson * On some SoCs the syscon area has a feature where the upper 16-bits of 393ea4666eSDouglas Anderson * each 32-bit register act as a write mask for the lower 16-bits. This allows 403ea4666eSDouglas Anderson * atomic updates of the register without locking. This macro is used on SoCs 413ea4666eSDouglas Anderson * that have that feature. 423ea4666eSDouglas Anderson */ 433ea4666eSDouglas Anderson #define HIWORD_UPDATE(val, mask, shift) \ 443ea4666eSDouglas Anderson ((val) << (shift) | (mask) << ((shift) + 16)) 453ea4666eSDouglas Anderson 463ea4666eSDouglas Anderson /** 473ea4666eSDouglas Anderson * struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map 483ea4666eSDouglas Anderson * 493ea4666eSDouglas Anderson * @reg: Offset within the syscon of the register containing this field 503ea4666eSDouglas Anderson * @width: Number of bits for this field 513ea4666eSDouglas Anderson * @shift: Bit offset within @reg of this field (or -1 if not avail) 523ea4666eSDouglas Anderson */ 533ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_field { 543ea4666eSDouglas Anderson u32 reg; 553ea4666eSDouglas Anderson u16 width; 563ea4666eSDouglas Anderson s16 shift; 573ea4666eSDouglas Anderson }; 583ea4666eSDouglas Anderson 593ea4666eSDouglas Anderson /** 603ea4666eSDouglas Anderson * struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers 613ea4666eSDouglas Anderson * 623ea4666eSDouglas Anderson * It's up to the licensee of the Arsan IP block to make these available 633ea4666eSDouglas Anderson * somewhere if needed. Presumably these will be scattered somewhere that's 643ea4666eSDouglas Anderson * accessible via the syscon API. 653ea4666eSDouglas Anderson * 663ea4666eSDouglas Anderson * @baseclkfreq: Where to find corecfg_baseclkfreq 67b2ca77c9SShawn Lin * @clockmultiplier: Where to find corecfg_clockmultiplier 683ea4666eSDouglas Anderson * @hiword_update: If true, use HIWORD_UPDATE to access the syscon 693ea4666eSDouglas Anderson */ 703ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_map { 713ea4666eSDouglas Anderson struct sdhci_arasan_soc_ctl_field baseclkfreq; 72b2ca77c9SShawn Lin struct sdhci_arasan_soc_ctl_field clockmultiplier; 733ea4666eSDouglas Anderson bool hiword_update; 743ea4666eSDouglas Anderson }; 753ea4666eSDouglas Anderson 76e3ec3a3dSSoren Brinkmann /** 77e3ec3a3dSSoren Brinkmann * struct sdhci_arasan_data 78c390f211SDouglas Anderson * @host: Pointer to the main SDHCI host structure. 79e3ec3a3dSSoren Brinkmann * @clk_ahb: Pointer to the AHB clock 8091aa3661SShawn Lin * @phy: Pointer to the generic phy 81b2db9c67SDouglas Anderson * @is_phy_on: True if the PHY is on; false if not. 82c390f211SDouglas Anderson * @sdcardclk_hw: Struct for the clock we might provide to a PHY. 83c390f211SDouglas Anderson * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. 843ea4666eSDouglas Anderson * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. 853ea4666eSDouglas Anderson * @soc_ctl_map: Map to get offsets into soc_ctl registers. 86e3ec3a3dSSoren Brinkmann */ 87e3ec3a3dSSoren Brinkmann struct sdhci_arasan_data { 88c390f211SDouglas Anderson struct sdhci_host *host; 89e3ec3a3dSSoren Brinkmann struct clk *clk_ahb; 9091aa3661SShawn Lin struct phy *phy; 91b2db9c67SDouglas Anderson bool is_phy_on; 923ea4666eSDouglas Anderson 93c390f211SDouglas Anderson struct clk_hw sdcardclk_hw; 94c390f211SDouglas Anderson struct clk *sdcardclk; 95c390f211SDouglas Anderson 963ea4666eSDouglas Anderson struct regmap *soc_ctl_base; 973ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; 983794c542SZach Brown unsigned int quirks; /* Arasan deviations from spec */ 993794c542SZach Brown 1003794c542SZach Brown /* Controller does not have CD wired and will not function normally without */ 1013794c542SZach Brown #define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0) 102e3ec3a3dSSoren Brinkmann }; 103e3ec3a3dSSoren Brinkmann 1043ea4666eSDouglas Anderson static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { 1053ea4666eSDouglas Anderson .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 }, 106b2ca77c9SShawn Lin .clockmultiplier = { .reg = 0xf02c, .width = 8, .shift = 0}, 1073ea4666eSDouglas Anderson .hiword_update = true, 1083ea4666eSDouglas Anderson }; 1093ea4666eSDouglas Anderson 1103ea4666eSDouglas Anderson /** 1113ea4666eSDouglas Anderson * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers 1123ea4666eSDouglas Anderson * 1133ea4666eSDouglas Anderson * This function allows writing to fields in sdhci_arasan_soc_ctl_map. 1143ea4666eSDouglas Anderson * Note that if a field is specified as not available (shift < 0) then 1153ea4666eSDouglas Anderson * this function will silently return an error code. It will be noisy 1163ea4666eSDouglas Anderson * and print errors for any other (unexpected) errors. 1173ea4666eSDouglas Anderson * 1183ea4666eSDouglas Anderson * @host: The sdhci_host 1193ea4666eSDouglas Anderson * @fld: The field to write to 1203ea4666eSDouglas Anderson * @val: The value to write 1213ea4666eSDouglas Anderson */ 1223ea4666eSDouglas Anderson static int sdhci_arasan_syscon_write(struct sdhci_host *host, 1233ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_field *fld, 1243ea4666eSDouglas Anderson u32 val) 1253ea4666eSDouglas Anderson { 1263ea4666eSDouglas Anderson struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1273ea4666eSDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 1283ea4666eSDouglas Anderson struct regmap *soc_ctl_base = sdhci_arasan->soc_ctl_base; 1293ea4666eSDouglas Anderson u32 reg = fld->reg; 1303ea4666eSDouglas Anderson u16 width = fld->width; 1313ea4666eSDouglas Anderson s16 shift = fld->shift; 1323ea4666eSDouglas Anderson int ret; 1333ea4666eSDouglas Anderson 1343ea4666eSDouglas Anderson /* 1353ea4666eSDouglas Anderson * Silently return errors for shift < 0 so caller doesn't have 1363ea4666eSDouglas Anderson * to check for fields which are optional. For fields that 1373ea4666eSDouglas Anderson * are required then caller needs to do something special 1383ea4666eSDouglas Anderson * anyway. 1393ea4666eSDouglas Anderson */ 1403ea4666eSDouglas Anderson if (shift < 0) 1413ea4666eSDouglas Anderson return -EINVAL; 1423ea4666eSDouglas Anderson 1433ea4666eSDouglas Anderson if (sdhci_arasan->soc_ctl_map->hiword_update) 1443ea4666eSDouglas Anderson ret = regmap_write(soc_ctl_base, reg, 1453ea4666eSDouglas Anderson HIWORD_UPDATE(val, GENMASK(width, 0), 1463ea4666eSDouglas Anderson shift)); 1473ea4666eSDouglas Anderson else 1483ea4666eSDouglas Anderson ret = regmap_update_bits(soc_ctl_base, reg, 1493ea4666eSDouglas Anderson GENMASK(shift + width, shift), 1503ea4666eSDouglas Anderson val << shift); 1513ea4666eSDouglas Anderson 1523ea4666eSDouglas Anderson /* Yell about (unexpected) regmap errors */ 1533ea4666eSDouglas Anderson if (ret) 1543ea4666eSDouglas Anderson pr_warn("%s: Regmap write fail: %d\n", 1553ea4666eSDouglas Anderson mmc_hostname(host->mmc), ret); 1563ea4666eSDouglas Anderson 1573ea4666eSDouglas Anderson return ret; 1583ea4666eSDouglas Anderson } 1593ea4666eSDouglas Anderson 160e3ec3a3dSSoren Brinkmann static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) 161e3ec3a3dSSoren Brinkmann { 162e3ec3a3dSSoren Brinkmann unsigned long freq; 163e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 164e3ec3a3dSSoren Brinkmann 16516681037SAnssi Hannula /* SDHCI timeout clock is in kHz */ 16616681037SAnssi Hannula freq = DIV_ROUND_UP(clk_get_rate(pltfm_host->clk), 1000); 167e3ec3a3dSSoren Brinkmann 16816681037SAnssi Hannula /* or in MHz */ 16916681037SAnssi Hannula if (host->caps & SDHCI_TIMEOUT_CLK_UNIT) 17016681037SAnssi Hannula freq = DIV_ROUND_UP(freq, 1000); 171e3ec3a3dSSoren Brinkmann 172e3ec3a3dSSoren Brinkmann return freq; 173e3ec3a3dSSoren Brinkmann } 174e3ec3a3dSSoren Brinkmann 175802ac39aSShawn Lin static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) 176802ac39aSShawn Lin { 177802ac39aSShawn Lin struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 178802ac39aSShawn Lin struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 1796fc09244SDouglas Anderson bool ctrl_phy = false; 180802ac39aSShawn Lin 181b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy)) { 182b2db9c67SDouglas Anderson if (!sdhci_arasan->is_phy_on && clock <= PHY_CLK_TOO_SLOW_HZ) { 183b2db9c67SDouglas Anderson /* 184b2db9c67SDouglas Anderson * If PHY off, set clock to max speed and power PHY on. 185b2db9c67SDouglas Anderson * 186b2db9c67SDouglas Anderson * Although PHY docs apparently suggest power cycling 187b2db9c67SDouglas Anderson * when changing the clock the PHY doesn't like to be 188b2db9c67SDouglas Anderson * powered on while at low speeds like those used in ID 189b2db9c67SDouglas Anderson * mode. Even worse is powering the PHY on while the 190b2db9c67SDouglas Anderson * clock is off. 191b2db9c67SDouglas Anderson * 192b2db9c67SDouglas Anderson * To workaround the PHY limitations, the best we can 193b2db9c67SDouglas Anderson * do is to power it on at a faster speed and then slam 194b2db9c67SDouglas Anderson * through low speeds without power cycling. 195b2db9c67SDouglas Anderson */ 196b2db9c67SDouglas Anderson sdhci_set_clock(host, host->max_clk); 197b2db9c67SDouglas Anderson spin_unlock_irq(&host->lock); 198b2db9c67SDouglas Anderson phy_power_on(sdhci_arasan->phy); 199b2db9c67SDouglas Anderson spin_lock_irq(&host->lock); 200b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 201802ac39aSShawn Lin 202b2db9c67SDouglas Anderson /* 203b2db9c67SDouglas Anderson * We'll now fall through to the below case with 204b2db9c67SDouglas Anderson * ctrl_phy = false (so we won't turn off/on). The 205b2db9c67SDouglas Anderson * sdhci_set_clock() will set the real clock. 206b2db9c67SDouglas Anderson */ 207b2db9c67SDouglas Anderson } else if (clock > PHY_CLK_TOO_SLOW_HZ) { 208b2db9c67SDouglas Anderson /* 209b2db9c67SDouglas Anderson * At higher clock speeds the PHY is fine being power 210b2db9c67SDouglas Anderson * cycled and docs say you _should_ power cycle when 211b2db9c67SDouglas Anderson * changing clock speeds. 212b2db9c67SDouglas Anderson */ 213b2db9c67SDouglas Anderson ctrl_phy = true; 214b2db9c67SDouglas Anderson } 215b2db9c67SDouglas Anderson } 216b2db9c67SDouglas Anderson 217b2db9c67SDouglas Anderson if (ctrl_phy && sdhci_arasan->is_phy_on) { 218802ac39aSShawn Lin spin_unlock_irq(&host->lock); 219802ac39aSShawn Lin phy_power_off(sdhci_arasan->phy); 220802ac39aSShawn Lin spin_lock_irq(&host->lock); 221b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = false; 222802ac39aSShawn Lin } 223802ac39aSShawn Lin 224802ac39aSShawn Lin sdhci_set_clock(host, clock); 225802ac39aSShawn Lin 2266fc09244SDouglas Anderson if (ctrl_phy) { 227802ac39aSShawn Lin spin_unlock_irq(&host->lock); 228802ac39aSShawn Lin phy_power_on(sdhci_arasan->phy); 229802ac39aSShawn Lin spin_lock_irq(&host->lock); 230b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 231802ac39aSShawn Lin } 232802ac39aSShawn Lin } 233802ac39aSShawn Lin 234a05c8465SShawn Lin static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc, 235a05c8465SShawn Lin struct mmc_ios *ios) 236a05c8465SShawn Lin { 237a05c8465SShawn Lin u32 vendor; 238a05c8465SShawn Lin struct sdhci_host *host = mmc_priv(mmc); 239a05c8465SShawn Lin 240a05c8465SShawn Lin vendor = readl(host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); 241a05c8465SShawn Lin if (ios->enhanced_strobe) 242a05c8465SShawn Lin vendor |= VENDOR_ENHANCED_STROBE; 243a05c8465SShawn Lin else 244a05c8465SShawn Lin vendor &= ~VENDOR_ENHANCED_STROBE; 245a05c8465SShawn Lin 246a05c8465SShawn Lin writel(vendor, host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); 247a05c8465SShawn Lin } 248a05c8465SShawn Lin 24913d62fd2SWei Yongjun static void sdhci_arasan_reset(struct sdhci_host *host, u8 mask) 2503794c542SZach Brown { 2513794c542SZach Brown u8 ctrl; 2523794c542SZach Brown struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2533794c542SZach Brown struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 2543794c542SZach Brown 2553794c542SZach Brown sdhci_reset(host, mask); 2563794c542SZach Brown 2573794c542SZach Brown if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) { 2583794c542SZach Brown ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 2593794c542SZach Brown ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN; 2603794c542SZach Brown sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 2613794c542SZach Brown } 2623794c542SZach Brown } 2633794c542SZach Brown 2648a3bee9bSShawn Lin static int sdhci_arasan_voltage_switch(struct mmc_host *mmc, 2658a3bee9bSShawn Lin struct mmc_ios *ios) 2668a3bee9bSShawn Lin { 2678a3bee9bSShawn Lin switch (ios->signal_voltage) { 2688a3bee9bSShawn Lin case MMC_SIGNAL_VOLTAGE_180: 2698a3bee9bSShawn Lin /* 2708a3bee9bSShawn Lin * Plese don't switch to 1V8 as arasan,5.1 doesn't 2718a3bee9bSShawn Lin * actually refer to this setting to indicate the 2728a3bee9bSShawn Lin * signal voltage and the state machine will be broken 2738a3bee9bSShawn Lin * actually if we force to enable 1V8. That's something 2748a3bee9bSShawn Lin * like broken quirk but we could work around here. 2758a3bee9bSShawn Lin */ 2768a3bee9bSShawn Lin return 0; 2778a3bee9bSShawn Lin case MMC_SIGNAL_VOLTAGE_330: 2788a3bee9bSShawn Lin case MMC_SIGNAL_VOLTAGE_120: 2798a3bee9bSShawn Lin /* We don't support 3V3 and 1V2 */ 2808a3bee9bSShawn Lin break; 2818a3bee9bSShawn Lin } 2828a3bee9bSShawn Lin 2838a3bee9bSShawn Lin return -EINVAL; 2848a3bee9bSShawn Lin } 2858a3bee9bSShawn Lin 286e3ec3a3dSSoren Brinkmann static struct sdhci_ops sdhci_arasan_ops = { 287802ac39aSShawn Lin .set_clock = sdhci_arasan_set_clock, 288e3ec3a3dSSoren Brinkmann .get_max_clock = sdhci_pltfm_clk_get_max_clock, 289e3ec3a3dSSoren Brinkmann .get_timeout_clock = sdhci_arasan_get_timeout_clock, 2902317f56cSRussell King .set_bus_width = sdhci_set_bus_width, 2913794c542SZach Brown .reset = sdhci_arasan_reset, 29296d7b78cSRussell King .set_uhs_signaling = sdhci_set_uhs_signaling, 293e3ec3a3dSSoren Brinkmann }; 294e3ec3a3dSSoren Brinkmann 295e3ec3a3dSSoren Brinkmann static struct sdhci_pltfm_data sdhci_arasan_pdata = { 296e3ec3a3dSSoren Brinkmann .ops = &sdhci_arasan_ops, 2972d532d45SSuneel Garapati .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 2982d532d45SSuneel Garapati .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 2992d532d45SSuneel Garapati SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, 300e3ec3a3dSSoren Brinkmann }; 301e3ec3a3dSSoren Brinkmann 302e3ec3a3dSSoren Brinkmann #ifdef CONFIG_PM_SLEEP 303e3ec3a3dSSoren Brinkmann /** 304e3ec3a3dSSoren Brinkmann * sdhci_arasan_suspend - Suspend method for the driver 305e3ec3a3dSSoren Brinkmann * @dev: Address of the device structure 306e3ec3a3dSSoren Brinkmann * Returns 0 on success and error value on error 307e3ec3a3dSSoren Brinkmann * 308e3ec3a3dSSoren Brinkmann * Put the device in a low power state. 309e3ec3a3dSSoren Brinkmann */ 310e3ec3a3dSSoren Brinkmann static int sdhci_arasan_suspend(struct device *dev) 311e3ec3a3dSSoren Brinkmann { 312e3ec3a3dSSoren Brinkmann struct platform_device *pdev = to_platform_device(dev); 313e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 314e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 31589211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 316e3ec3a3dSSoren Brinkmann int ret; 317e3ec3a3dSSoren Brinkmann 318d38dcad4SAdrian Hunter if (host->tuning_mode != SDHCI_TUNING_MODE_3) 319d38dcad4SAdrian Hunter mmc_retune_needed(host->mmc); 320d38dcad4SAdrian Hunter 321e3ec3a3dSSoren Brinkmann ret = sdhci_suspend_host(host); 322e3ec3a3dSSoren Brinkmann if (ret) 323e3ec3a3dSSoren Brinkmann return ret; 324e3ec3a3dSSoren Brinkmann 325b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy) && sdhci_arasan->is_phy_on) { 32691aa3661SShawn Lin ret = phy_power_off(sdhci_arasan->phy); 32791aa3661SShawn Lin if (ret) { 32891aa3661SShawn Lin dev_err(dev, "Cannot power off phy.\n"); 32991aa3661SShawn Lin sdhci_resume_host(host); 33091aa3661SShawn Lin return ret; 33191aa3661SShawn Lin } 332b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = false; 33391aa3661SShawn Lin } 33491aa3661SShawn Lin 335e3ec3a3dSSoren Brinkmann clk_disable(pltfm_host->clk); 336e3ec3a3dSSoren Brinkmann clk_disable(sdhci_arasan->clk_ahb); 337e3ec3a3dSSoren Brinkmann 338e3ec3a3dSSoren Brinkmann return 0; 339e3ec3a3dSSoren Brinkmann } 340e3ec3a3dSSoren Brinkmann 341e3ec3a3dSSoren Brinkmann /** 342e3ec3a3dSSoren Brinkmann * sdhci_arasan_resume - Resume method for the driver 343e3ec3a3dSSoren Brinkmann * @dev: Address of the device structure 344e3ec3a3dSSoren Brinkmann * Returns 0 on success and error value on error 345e3ec3a3dSSoren Brinkmann * 346e3ec3a3dSSoren Brinkmann * Resume operation after suspend 347e3ec3a3dSSoren Brinkmann */ 348e3ec3a3dSSoren Brinkmann static int sdhci_arasan_resume(struct device *dev) 349e3ec3a3dSSoren Brinkmann { 350e3ec3a3dSSoren Brinkmann struct platform_device *pdev = to_platform_device(dev); 351e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 352e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 35389211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 354e3ec3a3dSSoren Brinkmann int ret; 355e3ec3a3dSSoren Brinkmann 356e3ec3a3dSSoren Brinkmann ret = clk_enable(sdhci_arasan->clk_ahb); 357e3ec3a3dSSoren Brinkmann if (ret) { 358e3ec3a3dSSoren Brinkmann dev_err(dev, "Cannot enable AHB clock.\n"); 359e3ec3a3dSSoren Brinkmann return ret; 360e3ec3a3dSSoren Brinkmann } 361e3ec3a3dSSoren Brinkmann 362e3ec3a3dSSoren Brinkmann ret = clk_enable(pltfm_host->clk); 363e3ec3a3dSSoren Brinkmann if (ret) { 364e3ec3a3dSSoren Brinkmann dev_err(dev, "Cannot enable SD clock.\n"); 365e3ec3a3dSSoren Brinkmann return ret; 366e3ec3a3dSSoren Brinkmann } 367e3ec3a3dSSoren Brinkmann 368b2db9c67SDouglas Anderson if (!IS_ERR(sdhci_arasan->phy) && host->mmc->actual_clock) { 36991aa3661SShawn Lin ret = phy_power_on(sdhci_arasan->phy); 37091aa3661SShawn Lin if (ret) { 37191aa3661SShawn Lin dev_err(dev, "Cannot power on phy.\n"); 37291aa3661SShawn Lin return ret; 37391aa3661SShawn Lin } 374b2db9c67SDouglas Anderson sdhci_arasan->is_phy_on = true; 37591aa3661SShawn Lin } 37691aa3661SShawn Lin 377e3ec3a3dSSoren Brinkmann return sdhci_resume_host(host); 378e3ec3a3dSSoren Brinkmann } 379e3ec3a3dSSoren Brinkmann #endif /* ! CONFIG_PM_SLEEP */ 380e3ec3a3dSSoren Brinkmann 381e3ec3a3dSSoren Brinkmann static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, 382e3ec3a3dSSoren Brinkmann sdhci_arasan_resume); 383e3ec3a3dSSoren Brinkmann 3843ea4666eSDouglas Anderson static const struct of_device_id sdhci_arasan_of_match[] = { 3853ea4666eSDouglas Anderson /* SoC-specific compatible strings w/ soc_ctl_map */ 3863ea4666eSDouglas Anderson { 3873ea4666eSDouglas Anderson .compatible = "rockchip,rk3399-sdhci-5.1", 3883ea4666eSDouglas Anderson .data = &rk3399_soc_ctl_map, 3893ea4666eSDouglas Anderson }, 3903ea4666eSDouglas Anderson 3913ea4666eSDouglas Anderson /* Generic compatible below here */ 3923ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-8.9a" }, 3933ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-5.1" }, 3943ea4666eSDouglas Anderson { .compatible = "arasan,sdhci-4.9a" }, 3953ea4666eSDouglas Anderson 3963ea4666eSDouglas Anderson { /* sentinel */ } 3973ea4666eSDouglas Anderson }; 3983ea4666eSDouglas Anderson MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); 3993ea4666eSDouglas Anderson 4003ea4666eSDouglas Anderson /** 401c390f211SDouglas Anderson * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate 402c390f211SDouglas Anderson * 403c390f211SDouglas Anderson * Return the current actual rate of the SD card clock. This can be used 404c390f211SDouglas Anderson * to communicate with out PHY. 405c390f211SDouglas Anderson * 406c390f211SDouglas Anderson * @hw: Pointer to the hardware clock structure. 407c390f211SDouglas Anderson * @parent_rate The parent rate (should be rate of clk_xin). 408c390f211SDouglas Anderson * Returns the card clock rate. 409c390f211SDouglas Anderson */ 410c390f211SDouglas Anderson static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw, 411c390f211SDouglas Anderson unsigned long parent_rate) 412c390f211SDouglas Anderson 413c390f211SDouglas Anderson { 414c390f211SDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = 415c390f211SDouglas Anderson container_of(hw, struct sdhci_arasan_data, sdcardclk_hw); 416c390f211SDouglas Anderson struct sdhci_host *host = sdhci_arasan->host; 417c390f211SDouglas Anderson 418c390f211SDouglas Anderson return host->mmc->actual_clock; 419c390f211SDouglas Anderson } 420c390f211SDouglas Anderson 421c390f211SDouglas Anderson static const struct clk_ops arasan_sdcardclk_ops = { 422c390f211SDouglas Anderson .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, 423c390f211SDouglas Anderson }; 424c390f211SDouglas Anderson 425c390f211SDouglas Anderson /** 426b2ca77c9SShawn Lin * sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier 427b2ca77c9SShawn Lin * 428b2ca77c9SShawn Lin * The corecfg_clockmultiplier is supposed to contain clock multiplier 429b2ca77c9SShawn Lin * value of programmable clock generator. 430b2ca77c9SShawn Lin * 431b2ca77c9SShawn Lin * NOTES: 432b2ca77c9SShawn Lin * - Many existing devices don't seem to do this and work fine. To keep 433b2ca77c9SShawn Lin * compatibility for old hardware where the device tree doesn't provide a 434b2ca77c9SShawn Lin * register map, this function is a noop if a soc_ctl_map hasn't been provided 435b2ca77c9SShawn Lin * for this platform. 436b2ca77c9SShawn Lin * - The value of corecfg_clockmultiplier should sync with that of corresponding 437b2ca77c9SShawn Lin * value reading from sdhci_capability_register. So this function is called 438b2ca77c9SShawn Lin * once at probe time and never called again. 439b2ca77c9SShawn Lin * 440b2ca77c9SShawn Lin * @host: The sdhci_host 441b2ca77c9SShawn Lin */ 442b2ca77c9SShawn Lin static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host, 443b2ca77c9SShawn Lin u32 value) 444b2ca77c9SShawn Lin { 445b2ca77c9SShawn Lin struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 446b2ca77c9SShawn Lin struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 447b2ca77c9SShawn Lin const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = 448b2ca77c9SShawn Lin sdhci_arasan->soc_ctl_map; 449b2ca77c9SShawn Lin 450b2ca77c9SShawn Lin /* Having a map is optional */ 451b2ca77c9SShawn Lin if (!soc_ctl_map) 452b2ca77c9SShawn Lin return; 453b2ca77c9SShawn Lin 454b2ca77c9SShawn Lin /* If we have a map, we expect to have a syscon */ 455b2ca77c9SShawn Lin if (!sdhci_arasan->soc_ctl_base) { 456b2ca77c9SShawn Lin pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", 457b2ca77c9SShawn Lin mmc_hostname(host->mmc)); 458b2ca77c9SShawn Lin return; 459b2ca77c9SShawn Lin } 460b2ca77c9SShawn Lin 461b2ca77c9SShawn Lin sdhci_arasan_syscon_write(host, &soc_ctl_map->clockmultiplier, value); 462b2ca77c9SShawn Lin } 463b2ca77c9SShawn Lin 464b2ca77c9SShawn Lin /** 4653ea4666eSDouglas Anderson * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq 4663ea4666eSDouglas Anderson * 4673ea4666eSDouglas Anderson * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This 4683ea4666eSDouglas Anderson * function can be used to make that happen. 4693ea4666eSDouglas Anderson * 4703ea4666eSDouglas Anderson * NOTES: 4713ea4666eSDouglas Anderson * - Many existing devices don't seem to do this and work fine. To keep 4723ea4666eSDouglas Anderson * compatibility for old hardware where the device tree doesn't provide a 4733ea4666eSDouglas Anderson * register map, this function is a noop if a soc_ctl_map hasn't been provided 4743ea4666eSDouglas Anderson * for this platform. 4753ea4666eSDouglas Anderson * - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider 4763ea4666eSDouglas Anderson * to achieve lower clock rates. That means that this function is called once 4773ea4666eSDouglas Anderson * at probe time and never called again. 4783ea4666eSDouglas Anderson * 4793ea4666eSDouglas Anderson * @host: The sdhci_host 4803ea4666eSDouglas Anderson */ 4813ea4666eSDouglas Anderson static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host) 4823ea4666eSDouglas Anderson { 4833ea4666eSDouglas Anderson struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4843ea4666eSDouglas Anderson struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 4853ea4666eSDouglas Anderson const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = 4863ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_map; 4873ea4666eSDouglas Anderson u32 mhz = DIV_ROUND_CLOSEST(clk_get_rate(pltfm_host->clk), 1000000); 4883ea4666eSDouglas Anderson 4893ea4666eSDouglas Anderson /* Having a map is optional */ 4903ea4666eSDouglas Anderson if (!soc_ctl_map) 4913ea4666eSDouglas Anderson return; 4923ea4666eSDouglas Anderson 4933ea4666eSDouglas Anderson /* If we have a map, we expect to have a syscon */ 4943ea4666eSDouglas Anderson if (!sdhci_arasan->soc_ctl_base) { 4953ea4666eSDouglas Anderson pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", 4963ea4666eSDouglas Anderson mmc_hostname(host->mmc)); 4973ea4666eSDouglas Anderson return; 4983ea4666eSDouglas Anderson } 4993ea4666eSDouglas Anderson 5003ea4666eSDouglas Anderson sdhci_arasan_syscon_write(host, &soc_ctl_map->baseclkfreq, mhz); 5013ea4666eSDouglas Anderson } 5023ea4666eSDouglas Anderson 503c390f211SDouglas Anderson /** 504c390f211SDouglas Anderson * sdhci_arasan_register_sdclk - Register the sdclk for a PHY to use 505c390f211SDouglas Anderson * 506c390f211SDouglas Anderson * Some PHY devices need to know what the actual card clock is. In order for 507c390f211SDouglas Anderson * them to find out, we'll provide a clock through the common clock framework 508c390f211SDouglas Anderson * for them to query. 509c390f211SDouglas Anderson * 510c390f211SDouglas Anderson * Note: without seriously re-architecting SDHCI's clock code and testing on 511c390f211SDouglas Anderson * all platforms, there's no way to create a totally beautiful clock here 512c390f211SDouglas Anderson * with all clock ops implemented. Instead, we'll just create a clock that can 513c390f211SDouglas Anderson * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock 514c390f211SDouglas Anderson * framework that we're doing things behind its back. This should be sufficient 515c390f211SDouglas Anderson * to create nice clean device tree bindings and later (if needed) we can try 516c390f211SDouglas Anderson * re-architecting SDHCI if we see some benefit to it. 517c390f211SDouglas Anderson * 518c390f211SDouglas Anderson * @sdhci_arasan: Our private data structure. 519c390f211SDouglas Anderson * @clk_xin: Pointer to the functional clock 520c390f211SDouglas Anderson * @dev: Pointer to our struct device. 521c390f211SDouglas Anderson * Returns 0 on success and error value on error 522c390f211SDouglas Anderson */ 523c390f211SDouglas Anderson static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan, 524c390f211SDouglas Anderson struct clk *clk_xin, 525c390f211SDouglas Anderson struct device *dev) 526c390f211SDouglas Anderson { 527c390f211SDouglas Anderson struct device_node *np = dev->of_node; 528c390f211SDouglas Anderson struct clk_init_data sdcardclk_init; 529c390f211SDouglas Anderson const char *parent_clk_name; 530c390f211SDouglas Anderson int ret; 531c390f211SDouglas Anderson 532c390f211SDouglas Anderson /* Providing a clock to the PHY is optional; no error if missing */ 533c390f211SDouglas Anderson if (!of_find_property(np, "#clock-cells", NULL)) 534c390f211SDouglas Anderson return 0; 535c390f211SDouglas Anderson 536c390f211SDouglas Anderson ret = of_property_read_string_index(np, "clock-output-names", 0, 537c390f211SDouglas Anderson &sdcardclk_init.name); 538c390f211SDouglas Anderson if (ret) { 539c390f211SDouglas Anderson dev_err(dev, "DT has #clock-cells but no clock-output-names\n"); 540c390f211SDouglas Anderson return ret; 541c390f211SDouglas Anderson } 542c390f211SDouglas Anderson 543c390f211SDouglas Anderson parent_clk_name = __clk_get_name(clk_xin); 544c390f211SDouglas Anderson sdcardclk_init.parent_names = &parent_clk_name; 545c390f211SDouglas Anderson sdcardclk_init.num_parents = 1; 546c390f211SDouglas Anderson sdcardclk_init.flags = CLK_GET_RATE_NOCACHE; 547c390f211SDouglas Anderson sdcardclk_init.ops = &arasan_sdcardclk_ops; 548c390f211SDouglas Anderson 549c390f211SDouglas Anderson sdhci_arasan->sdcardclk_hw.init = &sdcardclk_init; 550c390f211SDouglas Anderson sdhci_arasan->sdcardclk = 551c390f211SDouglas Anderson devm_clk_register(dev, &sdhci_arasan->sdcardclk_hw); 552c390f211SDouglas Anderson sdhci_arasan->sdcardclk_hw.init = NULL; 553c390f211SDouglas Anderson 554c390f211SDouglas Anderson ret = of_clk_add_provider(np, of_clk_src_simple_get, 555c390f211SDouglas Anderson sdhci_arasan->sdcardclk); 556c390f211SDouglas Anderson if (ret) 557c390f211SDouglas Anderson dev_err(dev, "Failed to add clock provider\n"); 558c390f211SDouglas Anderson 559c390f211SDouglas Anderson return ret; 560c390f211SDouglas Anderson } 561c390f211SDouglas Anderson 562c390f211SDouglas Anderson /** 563c390f211SDouglas Anderson * sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk() 564c390f211SDouglas Anderson * 565c390f211SDouglas Anderson * Should be called any time we're exiting and sdhci_arasan_register_sdclk() 566c390f211SDouglas Anderson * returned success. 567c390f211SDouglas Anderson * 568c390f211SDouglas Anderson * @dev: Pointer to our struct device. 569c390f211SDouglas Anderson */ 570c390f211SDouglas Anderson static void sdhci_arasan_unregister_sdclk(struct device *dev) 571c390f211SDouglas Anderson { 572c390f211SDouglas Anderson struct device_node *np = dev->of_node; 573c390f211SDouglas Anderson 574c390f211SDouglas Anderson if (!of_find_property(np, "#clock-cells", NULL)) 575c390f211SDouglas Anderson return; 576c390f211SDouglas Anderson 577c390f211SDouglas Anderson of_clk_del_provider(dev->of_node); 578c390f211SDouglas Anderson } 579c390f211SDouglas Anderson 580e3ec3a3dSSoren Brinkmann static int sdhci_arasan_probe(struct platform_device *pdev) 581e3ec3a3dSSoren Brinkmann { 582e3ec3a3dSSoren Brinkmann int ret; 5833ea4666eSDouglas Anderson const struct of_device_id *match; 5843ea4666eSDouglas Anderson struct device_node *node; 585e3ec3a3dSSoren Brinkmann struct clk *clk_xin; 586e3ec3a3dSSoren Brinkmann struct sdhci_host *host; 587e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host; 588e3ec3a3dSSoren Brinkmann struct sdhci_arasan_data *sdhci_arasan; 5893794c542SZach Brown struct device_node *np = pdev->dev.of_node; 590e3ec3a3dSSoren Brinkmann 59189211418SJisheng Zhang host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 59289211418SJisheng Zhang sizeof(*sdhci_arasan)); 59389211418SJisheng Zhang if (IS_ERR(host)) 59489211418SJisheng Zhang return PTR_ERR(host); 59589211418SJisheng Zhang 59689211418SJisheng Zhang pltfm_host = sdhci_priv(host); 59789211418SJisheng Zhang sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 598c390f211SDouglas Anderson sdhci_arasan->host = host; 599e3ec3a3dSSoren Brinkmann 6003ea4666eSDouglas Anderson match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node); 6013ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_map = match->data; 6023ea4666eSDouglas Anderson 6033ea4666eSDouglas Anderson node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0); 6043ea4666eSDouglas Anderson if (node) { 6053ea4666eSDouglas Anderson sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node); 6063ea4666eSDouglas Anderson of_node_put(node); 6073ea4666eSDouglas Anderson 6083ea4666eSDouglas Anderson if (IS_ERR(sdhci_arasan->soc_ctl_base)) { 6093ea4666eSDouglas Anderson ret = PTR_ERR(sdhci_arasan->soc_ctl_base); 6103ea4666eSDouglas Anderson if (ret != -EPROBE_DEFER) 6113ea4666eSDouglas Anderson dev_err(&pdev->dev, "Can't get syscon: %d\n", 6123ea4666eSDouglas Anderson ret); 6133ea4666eSDouglas Anderson goto err_pltfm_free; 6143ea4666eSDouglas Anderson } 6153ea4666eSDouglas Anderson } 6163ea4666eSDouglas Anderson 617e3ec3a3dSSoren Brinkmann sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); 618e3ec3a3dSSoren Brinkmann if (IS_ERR(sdhci_arasan->clk_ahb)) { 619e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "clk_ahb clock not found.\n"); 620278d0962SShawn Lin ret = PTR_ERR(sdhci_arasan->clk_ahb); 621278d0962SShawn Lin goto err_pltfm_free; 622e3ec3a3dSSoren Brinkmann } 623e3ec3a3dSSoren Brinkmann 624e3ec3a3dSSoren Brinkmann clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); 625e3ec3a3dSSoren Brinkmann if (IS_ERR(clk_xin)) { 626e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "clk_xin clock not found.\n"); 627278d0962SShawn Lin ret = PTR_ERR(clk_xin); 628278d0962SShawn Lin goto err_pltfm_free; 629e3ec3a3dSSoren Brinkmann } 630e3ec3a3dSSoren Brinkmann 631e3ec3a3dSSoren Brinkmann ret = clk_prepare_enable(sdhci_arasan->clk_ahb); 632e3ec3a3dSSoren Brinkmann if (ret) { 633e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); 634278d0962SShawn Lin goto err_pltfm_free; 635e3ec3a3dSSoren Brinkmann } 636e3ec3a3dSSoren Brinkmann 637e3ec3a3dSSoren Brinkmann ret = clk_prepare_enable(clk_xin); 638e3ec3a3dSSoren Brinkmann if (ret) { 639e3ec3a3dSSoren Brinkmann dev_err(&pdev->dev, "Unable to enable SD clock.\n"); 640e3ec3a3dSSoren Brinkmann goto clk_dis_ahb; 641e3ec3a3dSSoren Brinkmann } 642e3ec3a3dSSoren Brinkmann 643e3ec3a3dSSoren Brinkmann sdhci_get_of_property(pdev); 6443794c542SZach Brown 6453794c542SZach Brown if (of_property_read_bool(np, "xlnx,fails-without-test-cd")) 6463794c542SZach Brown sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST; 6473794c542SZach Brown 648e3ec3a3dSSoren Brinkmann pltfm_host->clk = clk_xin; 649e3ec3a3dSSoren Brinkmann 650b2ca77c9SShawn Lin if (of_device_is_compatible(pdev->dev.of_node, 651b2ca77c9SShawn Lin "rockchip,rk3399-sdhci-5.1")) 652b2ca77c9SShawn Lin sdhci_arasan_update_clockmultiplier(host, 0x0); 653b2ca77c9SShawn Lin 6543ea4666eSDouglas Anderson sdhci_arasan_update_baseclkfreq(host); 6553ea4666eSDouglas Anderson 656c390f211SDouglas Anderson ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev); 657c390f211SDouglas Anderson if (ret) 658c390f211SDouglas Anderson goto clk_disable_all; 659c390f211SDouglas Anderson 66016b23787SMichal Simek ret = mmc_of_parse(host->mmc); 66116b23787SMichal Simek if (ret) { 66216b23787SMichal Simek dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret); 663c390f211SDouglas Anderson goto unreg_clk; 66416b23787SMichal Simek } 66516b23787SMichal Simek 66691aa3661SShawn Lin sdhci_arasan->phy = ERR_PTR(-ENODEV); 66791aa3661SShawn Lin if (of_device_is_compatible(pdev->dev.of_node, 66891aa3661SShawn Lin "arasan,sdhci-5.1")) { 66991aa3661SShawn Lin sdhci_arasan->phy = devm_phy_get(&pdev->dev, 67091aa3661SShawn Lin "phy_arasan"); 67191aa3661SShawn Lin if (IS_ERR(sdhci_arasan->phy)) { 67291aa3661SShawn Lin ret = PTR_ERR(sdhci_arasan->phy); 67391aa3661SShawn Lin dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n"); 674c390f211SDouglas Anderson goto unreg_clk; 67591aa3661SShawn Lin } 67691aa3661SShawn Lin 67791aa3661SShawn Lin ret = phy_init(sdhci_arasan->phy); 67891aa3661SShawn Lin if (ret < 0) { 67991aa3661SShawn Lin dev_err(&pdev->dev, "phy_init err.\n"); 680c390f211SDouglas Anderson goto unreg_clk; 68191aa3661SShawn Lin } 68291aa3661SShawn Lin 683a05c8465SShawn Lin host->mmc_host_ops.hs400_enhanced_strobe = 684a05c8465SShawn Lin sdhci_arasan_hs400_enhanced_strobe; 6858a3bee9bSShawn Lin host->mmc_host_ops.start_signal_voltage_switch = 6868a3bee9bSShawn Lin sdhci_arasan_voltage_switch; 68791aa3661SShawn Lin } 68891aa3661SShawn Lin 689e3ec3a3dSSoren Brinkmann ret = sdhci_add_host(host); 690b1df9de7SMike Looijmans if (ret) 69191aa3661SShawn Lin goto err_add_host; 692e3ec3a3dSSoren Brinkmann 693e3ec3a3dSSoren Brinkmann return 0; 694e3ec3a3dSSoren Brinkmann 69591aa3661SShawn Lin err_add_host: 69691aa3661SShawn Lin if (!IS_ERR(sdhci_arasan->phy)) 69791aa3661SShawn Lin phy_exit(sdhci_arasan->phy); 698c390f211SDouglas Anderson unreg_clk: 699c390f211SDouglas Anderson sdhci_arasan_unregister_sdclk(&pdev->dev); 700e3ec3a3dSSoren Brinkmann clk_disable_all: 701e3ec3a3dSSoren Brinkmann clk_disable_unprepare(clk_xin); 702e3ec3a3dSSoren Brinkmann clk_dis_ahb: 703e3ec3a3dSSoren Brinkmann clk_disable_unprepare(sdhci_arasan->clk_ahb); 704278d0962SShawn Lin err_pltfm_free: 705278d0962SShawn Lin sdhci_pltfm_free(pdev); 706e3ec3a3dSSoren Brinkmann return ret; 707e3ec3a3dSSoren Brinkmann } 708e3ec3a3dSSoren Brinkmann 709e3ec3a3dSSoren Brinkmann static int sdhci_arasan_remove(struct platform_device *pdev) 710e3ec3a3dSSoren Brinkmann { 7110c7fe32eSJisheng Zhang int ret; 712e3ec3a3dSSoren Brinkmann struct sdhci_host *host = platform_get_drvdata(pdev); 713e3ec3a3dSSoren Brinkmann struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 71489211418SJisheng Zhang struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); 71589211418SJisheng Zhang struct clk *clk_ahb = sdhci_arasan->clk_ahb; 716e3ec3a3dSSoren Brinkmann 71791aa3661SShawn Lin if (!IS_ERR(sdhci_arasan->phy)) { 718b2db9c67SDouglas Anderson if (sdhci_arasan->is_phy_on) 71991aa3661SShawn Lin phy_power_off(sdhci_arasan->phy); 72091aa3661SShawn Lin phy_exit(sdhci_arasan->phy); 72191aa3661SShawn Lin } 72291aa3661SShawn Lin 723c390f211SDouglas Anderson sdhci_arasan_unregister_sdclk(&pdev->dev); 724c390f211SDouglas Anderson 7250c7fe32eSJisheng Zhang ret = sdhci_pltfm_unregister(pdev); 7260c7fe32eSJisheng Zhang 72789211418SJisheng Zhang clk_disable_unprepare(clk_ahb); 728e3ec3a3dSSoren Brinkmann 7290c7fe32eSJisheng Zhang return ret; 730e3ec3a3dSSoren Brinkmann } 731e3ec3a3dSSoren Brinkmann 732e3ec3a3dSSoren Brinkmann static struct platform_driver sdhci_arasan_driver = { 733e3ec3a3dSSoren Brinkmann .driver = { 734e3ec3a3dSSoren Brinkmann .name = "sdhci-arasan", 735e3ec3a3dSSoren Brinkmann .of_match_table = sdhci_arasan_of_match, 736e3ec3a3dSSoren Brinkmann .pm = &sdhci_arasan_dev_pm_ops, 737e3ec3a3dSSoren Brinkmann }, 738e3ec3a3dSSoren Brinkmann .probe = sdhci_arasan_probe, 739e3ec3a3dSSoren Brinkmann .remove = sdhci_arasan_remove, 740e3ec3a3dSSoren Brinkmann }; 741e3ec3a3dSSoren Brinkmann 742e3ec3a3dSSoren Brinkmann module_platform_driver(sdhci_arasan_driver); 743e3ec3a3dSSoren Brinkmann 744e3ec3a3dSSoren Brinkmann MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); 745e3ec3a3dSSoren Brinkmann MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>"); 746e3ec3a3dSSoren Brinkmann MODULE_LICENSE("GPL"); 747