1bb5f8ea4Sludovic.desroches@atmel.com /* 2bb5f8ea4Sludovic.desroches@atmel.com * Atmel SDMMC controller driver. 3bb5f8ea4Sludovic.desroches@atmel.com * 4bb5f8ea4Sludovic.desroches@atmel.com * Copyright (C) 2015 Atmel, 5bb5f8ea4Sludovic.desroches@atmel.com * 2015 Ludovic Desroches <ludovic.desroches@atmel.com> 6bb5f8ea4Sludovic.desroches@atmel.com * 7bb5f8ea4Sludovic.desroches@atmel.com * This software is licensed under the terms of the GNU General Public 8bb5f8ea4Sludovic.desroches@atmel.com * License version 2, as published by the Free Software Foundation, and 9bb5f8ea4Sludovic.desroches@atmel.com * may be copied, distributed, and modified under those terms. 10bb5f8ea4Sludovic.desroches@atmel.com * 11bb5f8ea4Sludovic.desroches@atmel.com * This program is distributed in the hope that it will be useful, 12bb5f8ea4Sludovic.desroches@atmel.com * but WITHOUT ANY WARRANTY; without even the implied warranty of 13bb5f8ea4Sludovic.desroches@atmel.com * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14bb5f8ea4Sludovic.desroches@atmel.com * GNU General Public License for more details. 15bb5f8ea4Sludovic.desroches@atmel.com */ 16bb5f8ea4Sludovic.desroches@atmel.com 17bb5f8ea4Sludovic.desroches@atmel.com #include <linux/clk.h> 184e289a7dSLudovic Desroches #include <linux/delay.h> 19bb5f8ea4Sludovic.desroches@atmel.com #include <linux/err.h> 20bb5f8ea4Sludovic.desroches@atmel.com #include <linux/io.h> 214406433dSLudovic Desroches #include <linux/kernel.h> 22bb5f8ea4Sludovic.desroches@atmel.com #include <linux/mmc/host.h> 2364e5cd72Sludovic.desroches@atmel.com #include <linux/mmc/slot-gpio.h> 24bb5f8ea4Sludovic.desroches@atmel.com #include <linux/module.h> 25bb5f8ea4Sludovic.desroches@atmel.com #include <linux/of.h> 26bb5f8ea4Sludovic.desroches@atmel.com #include <linux/of_device.h> 27f5f17813Sludovic.desroches@atmel.com #include <linux/pm.h> 28f5f17813Sludovic.desroches@atmel.com #include <linux/pm_runtime.h> 29bb5f8ea4Sludovic.desroches@atmel.com 30bb5f8ea4Sludovic.desroches@atmel.com #include "sdhci-pltfm.h" 31bb5f8ea4Sludovic.desroches@atmel.com 32bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR 0x230 33bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR_CAPWREN BIT(0) 34bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR_KEY (0x46 << 8) 35bb5f8ea4Sludovic.desroches@atmel.com 364406433dSLudovic Desroches #define SDHCI_AT91_PRESET_COMMON_CONF 0x400 /* drv type B, programmable clock mode */ 374406433dSLudovic Desroches 38bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_at91_priv { 39bb5f8ea4Sludovic.desroches@atmel.com struct clk *hclock; 40bb5f8ea4Sludovic.desroches@atmel.com struct clk *gck; 41bb5f8ea4Sludovic.desroches@atmel.com struct clk *mainck; 42bb5f8ea4Sludovic.desroches@atmel.com }; 43bb5f8ea4Sludovic.desroches@atmel.com 444e289a7dSLudovic Desroches static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock) 454e289a7dSLudovic Desroches { 464e289a7dSLudovic Desroches u16 clk; 474e289a7dSLudovic Desroches unsigned long timeout; 484e289a7dSLudovic Desroches 494e289a7dSLudovic Desroches host->mmc->actual_clock = 0; 504e289a7dSLudovic Desroches 514e289a7dSLudovic Desroches /* 524e289a7dSLudovic Desroches * There is no requirement to disable the internal clock before 534e289a7dSLudovic Desroches * changing the SD clock configuration. Moreover, disabling the 544e289a7dSLudovic Desroches * internal clock, changing the configuration and re-enabling the 554e289a7dSLudovic Desroches * internal clock causes some bugs. It can prevent to get the internal 564e289a7dSLudovic Desroches * clock stable flag ready and an unexpected switch to the base clock 574e289a7dSLudovic Desroches * when using presets. 584e289a7dSLudovic Desroches */ 594e289a7dSLudovic Desroches clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 604e289a7dSLudovic Desroches clk &= SDHCI_CLOCK_INT_EN; 614e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 624e289a7dSLudovic Desroches 634e289a7dSLudovic Desroches if (clock == 0) 644e289a7dSLudovic Desroches return; 654e289a7dSLudovic Desroches 664e289a7dSLudovic Desroches clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 674e289a7dSLudovic Desroches 684e289a7dSLudovic Desroches clk |= SDHCI_CLOCK_INT_EN; 694e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 704e289a7dSLudovic Desroches 714e289a7dSLudovic Desroches /* Wait max 20 ms */ 724e289a7dSLudovic Desroches timeout = 20; 734e289a7dSLudovic Desroches while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) 744e289a7dSLudovic Desroches & SDHCI_CLOCK_INT_STABLE)) { 754e289a7dSLudovic Desroches if (timeout == 0) { 764e289a7dSLudovic Desroches pr_err("%s: Internal clock never stabilised.\n", 774e289a7dSLudovic Desroches mmc_hostname(host->mmc)); 784e289a7dSLudovic Desroches return; 794e289a7dSLudovic Desroches } 804e289a7dSLudovic Desroches timeout--; 814e289a7dSLudovic Desroches mdelay(1); 824e289a7dSLudovic Desroches } 834e289a7dSLudovic Desroches 844e289a7dSLudovic Desroches clk |= SDHCI_CLOCK_CARD_EN; 854e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 864e289a7dSLudovic Desroches } 874e289a7dSLudovic Desroches 88bb5f8ea4Sludovic.desroches@atmel.com static const struct sdhci_ops sdhci_at91_sama5d2_ops = { 894e289a7dSLudovic Desroches .set_clock = sdhci_at91_set_clock, 90bb5f8ea4Sludovic.desroches@atmel.com .set_bus_width = sdhci_set_bus_width, 91bb5f8ea4Sludovic.desroches@atmel.com .reset = sdhci_reset, 92bb5f8ea4Sludovic.desroches@atmel.com .set_uhs_signaling = sdhci_set_uhs_signaling, 93bb5f8ea4Sludovic.desroches@atmel.com }; 94bb5f8ea4Sludovic.desroches@atmel.com 95bb5f8ea4Sludovic.desroches@atmel.com static const struct sdhci_pltfm_data soc_data_sama5d2 = { 96bb5f8ea4Sludovic.desroches@atmel.com .ops = &sdhci_at91_sama5d2_ops, 97bb5f8ea4Sludovic.desroches@atmel.com }; 98bb5f8ea4Sludovic.desroches@atmel.com 99bb5f8ea4Sludovic.desroches@atmel.com static const struct of_device_id sdhci_at91_dt_match[] = { 100bb5f8ea4Sludovic.desroches@atmel.com { .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 }, 101bb5f8ea4Sludovic.desroches@atmel.com {} 102bb5f8ea4Sludovic.desroches@atmel.com }; 103bb5f8ea4Sludovic.desroches@atmel.com 104f5f17813Sludovic.desroches@atmel.com #ifdef CONFIG_PM 105f5f17813Sludovic.desroches@atmel.com static int sdhci_at91_runtime_suspend(struct device *dev) 106f5f17813Sludovic.desroches@atmel.com { 107f5f17813Sludovic.desroches@atmel.com struct sdhci_host *host = dev_get_drvdata(dev); 108f5f17813Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 10910f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 110f5f17813Sludovic.desroches@atmel.com int ret; 111f5f17813Sludovic.desroches@atmel.com 112f5f17813Sludovic.desroches@atmel.com ret = sdhci_runtime_suspend_host(host); 113f5f17813Sludovic.desroches@atmel.com 114f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->gck); 115f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->hclock); 116f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->mainck); 117f5f17813Sludovic.desroches@atmel.com 118f5f17813Sludovic.desroches@atmel.com return ret; 119f5f17813Sludovic.desroches@atmel.com } 120f5f17813Sludovic.desroches@atmel.com 121f5f17813Sludovic.desroches@atmel.com static int sdhci_at91_runtime_resume(struct device *dev) 122f5f17813Sludovic.desroches@atmel.com { 123f5f17813Sludovic.desroches@atmel.com struct sdhci_host *host = dev_get_drvdata(dev); 124f5f17813Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 12510f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 126f5f17813Sludovic.desroches@atmel.com int ret; 127f5f17813Sludovic.desroches@atmel.com 128f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->mainck); 129f5f17813Sludovic.desroches@atmel.com if (ret) { 130f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable mainck\n"); 131f5f17813Sludovic.desroches@atmel.com return ret; 132f5f17813Sludovic.desroches@atmel.com } 133f5f17813Sludovic.desroches@atmel.com 134f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->hclock); 135f5f17813Sludovic.desroches@atmel.com if (ret) { 136f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable hclock\n"); 137f5f17813Sludovic.desroches@atmel.com return ret; 138f5f17813Sludovic.desroches@atmel.com } 139f5f17813Sludovic.desroches@atmel.com 140f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->gck); 141f5f17813Sludovic.desroches@atmel.com if (ret) { 142f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable gck\n"); 143f5f17813Sludovic.desroches@atmel.com return ret; 144f5f17813Sludovic.desroches@atmel.com } 145f5f17813Sludovic.desroches@atmel.com 146f5f17813Sludovic.desroches@atmel.com return sdhci_runtime_resume_host(host); 147f5f17813Sludovic.desroches@atmel.com } 148f5f17813Sludovic.desroches@atmel.com #endif /* CONFIG_PM */ 149f5f17813Sludovic.desroches@atmel.com 150f5f17813Sludovic.desroches@atmel.com static const struct dev_pm_ops sdhci_at91_dev_pm_ops = { 151f5f17813Sludovic.desroches@atmel.com SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 152f5f17813Sludovic.desroches@atmel.com pm_runtime_force_resume) 153f5f17813Sludovic.desroches@atmel.com SET_RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, 154f5f17813Sludovic.desroches@atmel.com sdhci_at91_runtime_resume, 155f5f17813Sludovic.desroches@atmel.com NULL) 156f5f17813Sludovic.desroches@atmel.com }; 157f5f17813Sludovic.desroches@atmel.com 158bb5f8ea4Sludovic.desroches@atmel.com static int sdhci_at91_probe(struct platform_device *pdev) 159bb5f8ea4Sludovic.desroches@atmel.com { 160bb5f8ea4Sludovic.desroches@atmel.com const struct of_device_id *match; 161bb5f8ea4Sludovic.desroches@atmel.com const struct sdhci_pltfm_data *soc_data; 162bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_host *host; 163bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host; 164bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_at91_priv *priv; 165bb5f8ea4Sludovic.desroches@atmel.com unsigned int caps0, caps1; 166bb5f8ea4Sludovic.desroches@atmel.com unsigned int clk_base, clk_mul; 167bb5f8ea4Sludovic.desroches@atmel.com unsigned int gck_rate, real_gck_rate; 168bb5f8ea4Sludovic.desroches@atmel.com int ret; 1694406433dSLudovic Desroches unsigned int preset_div; 170bb5f8ea4Sludovic.desroches@atmel.com 171bb5f8ea4Sludovic.desroches@atmel.com match = of_match_device(sdhci_at91_dt_match, &pdev->dev); 172bb5f8ea4Sludovic.desroches@atmel.com if (!match) 173bb5f8ea4Sludovic.desroches@atmel.com return -EINVAL; 174bb5f8ea4Sludovic.desroches@atmel.com soc_data = match->data; 175bb5f8ea4Sludovic.desroches@atmel.com 17610f1c135SJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data, sizeof(*priv)); 17710f1c135SJisheng Zhang if (IS_ERR(host)) 17810f1c135SJisheng Zhang return PTR_ERR(host); 17910f1c135SJisheng Zhang 18010f1c135SJisheng Zhang pltfm_host = sdhci_priv(host); 18110f1c135SJisheng Zhang priv = sdhci_pltfm_priv(pltfm_host); 182bb5f8ea4Sludovic.desroches@atmel.com 183bb5f8ea4Sludovic.desroches@atmel.com priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); 184bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->mainck)) { 185bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get baseclk\n"); 186bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->mainck); 187bb5f8ea4Sludovic.desroches@atmel.com } 188bb5f8ea4Sludovic.desroches@atmel.com 189bb5f8ea4Sludovic.desroches@atmel.com priv->hclock = devm_clk_get(&pdev->dev, "hclock"); 190bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->hclock)) { 191bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get hclock\n"); 192bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->hclock); 193bb5f8ea4Sludovic.desroches@atmel.com } 194bb5f8ea4Sludovic.desroches@atmel.com 195bb5f8ea4Sludovic.desroches@atmel.com priv->gck = devm_clk_get(&pdev->dev, "multclk"); 196bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->gck)) { 197bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get multclk\n"); 198bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->gck); 199bb5f8ea4Sludovic.desroches@atmel.com } 200bb5f8ea4Sludovic.desroches@atmel.com 201bb5f8ea4Sludovic.desroches@atmel.com /* 202bb5f8ea4Sludovic.desroches@atmel.com * The mult clock is provided by as a generated clock by the PMC 203bb5f8ea4Sludovic.desroches@atmel.com * controller. In order to set the rate of gck, we have to get the 204bb5f8ea4Sludovic.desroches@atmel.com * base clock rate and the clock mult from capabilities. 205bb5f8ea4Sludovic.desroches@atmel.com */ 206bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->hclock); 207bb5f8ea4Sludovic.desroches@atmel.com caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES); 208bb5f8ea4Sludovic.desroches@atmel.com caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1); 209bb5f8ea4Sludovic.desroches@atmel.com clk_base = (caps0 & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; 210bb5f8ea4Sludovic.desroches@atmel.com clk_mul = (caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; 211bb5f8ea4Sludovic.desroches@atmel.com gck_rate = clk_base * 1000000 * (clk_mul + 1); 212bb5f8ea4Sludovic.desroches@atmel.com ret = clk_set_rate(priv->gck, gck_rate); 213bb5f8ea4Sludovic.desroches@atmel.com if (ret < 0) { 214bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to set gck"); 215bb5f8ea4Sludovic.desroches@atmel.com goto hclock_disable_unprepare; 216bb5f8ea4Sludovic.desroches@atmel.com } 217bb5f8ea4Sludovic.desroches@atmel.com /* 218bb5f8ea4Sludovic.desroches@atmel.com * We need to check if we have the requested rate for gck because in 219bb5f8ea4Sludovic.desroches@atmel.com * some cases this rate could be not supported. If it happens, the rate 220bb5f8ea4Sludovic.desroches@atmel.com * is the closest one gck can provide. We have to update the value 221bb5f8ea4Sludovic.desroches@atmel.com * of clk mul. 222bb5f8ea4Sludovic.desroches@atmel.com */ 223bb5f8ea4Sludovic.desroches@atmel.com real_gck_rate = clk_get_rate(priv->gck); 224bb5f8ea4Sludovic.desroches@atmel.com if (real_gck_rate != gck_rate) { 225bb5f8ea4Sludovic.desroches@atmel.com clk_mul = real_gck_rate / (clk_base * 1000000) - 1; 226bb5f8ea4Sludovic.desroches@atmel.com caps1 &= (~SDHCI_CLOCK_MUL_MASK); 227bb5f8ea4Sludovic.desroches@atmel.com caps1 |= ((clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK); 228bb5f8ea4Sludovic.desroches@atmel.com /* Set capabilities in r/w mode. */ 229bb5f8ea4Sludovic.desroches@atmel.com writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR); 230bb5f8ea4Sludovic.desroches@atmel.com writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1); 231bb5f8ea4Sludovic.desroches@atmel.com /* Set capabilities in ro mode. */ 232bb5f8ea4Sludovic.desroches@atmel.com writel(0, host->ioaddr + SDMMC_CACR); 233bb5f8ea4Sludovic.desroches@atmel.com dev_info(&pdev->dev, "update clk mul to %u as gck rate is %u Hz\n", 234bb5f8ea4Sludovic.desroches@atmel.com clk_mul, real_gck_rate); 235bb5f8ea4Sludovic.desroches@atmel.com } 236bb5f8ea4Sludovic.desroches@atmel.com 2374406433dSLudovic Desroches /* 2384406433dSLudovic Desroches * We have to set preset values because it depends on the clk_mul 2394406433dSLudovic Desroches * value. Moreover, SDR104 is supported in a degraded mode since the 2404406433dSLudovic Desroches * maximum sd clock value is 120 MHz instead of 208 MHz. For that 2414406433dSLudovic Desroches * reason, we need to use presets to support SDR104. 2424406433dSLudovic Desroches */ 2434406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 24000000) - 1; 2444406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2454406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR12); 2464406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1; 2474406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2484406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR25); 2494406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 100000000) - 1; 2504406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2514406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR50); 2524406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 120000000) - 1; 2534406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2544406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR104); 2554406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1; 2564406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2574406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_DDR50); 2584406433dSLudovic Desroches 259bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->mainck); 260bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->gck); 261bb5f8ea4Sludovic.desroches@atmel.com 262bb5f8ea4Sludovic.desroches@atmel.com ret = mmc_of_parse(host->mmc); 263bb5f8ea4Sludovic.desroches@atmel.com if (ret) 264bb5f8ea4Sludovic.desroches@atmel.com goto clocks_disable_unprepare; 265bb5f8ea4Sludovic.desroches@atmel.com 266bb5f8ea4Sludovic.desroches@atmel.com sdhci_get_of_property(pdev); 267bb5f8ea4Sludovic.desroches@atmel.com 268f5f17813Sludovic.desroches@atmel.com pm_runtime_get_noresume(&pdev->dev); 269f5f17813Sludovic.desroches@atmel.com pm_runtime_set_active(&pdev->dev); 270f5f17813Sludovic.desroches@atmel.com pm_runtime_enable(&pdev->dev); 271f5f17813Sludovic.desroches@atmel.com pm_runtime_set_autosuspend_delay(&pdev->dev, 50); 272f5f17813Sludovic.desroches@atmel.com pm_runtime_use_autosuspend(&pdev->dev); 273f5f17813Sludovic.desroches@atmel.com 274bb5f8ea4Sludovic.desroches@atmel.com ret = sdhci_add_host(host); 275bb5f8ea4Sludovic.desroches@atmel.com if (ret) 276f5f17813Sludovic.desroches@atmel.com goto pm_runtime_disable; 277f5f17813Sludovic.desroches@atmel.com 27864e5cd72Sludovic.desroches@atmel.com /* 27964e5cd72Sludovic.desroches@atmel.com * When calling sdhci_runtime_suspend_host(), the sdhci layer makes 28064e5cd72Sludovic.desroches@atmel.com * the assumption that all the clocks of the controller are disabled. 28164e5cd72Sludovic.desroches@atmel.com * It means we can't get irq from it when it is runtime suspended. 28264e5cd72Sludovic.desroches@atmel.com * For that reason, it is not planned to wake-up on a card detect irq 28364e5cd72Sludovic.desroches@atmel.com * from the controller. 28464e5cd72Sludovic.desroches@atmel.com * If we want to use runtime PM and to be able to wake-up on card 28564e5cd72Sludovic.desroches@atmel.com * insertion, we have to use a GPIO for the card detection or we can 28664e5cd72Sludovic.desroches@atmel.com * use polling. Be aware that using polling will resume/suspend the 28764e5cd72Sludovic.desroches@atmel.com * controller between each attempt. 28864e5cd72Sludovic.desroches@atmel.com * Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries 28964e5cd72Sludovic.desroches@atmel.com * to enable polling via device tree with broken-cd property. 29064e5cd72Sludovic.desroches@atmel.com */ 291860951c5SJaehoon Chung if (mmc_card_is_removable(host->mmc) && 292287980e4SArnd Bergmann mmc_gpio_get_cd(host->mmc) < 0) { 29364e5cd72Sludovic.desroches@atmel.com host->mmc->caps |= MMC_CAP_NEEDS_POLL; 29464e5cd72Sludovic.desroches@atmel.com host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; 29564e5cd72Sludovic.desroches@atmel.com } 29664e5cd72Sludovic.desroches@atmel.com 297f5f17813Sludovic.desroches@atmel.com pm_runtime_put_autosuspend(&pdev->dev); 298bb5f8ea4Sludovic.desroches@atmel.com 299bb5f8ea4Sludovic.desroches@atmel.com return 0; 300bb5f8ea4Sludovic.desroches@atmel.com 301f5f17813Sludovic.desroches@atmel.com pm_runtime_disable: 302f5f17813Sludovic.desroches@atmel.com pm_runtime_disable(&pdev->dev); 303f5f17813Sludovic.desroches@atmel.com pm_runtime_set_suspended(&pdev->dev); 3042df9d58fSJisheng Zhang pm_runtime_put_noidle(&pdev->dev); 305bb5f8ea4Sludovic.desroches@atmel.com clocks_disable_unprepare: 306bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->gck); 307bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->mainck); 308bb5f8ea4Sludovic.desroches@atmel.com hclock_disable_unprepare: 309bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->hclock); 310bb5f8ea4Sludovic.desroches@atmel.com sdhci_pltfm_free(pdev); 311bb5f8ea4Sludovic.desroches@atmel.com return ret; 312bb5f8ea4Sludovic.desroches@atmel.com } 313bb5f8ea4Sludovic.desroches@atmel.com 314bb5f8ea4Sludovic.desroches@atmel.com static int sdhci_at91_remove(struct platform_device *pdev) 315bb5f8ea4Sludovic.desroches@atmel.com { 316bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_host *host = platform_get_drvdata(pdev); 317bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 31810f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 31910f1c135SJisheng Zhang struct clk *gck = priv->gck; 32010f1c135SJisheng Zhang struct clk *hclock = priv->hclock; 32110f1c135SJisheng Zhang struct clk *mainck = priv->mainck; 322bb5f8ea4Sludovic.desroches@atmel.com 323f5f17813Sludovic.desroches@atmel.com pm_runtime_get_sync(&pdev->dev); 324f5f17813Sludovic.desroches@atmel.com pm_runtime_disable(&pdev->dev); 325f5f17813Sludovic.desroches@atmel.com pm_runtime_put_noidle(&pdev->dev); 326f5f17813Sludovic.desroches@atmel.com 327bb5f8ea4Sludovic.desroches@atmel.com sdhci_pltfm_unregister(pdev); 328bb5f8ea4Sludovic.desroches@atmel.com 32910f1c135SJisheng Zhang clk_disable_unprepare(gck); 33010f1c135SJisheng Zhang clk_disable_unprepare(hclock); 33110f1c135SJisheng Zhang clk_disable_unprepare(mainck); 332bb5f8ea4Sludovic.desroches@atmel.com 333bb5f8ea4Sludovic.desroches@atmel.com return 0; 334bb5f8ea4Sludovic.desroches@atmel.com } 335bb5f8ea4Sludovic.desroches@atmel.com 336bb5f8ea4Sludovic.desroches@atmel.com static struct platform_driver sdhci_at91_driver = { 337bb5f8ea4Sludovic.desroches@atmel.com .driver = { 338bb5f8ea4Sludovic.desroches@atmel.com .name = "sdhci-at91", 339bb5f8ea4Sludovic.desroches@atmel.com .of_match_table = sdhci_at91_dt_match, 340f5f17813Sludovic.desroches@atmel.com .pm = &sdhci_at91_dev_pm_ops, 341bb5f8ea4Sludovic.desroches@atmel.com }, 342bb5f8ea4Sludovic.desroches@atmel.com .probe = sdhci_at91_probe, 343bb5f8ea4Sludovic.desroches@atmel.com .remove = sdhci_at91_remove, 344bb5f8ea4Sludovic.desroches@atmel.com }; 345bb5f8ea4Sludovic.desroches@atmel.com 346bb5f8ea4Sludovic.desroches@atmel.com module_platform_driver(sdhci_at91_driver); 347bb5f8ea4Sludovic.desroches@atmel.com 348bb5f8ea4Sludovic.desroches@atmel.com MODULE_DESCRIPTION("SDHCI driver for at91"); 349bb5f8ea4Sludovic.desroches@atmel.com MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>"); 350bb5f8ea4Sludovic.desroches@atmel.com MODULE_LICENSE("GPL v2"); 351