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 88*2ce0c7b6SRomain Izard /* 89*2ce0c7b6SRomain Izard * In this specific implementation of the SDHCI controller, the power register 90*2ce0c7b6SRomain Izard * needs to have a valid voltage set even when the power supply is managed by 91*2ce0c7b6SRomain Izard * an external regulator. 92*2ce0c7b6SRomain Izard */ 93*2ce0c7b6SRomain Izard static void sdhci_at91_set_power(struct sdhci_host *host, unsigned char mode, 94*2ce0c7b6SRomain Izard unsigned short vdd) 95*2ce0c7b6SRomain Izard { 96*2ce0c7b6SRomain Izard if (!IS_ERR(host->mmc->supply.vmmc)) { 97*2ce0c7b6SRomain Izard struct mmc_host *mmc = host->mmc; 98*2ce0c7b6SRomain Izard 99*2ce0c7b6SRomain Izard spin_unlock_irq(&host->lock); 100*2ce0c7b6SRomain Izard mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); 101*2ce0c7b6SRomain Izard spin_lock_irq(&host->lock); 102*2ce0c7b6SRomain Izard } 103*2ce0c7b6SRomain Izard sdhci_set_power_noreg(host, mode, vdd); 104*2ce0c7b6SRomain Izard } 105*2ce0c7b6SRomain Izard 106bb5f8ea4Sludovic.desroches@atmel.com static const struct sdhci_ops sdhci_at91_sama5d2_ops = { 1074e289a7dSLudovic Desroches .set_clock = sdhci_at91_set_clock, 108bb5f8ea4Sludovic.desroches@atmel.com .set_bus_width = sdhci_set_bus_width, 109bb5f8ea4Sludovic.desroches@atmel.com .reset = sdhci_reset, 110bb5f8ea4Sludovic.desroches@atmel.com .set_uhs_signaling = sdhci_set_uhs_signaling, 111*2ce0c7b6SRomain Izard .set_power = sdhci_at91_set_power, 112bb5f8ea4Sludovic.desroches@atmel.com }; 113bb5f8ea4Sludovic.desroches@atmel.com 114bb5f8ea4Sludovic.desroches@atmel.com static const struct sdhci_pltfm_data soc_data_sama5d2 = { 115bb5f8ea4Sludovic.desroches@atmel.com .ops = &sdhci_at91_sama5d2_ops, 116bb5f8ea4Sludovic.desroches@atmel.com }; 117bb5f8ea4Sludovic.desroches@atmel.com 118bb5f8ea4Sludovic.desroches@atmel.com static const struct of_device_id sdhci_at91_dt_match[] = { 119bb5f8ea4Sludovic.desroches@atmel.com { .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 }, 120bb5f8ea4Sludovic.desroches@atmel.com {} 121bb5f8ea4Sludovic.desroches@atmel.com }; 122d9943c68SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, sdhci_at91_dt_match); 123bb5f8ea4Sludovic.desroches@atmel.com 124f5f17813Sludovic.desroches@atmel.com #ifdef CONFIG_PM 125f5f17813Sludovic.desroches@atmel.com static int sdhci_at91_runtime_suspend(struct device *dev) 126f5f17813Sludovic.desroches@atmel.com { 127f5f17813Sludovic.desroches@atmel.com struct sdhci_host *host = dev_get_drvdata(dev); 128f5f17813Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 12910f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 130f5f17813Sludovic.desroches@atmel.com int ret; 131f5f17813Sludovic.desroches@atmel.com 132f5f17813Sludovic.desroches@atmel.com ret = sdhci_runtime_suspend_host(host); 133f5f17813Sludovic.desroches@atmel.com 134f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->gck); 135f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->hclock); 136f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->mainck); 137f5f17813Sludovic.desroches@atmel.com 138f5f17813Sludovic.desroches@atmel.com return ret; 139f5f17813Sludovic.desroches@atmel.com } 140f5f17813Sludovic.desroches@atmel.com 141f5f17813Sludovic.desroches@atmel.com static int sdhci_at91_runtime_resume(struct device *dev) 142f5f17813Sludovic.desroches@atmel.com { 143f5f17813Sludovic.desroches@atmel.com struct sdhci_host *host = dev_get_drvdata(dev); 144f5f17813Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 14510f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 146f5f17813Sludovic.desroches@atmel.com int ret; 147f5f17813Sludovic.desroches@atmel.com 148f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->mainck); 149f5f17813Sludovic.desroches@atmel.com if (ret) { 150f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable mainck\n"); 151f5f17813Sludovic.desroches@atmel.com return ret; 152f5f17813Sludovic.desroches@atmel.com } 153f5f17813Sludovic.desroches@atmel.com 154f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->hclock); 155f5f17813Sludovic.desroches@atmel.com if (ret) { 156f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable hclock\n"); 157f5f17813Sludovic.desroches@atmel.com return ret; 158f5f17813Sludovic.desroches@atmel.com } 159f5f17813Sludovic.desroches@atmel.com 160f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->gck); 161f5f17813Sludovic.desroches@atmel.com if (ret) { 162f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable gck\n"); 163f5f17813Sludovic.desroches@atmel.com return ret; 164f5f17813Sludovic.desroches@atmel.com } 165f5f17813Sludovic.desroches@atmel.com 166f5f17813Sludovic.desroches@atmel.com return sdhci_runtime_resume_host(host); 167f5f17813Sludovic.desroches@atmel.com } 168f5f17813Sludovic.desroches@atmel.com #endif /* CONFIG_PM */ 169f5f17813Sludovic.desroches@atmel.com 170f5f17813Sludovic.desroches@atmel.com static const struct dev_pm_ops sdhci_at91_dev_pm_ops = { 171f5f17813Sludovic.desroches@atmel.com SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 172f5f17813Sludovic.desroches@atmel.com pm_runtime_force_resume) 173f5f17813Sludovic.desroches@atmel.com SET_RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, 174f5f17813Sludovic.desroches@atmel.com sdhci_at91_runtime_resume, 175f5f17813Sludovic.desroches@atmel.com NULL) 176f5f17813Sludovic.desroches@atmel.com }; 177f5f17813Sludovic.desroches@atmel.com 178bb5f8ea4Sludovic.desroches@atmel.com static int sdhci_at91_probe(struct platform_device *pdev) 179bb5f8ea4Sludovic.desroches@atmel.com { 180bb5f8ea4Sludovic.desroches@atmel.com const struct of_device_id *match; 181bb5f8ea4Sludovic.desroches@atmel.com const struct sdhci_pltfm_data *soc_data; 182bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_host *host; 183bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host; 184bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_at91_priv *priv; 185bb5f8ea4Sludovic.desroches@atmel.com unsigned int caps0, caps1; 186bb5f8ea4Sludovic.desroches@atmel.com unsigned int clk_base, clk_mul; 187bb5f8ea4Sludovic.desroches@atmel.com unsigned int gck_rate, real_gck_rate; 188bb5f8ea4Sludovic.desroches@atmel.com int ret; 1894406433dSLudovic Desroches unsigned int preset_div; 190bb5f8ea4Sludovic.desroches@atmel.com 191bb5f8ea4Sludovic.desroches@atmel.com match = of_match_device(sdhci_at91_dt_match, &pdev->dev); 192bb5f8ea4Sludovic.desroches@atmel.com if (!match) 193bb5f8ea4Sludovic.desroches@atmel.com return -EINVAL; 194bb5f8ea4Sludovic.desroches@atmel.com soc_data = match->data; 195bb5f8ea4Sludovic.desroches@atmel.com 19610f1c135SJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data, sizeof(*priv)); 19710f1c135SJisheng Zhang if (IS_ERR(host)) 19810f1c135SJisheng Zhang return PTR_ERR(host); 19910f1c135SJisheng Zhang 20010f1c135SJisheng Zhang pltfm_host = sdhci_priv(host); 20110f1c135SJisheng Zhang priv = sdhci_pltfm_priv(pltfm_host); 202bb5f8ea4Sludovic.desroches@atmel.com 203bb5f8ea4Sludovic.desroches@atmel.com priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); 204bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->mainck)) { 205bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get baseclk\n"); 206bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->mainck); 207bb5f8ea4Sludovic.desroches@atmel.com } 208bb5f8ea4Sludovic.desroches@atmel.com 209bb5f8ea4Sludovic.desroches@atmel.com priv->hclock = devm_clk_get(&pdev->dev, "hclock"); 210bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->hclock)) { 211bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get hclock\n"); 212bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->hclock); 213bb5f8ea4Sludovic.desroches@atmel.com } 214bb5f8ea4Sludovic.desroches@atmel.com 215bb5f8ea4Sludovic.desroches@atmel.com priv->gck = devm_clk_get(&pdev->dev, "multclk"); 216bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->gck)) { 217bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get multclk\n"); 218bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->gck); 219bb5f8ea4Sludovic.desroches@atmel.com } 220bb5f8ea4Sludovic.desroches@atmel.com 221bb5f8ea4Sludovic.desroches@atmel.com /* 222bb5f8ea4Sludovic.desroches@atmel.com * The mult clock is provided by as a generated clock by the PMC 223bb5f8ea4Sludovic.desroches@atmel.com * controller. In order to set the rate of gck, we have to get the 224bb5f8ea4Sludovic.desroches@atmel.com * base clock rate and the clock mult from capabilities. 225bb5f8ea4Sludovic.desroches@atmel.com */ 226bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->hclock); 227bb5f8ea4Sludovic.desroches@atmel.com caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES); 228bb5f8ea4Sludovic.desroches@atmel.com caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1); 229bb5f8ea4Sludovic.desroches@atmel.com clk_base = (caps0 & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; 230bb5f8ea4Sludovic.desroches@atmel.com clk_mul = (caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; 231bb5f8ea4Sludovic.desroches@atmel.com gck_rate = clk_base * 1000000 * (clk_mul + 1); 232bb5f8ea4Sludovic.desroches@atmel.com ret = clk_set_rate(priv->gck, gck_rate); 233bb5f8ea4Sludovic.desroches@atmel.com if (ret < 0) { 234bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to set gck"); 235bb5f8ea4Sludovic.desroches@atmel.com goto hclock_disable_unprepare; 236bb5f8ea4Sludovic.desroches@atmel.com } 237bb5f8ea4Sludovic.desroches@atmel.com /* 238bb5f8ea4Sludovic.desroches@atmel.com * We need to check if we have the requested rate for gck because in 239bb5f8ea4Sludovic.desroches@atmel.com * some cases this rate could be not supported. If it happens, the rate 240bb5f8ea4Sludovic.desroches@atmel.com * is the closest one gck can provide. We have to update the value 241bb5f8ea4Sludovic.desroches@atmel.com * of clk mul. 242bb5f8ea4Sludovic.desroches@atmel.com */ 243bb5f8ea4Sludovic.desroches@atmel.com real_gck_rate = clk_get_rate(priv->gck); 244bb5f8ea4Sludovic.desroches@atmel.com if (real_gck_rate != gck_rate) { 245bb5f8ea4Sludovic.desroches@atmel.com clk_mul = real_gck_rate / (clk_base * 1000000) - 1; 246bb5f8ea4Sludovic.desroches@atmel.com caps1 &= (~SDHCI_CLOCK_MUL_MASK); 247bb5f8ea4Sludovic.desroches@atmel.com caps1 |= ((clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK); 248bb5f8ea4Sludovic.desroches@atmel.com /* Set capabilities in r/w mode. */ 249bb5f8ea4Sludovic.desroches@atmel.com writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR); 250bb5f8ea4Sludovic.desroches@atmel.com writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1); 251bb5f8ea4Sludovic.desroches@atmel.com /* Set capabilities in ro mode. */ 252bb5f8ea4Sludovic.desroches@atmel.com writel(0, host->ioaddr + SDMMC_CACR); 253bb5f8ea4Sludovic.desroches@atmel.com dev_info(&pdev->dev, "update clk mul to %u as gck rate is %u Hz\n", 254bb5f8ea4Sludovic.desroches@atmel.com clk_mul, real_gck_rate); 255bb5f8ea4Sludovic.desroches@atmel.com } 256bb5f8ea4Sludovic.desroches@atmel.com 2574406433dSLudovic Desroches /* 2584406433dSLudovic Desroches * We have to set preset values because it depends on the clk_mul 2594406433dSLudovic Desroches * value. Moreover, SDR104 is supported in a degraded mode since the 2604406433dSLudovic Desroches * maximum sd clock value is 120 MHz instead of 208 MHz. For that 2614406433dSLudovic Desroches * reason, we need to use presets to support SDR104. 2624406433dSLudovic Desroches */ 2634406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 24000000) - 1; 2644406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2654406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR12); 2664406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1; 2674406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2684406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR25); 2694406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 100000000) - 1; 2704406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2714406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR50); 2724406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 120000000) - 1; 2734406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2744406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR104); 2754406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1; 2764406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2774406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_DDR50); 2784406433dSLudovic Desroches 279bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->mainck); 280bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->gck); 281bb5f8ea4Sludovic.desroches@atmel.com 282bb5f8ea4Sludovic.desroches@atmel.com ret = mmc_of_parse(host->mmc); 283bb5f8ea4Sludovic.desroches@atmel.com if (ret) 284bb5f8ea4Sludovic.desroches@atmel.com goto clocks_disable_unprepare; 285bb5f8ea4Sludovic.desroches@atmel.com 286bb5f8ea4Sludovic.desroches@atmel.com sdhci_get_of_property(pdev); 287bb5f8ea4Sludovic.desroches@atmel.com 288f5f17813Sludovic.desroches@atmel.com pm_runtime_get_noresume(&pdev->dev); 289f5f17813Sludovic.desroches@atmel.com pm_runtime_set_active(&pdev->dev); 290f5f17813Sludovic.desroches@atmel.com pm_runtime_enable(&pdev->dev); 291f5f17813Sludovic.desroches@atmel.com pm_runtime_set_autosuspend_delay(&pdev->dev, 50); 292f5f17813Sludovic.desroches@atmel.com pm_runtime_use_autosuspend(&pdev->dev); 293f5f17813Sludovic.desroches@atmel.com 294bb5f8ea4Sludovic.desroches@atmel.com ret = sdhci_add_host(host); 295bb5f8ea4Sludovic.desroches@atmel.com if (ret) 296f5f17813Sludovic.desroches@atmel.com goto pm_runtime_disable; 297f5f17813Sludovic.desroches@atmel.com 29864e5cd72Sludovic.desroches@atmel.com /* 29964e5cd72Sludovic.desroches@atmel.com * When calling sdhci_runtime_suspend_host(), the sdhci layer makes 30064e5cd72Sludovic.desroches@atmel.com * the assumption that all the clocks of the controller are disabled. 30164e5cd72Sludovic.desroches@atmel.com * It means we can't get irq from it when it is runtime suspended. 30264e5cd72Sludovic.desroches@atmel.com * For that reason, it is not planned to wake-up on a card detect irq 30364e5cd72Sludovic.desroches@atmel.com * from the controller. 30464e5cd72Sludovic.desroches@atmel.com * If we want to use runtime PM and to be able to wake-up on card 30564e5cd72Sludovic.desroches@atmel.com * insertion, we have to use a GPIO for the card detection or we can 30664e5cd72Sludovic.desroches@atmel.com * use polling. Be aware that using polling will resume/suspend the 30764e5cd72Sludovic.desroches@atmel.com * controller between each attempt. 30864e5cd72Sludovic.desroches@atmel.com * Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries 30964e5cd72Sludovic.desroches@atmel.com * to enable polling via device tree with broken-cd property. 31064e5cd72Sludovic.desroches@atmel.com */ 311860951c5SJaehoon Chung if (mmc_card_is_removable(host->mmc) && 312287980e4SArnd Bergmann mmc_gpio_get_cd(host->mmc) < 0) { 31364e5cd72Sludovic.desroches@atmel.com host->mmc->caps |= MMC_CAP_NEEDS_POLL; 31464e5cd72Sludovic.desroches@atmel.com host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; 31564e5cd72Sludovic.desroches@atmel.com } 31664e5cd72Sludovic.desroches@atmel.com 317f5f17813Sludovic.desroches@atmel.com pm_runtime_put_autosuspend(&pdev->dev); 318bb5f8ea4Sludovic.desroches@atmel.com 319bb5f8ea4Sludovic.desroches@atmel.com return 0; 320bb5f8ea4Sludovic.desroches@atmel.com 321f5f17813Sludovic.desroches@atmel.com pm_runtime_disable: 322f5f17813Sludovic.desroches@atmel.com pm_runtime_disable(&pdev->dev); 323f5f17813Sludovic.desroches@atmel.com pm_runtime_set_suspended(&pdev->dev); 3242df9d58fSJisheng Zhang pm_runtime_put_noidle(&pdev->dev); 325bb5f8ea4Sludovic.desroches@atmel.com clocks_disable_unprepare: 326bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->gck); 327bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->mainck); 328bb5f8ea4Sludovic.desroches@atmel.com hclock_disable_unprepare: 329bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->hclock); 330bb5f8ea4Sludovic.desroches@atmel.com sdhci_pltfm_free(pdev); 331bb5f8ea4Sludovic.desroches@atmel.com return ret; 332bb5f8ea4Sludovic.desroches@atmel.com } 333bb5f8ea4Sludovic.desroches@atmel.com 334bb5f8ea4Sludovic.desroches@atmel.com static int sdhci_at91_remove(struct platform_device *pdev) 335bb5f8ea4Sludovic.desroches@atmel.com { 336bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_host *host = platform_get_drvdata(pdev); 337bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 33810f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 33910f1c135SJisheng Zhang struct clk *gck = priv->gck; 34010f1c135SJisheng Zhang struct clk *hclock = priv->hclock; 34110f1c135SJisheng Zhang struct clk *mainck = priv->mainck; 342bb5f8ea4Sludovic.desroches@atmel.com 343f5f17813Sludovic.desroches@atmel.com pm_runtime_get_sync(&pdev->dev); 344f5f17813Sludovic.desroches@atmel.com pm_runtime_disable(&pdev->dev); 345f5f17813Sludovic.desroches@atmel.com pm_runtime_put_noidle(&pdev->dev); 346f5f17813Sludovic.desroches@atmel.com 347bb5f8ea4Sludovic.desroches@atmel.com sdhci_pltfm_unregister(pdev); 348bb5f8ea4Sludovic.desroches@atmel.com 34910f1c135SJisheng Zhang clk_disable_unprepare(gck); 35010f1c135SJisheng Zhang clk_disable_unprepare(hclock); 35110f1c135SJisheng Zhang clk_disable_unprepare(mainck); 352bb5f8ea4Sludovic.desroches@atmel.com 353bb5f8ea4Sludovic.desroches@atmel.com return 0; 354bb5f8ea4Sludovic.desroches@atmel.com } 355bb5f8ea4Sludovic.desroches@atmel.com 356bb5f8ea4Sludovic.desroches@atmel.com static struct platform_driver sdhci_at91_driver = { 357bb5f8ea4Sludovic.desroches@atmel.com .driver = { 358bb5f8ea4Sludovic.desroches@atmel.com .name = "sdhci-at91", 359bb5f8ea4Sludovic.desroches@atmel.com .of_match_table = sdhci_at91_dt_match, 360f5f17813Sludovic.desroches@atmel.com .pm = &sdhci_at91_dev_pm_ops, 361bb5f8ea4Sludovic.desroches@atmel.com }, 362bb5f8ea4Sludovic.desroches@atmel.com .probe = sdhci_at91_probe, 363bb5f8ea4Sludovic.desroches@atmel.com .remove = sdhci_at91_remove, 364bb5f8ea4Sludovic.desroches@atmel.com }; 365bb5f8ea4Sludovic.desroches@atmel.com 366bb5f8ea4Sludovic.desroches@atmel.com module_platform_driver(sdhci_at91_driver); 367bb5f8ea4Sludovic.desroches@atmel.com 368bb5f8ea4Sludovic.desroches@atmel.com MODULE_DESCRIPTION("SDHCI driver for at91"); 369bb5f8ea4Sludovic.desroches@atmel.com MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>"); 370bb5f8ea4Sludovic.desroches@atmel.com MODULE_LICENSE("GPL v2"); 371