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 32d0918764SLudovic Desroches #define SDMMC_MC1R 0x204 33d0918764SLudovic Desroches #define SDMMC_MC1R_DDR BIT(3) 34bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR 0x230 35bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR_CAPWREN BIT(0) 36bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR_KEY (0x46 << 8) 37bb5f8ea4Sludovic.desroches@atmel.com 384406433dSLudovic Desroches #define SDHCI_AT91_PRESET_COMMON_CONF 0x400 /* drv type B, programmable clock mode */ 394406433dSLudovic Desroches 40bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_at91_priv { 41bb5f8ea4Sludovic.desroches@atmel.com struct clk *hclock; 42bb5f8ea4Sludovic.desroches@atmel.com struct clk *gck; 43bb5f8ea4Sludovic.desroches@atmel.com struct clk *mainck; 44bb5f8ea4Sludovic.desroches@atmel.com }; 45bb5f8ea4Sludovic.desroches@atmel.com 464e289a7dSLudovic Desroches static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock) 474e289a7dSLudovic Desroches { 484e289a7dSLudovic Desroches u16 clk; 494e289a7dSLudovic Desroches unsigned long timeout; 504e289a7dSLudovic Desroches 514e289a7dSLudovic Desroches host->mmc->actual_clock = 0; 524e289a7dSLudovic Desroches 534e289a7dSLudovic Desroches /* 544e289a7dSLudovic Desroches * There is no requirement to disable the internal clock before 554e289a7dSLudovic Desroches * changing the SD clock configuration. Moreover, disabling the 564e289a7dSLudovic Desroches * internal clock, changing the configuration and re-enabling the 574e289a7dSLudovic Desroches * internal clock causes some bugs. It can prevent to get the internal 584e289a7dSLudovic Desroches * clock stable flag ready and an unexpected switch to the base clock 594e289a7dSLudovic Desroches * when using presets. 604e289a7dSLudovic Desroches */ 614e289a7dSLudovic Desroches clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 624e289a7dSLudovic Desroches clk &= SDHCI_CLOCK_INT_EN; 634e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 644e289a7dSLudovic Desroches 654e289a7dSLudovic Desroches if (clock == 0) 664e289a7dSLudovic Desroches return; 674e289a7dSLudovic Desroches 684e289a7dSLudovic Desroches clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 694e289a7dSLudovic Desroches 704e289a7dSLudovic Desroches clk |= SDHCI_CLOCK_INT_EN; 714e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 724e289a7dSLudovic Desroches 734e289a7dSLudovic Desroches /* Wait max 20 ms */ 744e289a7dSLudovic Desroches timeout = 20; 754e289a7dSLudovic Desroches while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) 764e289a7dSLudovic Desroches & SDHCI_CLOCK_INT_STABLE)) { 774e289a7dSLudovic Desroches if (timeout == 0) { 784e289a7dSLudovic Desroches pr_err("%s: Internal clock never stabilised.\n", 794e289a7dSLudovic Desroches mmc_hostname(host->mmc)); 804e289a7dSLudovic Desroches return; 814e289a7dSLudovic Desroches } 824e289a7dSLudovic Desroches timeout--; 834e289a7dSLudovic Desroches mdelay(1); 844e289a7dSLudovic Desroches } 854e289a7dSLudovic Desroches 864e289a7dSLudovic Desroches clk |= SDHCI_CLOCK_CARD_EN; 874e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 884e289a7dSLudovic Desroches } 894e289a7dSLudovic Desroches 902ce0c7b6SRomain Izard /* 912ce0c7b6SRomain Izard * In this specific implementation of the SDHCI controller, the power register 922ce0c7b6SRomain Izard * needs to have a valid voltage set even when the power supply is managed by 932ce0c7b6SRomain Izard * an external regulator. 942ce0c7b6SRomain Izard */ 952ce0c7b6SRomain Izard static void sdhci_at91_set_power(struct sdhci_host *host, unsigned char mode, 962ce0c7b6SRomain Izard unsigned short vdd) 972ce0c7b6SRomain Izard { 982ce0c7b6SRomain Izard if (!IS_ERR(host->mmc->supply.vmmc)) { 992ce0c7b6SRomain Izard struct mmc_host *mmc = host->mmc; 1002ce0c7b6SRomain Izard 1012ce0c7b6SRomain Izard spin_unlock_irq(&host->lock); 1022ce0c7b6SRomain Izard mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); 1032ce0c7b6SRomain Izard spin_lock_irq(&host->lock); 1042ce0c7b6SRomain Izard } 1052ce0c7b6SRomain Izard sdhci_set_power_noreg(host, mode, vdd); 1062ce0c7b6SRomain Izard } 1072ce0c7b6SRomain Izard 108d0918764SLudovic Desroches void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) 109d0918764SLudovic Desroches { 110d0918764SLudovic Desroches if (timing == MMC_TIMING_MMC_DDR52) 111d0918764SLudovic Desroches sdhci_writeb(host, SDMMC_MC1R_DDR, SDMMC_MC1R); 112d0918764SLudovic Desroches sdhci_set_uhs_signaling(host, timing); 113d0918764SLudovic Desroches } 114d0918764SLudovic Desroches 115bb5f8ea4Sludovic.desroches@atmel.com static const struct sdhci_ops sdhci_at91_sama5d2_ops = { 1164e289a7dSLudovic Desroches .set_clock = sdhci_at91_set_clock, 117bb5f8ea4Sludovic.desroches@atmel.com .set_bus_width = sdhci_set_bus_width, 118bb5f8ea4Sludovic.desroches@atmel.com .reset = sdhci_reset, 119d0918764SLudovic Desroches .set_uhs_signaling = sdhci_at91_set_uhs_signaling, 1202ce0c7b6SRomain Izard .set_power = sdhci_at91_set_power, 121bb5f8ea4Sludovic.desroches@atmel.com }; 122bb5f8ea4Sludovic.desroches@atmel.com 123bb5f8ea4Sludovic.desroches@atmel.com static const struct sdhci_pltfm_data soc_data_sama5d2 = { 124bb5f8ea4Sludovic.desroches@atmel.com .ops = &sdhci_at91_sama5d2_ops, 125bb5f8ea4Sludovic.desroches@atmel.com }; 126bb5f8ea4Sludovic.desroches@atmel.com 127bb5f8ea4Sludovic.desroches@atmel.com static const struct of_device_id sdhci_at91_dt_match[] = { 128bb5f8ea4Sludovic.desroches@atmel.com { .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 }, 129bb5f8ea4Sludovic.desroches@atmel.com {} 130bb5f8ea4Sludovic.desroches@atmel.com }; 131d9943c68SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, sdhci_at91_dt_match); 132bb5f8ea4Sludovic.desroches@atmel.com 133f5f17813Sludovic.desroches@atmel.com #ifdef CONFIG_PM 134f5f17813Sludovic.desroches@atmel.com static int sdhci_at91_runtime_suspend(struct device *dev) 135f5f17813Sludovic.desroches@atmel.com { 136f5f17813Sludovic.desroches@atmel.com struct sdhci_host *host = dev_get_drvdata(dev); 137f5f17813Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 13810f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 139f5f17813Sludovic.desroches@atmel.com int ret; 140f5f17813Sludovic.desroches@atmel.com 141f5f17813Sludovic.desroches@atmel.com ret = sdhci_runtime_suspend_host(host); 142f5f17813Sludovic.desroches@atmel.com 143d38dcad4SAdrian Hunter if (host->tuning_mode != SDHCI_TUNING_MODE_3) 144d38dcad4SAdrian Hunter mmc_retune_needed(host->mmc); 145d38dcad4SAdrian Hunter 146f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->gck); 147f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->hclock); 148f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->mainck); 149f5f17813Sludovic.desroches@atmel.com 150f5f17813Sludovic.desroches@atmel.com return ret; 151f5f17813Sludovic.desroches@atmel.com } 152f5f17813Sludovic.desroches@atmel.com 153f5f17813Sludovic.desroches@atmel.com static int sdhci_at91_runtime_resume(struct device *dev) 154f5f17813Sludovic.desroches@atmel.com { 155f5f17813Sludovic.desroches@atmel.com struct sdhci_host *host = dev_get_drvdata(dev); 156f5f17813Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 15710f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 158f5f17813Sludovic.desroches@atmel.com int ret; 159f5f17813Sludovic.desroches@atmel.com 160f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->mainck); 161f5f17813Sludovic.desroches@atmel.com if (ret) { 162f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable mainck\n"); 163f5f17813Sludovic.desroches@atmel.com return ret; 164f5f17813Sludovic.desroches@atmel.com } 165f5f17813Sludovic.desroches@atmel.com 166f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->hclock); 167f5f17813Sludovic.desroches@atmel.com if (ret) { 168f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable hclock\n"); 169f5f17813Sludovic.desroches@atmel.com return ret; 170f5f17813Sludovic.desroches@atmel.com } 171f5f17813Sludovic.desroches@atmel.com 172f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->gck); 173f5f17813Sludovic.desroches@atmel.com if (ret) { 174f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable gck\n"); 175f5f17813Sludovic.desroches@atmel.com return ret; 176f5f17813Sludovic.desroches@atmel.com } 177f5f17813Sludovic.desroches@atmel.com 178f5f17813Sludovic.desroches@atmel.com return sdhci_runtime_resume_host(host); 179f5f17813Sludovic.desroches@atmel.com } 180f5f17813Sludovic.desroches@atmel.com #endif /* CONFIG_PM */ 181f5f17813Sludovic.desroches@atmel.com 182f5f17813Sludovic.desroches@atmel.com static const struct dev_pm_ops sdhci_at91_dev_pm_ops = { 183f5f17813Sludovic.desroches@atmel.com SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 184f5f17813Sludovic.desroches@atmel.com pm_runtime_force_resume) 185f5f17813Sludovic.desroches@atmel.com SET_RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, 186f5f17813Sludovic.desroches@atmel.com sdhci_at91_runtime_resume, 187f5f17813Sludovic.desroches@atmel.com NULL) 188f5f17813Sludovic.desroches@atmel.com }; 189f5f17813Sludovic.desroches@atmel.com 190bb5f8ea4Sludovic.desroches@atmel.com static int sdhci_at91_probe(struct platform_device *pdev) 191bb5f8ea4Sludovic.desroches@atmel.com { 192bb5f8ea4Sludovic.desroches@atmel.com const struct of_device_id *match; 193bb5f8ea4Sludovic.desroches@atmel.com const struct sdhci_pltfm_data *soc_data; 194bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_host *host; 195bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host; 196bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_at91_priv *priv; 197bb5f8ea4Sludovic.desroches@atmel.com unsigned int caps0, caps1; 198bb5f8ea4Sludovic.desroches@atmel.com unsigned int clk_base, clk_mul; 199bb5f8ea4Sludovic.desroches@atmel.com unsigned int gck_rate, real_gck_rate; 200bb5f8ea4Sludovic.desroches@atmel.com int ret; 2014406433dSLudovic Desroches unsigned int preset_div; 202bb5f8ea4Sludovic.desroches@atmel.com 203bb5f8ea4Sludovic.desroches@atmel.com match = of_match_device(sdhci_at91_dt_match, &pdev->dev); 204bb5f8ea4Sludovic.desroches@atmel.com if (!match) 205bb5f8ea4Sludovic.desroches@atmel.com return -EINVAL; 206bb5f8ea4Sludovic.desroches@atmel.com soc_data = match->data; 207bb5f8ea4Sludovic.desroches@atmel.com 20810f1c135SJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data, sizeof(*priv)); 20910f1c135SJisheng Zhang if (IS_ERR(host)) 21010f1c135SJisheng Zhang return PTR_ERR(host); 21110f1c135SJisheng Zhang 21210f1c135SJisheng Zhang pltfm_host = sdhci_priv(host); 21310f1c135SJisheng Zhang priv = sdhci_pltfm_priv(pltfm_host); 214bb5f8ea4Sludovic.desroches@atmel.com 215bb5f8ea4Sludovic.desroches@atmel.com priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); 216bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->mainck)) { 217bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get baseclk\n"); 218bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->mainck); 219bb5f8ea4Sludovic.desroches@atmel.com } 220bb5f8ea4Sludovic.desroches@atmel.com 221bb5f8ea4Sludovic.desroches@atmel.com priv->hclock = devm_clk_get(&pdev->dev, "hclock"); 222bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->hclock)) { 223bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get hclock\n"); 224bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->hclock); 225bb5f8ea4Sludovic.desroches@atmel.com } 226bb5f8ea4Sludovic.desroches@atmel.com 227bb5f8ea4Sludovic.desroches@atmel.com priv->gck = devm_clk_get(&pdev->dev, "multclk"); 228bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->gck)) { 229bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get multclk\n"); 230bb5f8ea4Sludovic.desroches@atmel.com return PTR_ERR(priv->gck); 231bb5f8ea4Sludovic.desroches@atmel.com } 232bb5f8ea4Sludovic.desroches@atmel.com 233bb5f8ea4Sludovic.desroches@atmel.com /* 234bb5f8ea4Sludovic.desroches@atmel.com * The mult clock is provided by as a generated clock by the PMC 235bb5f8ea4Sludovic.desroches@atmel.com * controller. In order to set the rate of gck, we have to get the 236bb5f8ea4Sludovic.desroches@atmel.com * base clock rate and the clock mult from capabilities. 237bb5f8ea4Sludovic.desroches@atmel.com */ 238bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->hclock); 239bb5f8ea4Sludovic.desroches@atmel.com caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES); 240bb5f8ea4Sludovic.desroches@atmel.com caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1); 241bb5f8ea4Sludovic.desroches@atmel.com clk_base = (caps0 & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; 242bb5f8ea4Sludovic.desroches@atmel.com clk_mul = (caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; 243bb5f8ea4Sludovic.desroches@atmel.com gck_rate = clk_base * 1000000 * (clk_mul + 1); 244bb5f8ea4Sludovic.desroches@atmel.com ret = clk_set_rate(priv->gck, gck_rate); 245bb5f8ea4Sludovic.desroches@atmel.com if (ret < 0) { 246bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to set gck"); 247bb5f8ea4Sludovic.desroches@atmel.com goto hclock_disable_unprepare; 248bb5f8ea4Sludovic.desroches@atmel.com } 249bb5f8ea4Sludovic.desroches@atmel.com /* 250bb5f8ea4Sludovic.desroches@atmel.com * We need to check if we have the requested rate for gck because in 251bb5f8ea4Sludovic.desroches@atmel.com * some cases this rate could be not supported. If it happens, the rate 252bb5f8ea4Sludovic.desroches@atmel.com * is the closest one gck can provide. We have to update the value 253bb5f8ea4Sludovic.desroches@atmel.com * of clk mul. 254bb5f8ea4Sludovic.desroches@atmel.com */ 255bb5f8ea4Sludovic.desroches@atmel.com real_gck_rate = clk_get_rate(priv->gck); 256bb5f8ea4Sludovic.desroches@atmel.com if (real_gck_rate != gck_rate) { 257bb5f8ea4Sludovic.desroches@atmel.com clk_mul = real_gck_rate / (clk_base * 1000000) - 1; 258bb5f8ea4Sludovic.desroches@atmel.com caps1 &= (~SDHCI_CLOCK_MUL_MASK); 259bb5f8ea4Sludovic.desroches@atmel.com caps1 |= ((clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK); 260bb5f8ea4Sludovic.desroches@atmel.com /* Set capabilities in r/w mode. */ 261bb5f8ea4Sludovic.desroches@atmel.com writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR); 262bb5f8ea4Sludovic.desroches@atmel.com writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1); 263bb5f8ea4Sludovic.desroches@atmel.com /* Set capabilities in ro mode. */ 264bb5f8ea4Sludovic.desroches@atmel.com writel(0, host->ioaddr + SDMMC_CACR); 265bb5f8ea4Sludovic.desroches@atmel.com dev_info(&pdev->dev, "update clk mul to %u as gck rate is %u Hz\n", 266bb5f8ea4Sludovic.desroches@atmel.com clk_mul, real_gck_rate); 267bb5f8ea4Sludovic.desroches@atmel.com } 268bb5f8ea4Sludovic.desroches@atmel.com 2694406433dSLudovic Desroches /* 2704406433dSLudovic Desroches * We have to set preset values because it depends on the clk_mul 2714406433dSLudovic Desroches * value. Moreover, SDR104 is supported in a degraded mode since the 2724406433dSLudovic Desroches * maximum sd clock value is 120 MHz instead of 208 MHz. For that 2734406433dSLudovic Desroches * reason, we need to use presets to support SDR104. 2744406433dSLudovic Desroches */ 2754406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 24000000) - 1; 2764406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2774406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR12); 2784406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1; 2794406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2804406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR25); 2814406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 100000000) - 1; 2824406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2834406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR50); 2844406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 120000000) - 1; 2854406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2864406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_SDR104); 2874406433dSLudovic Desroches preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1; 2884406433dSLudovic Desroches writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 2894406433dSLudovic Desroches host->ioaddr + SDHCI_PRESET_FOR_DDR50); 2904406433dSLudovic Desroches 291bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->mainck); 292bb5f8ea4Sludovic.desroches@atmel.com clk_prepare_enable(priv->gck); 293bb5f8ea4Sludovic.desroches@atmel.com 294bb5f8ea4Sludovic.desroches@atmel.com ret = mmc_of_parse(host->mmc); 295bb5f8ea4Sludovic.desroches@atmel.com if (ret) 296bb5f8ea4Sludovic.desroches@atmel.com goto clocks_disable_unprepare; 297bb5f8ea4Sludovic.desroches@atmel.com 298bb5f8ea4Sludovic.desroches@atmel.com sdhci_get_of_property(pdev); 299bb5f8ea4Sludovic.desroches@atmel.com 300f5f17813Sludovic.desroches@atmel.com pm_runtime_get_noresume(&pdev->dev); 301f5f17813Sludovic.desroches@atmel.com pm_runtime_set_active(&pdev->dev); 302f5f17813Sludovic.desroches@atmel.com pm_runtime_enable(&pdev->dev); 303f5f17813Sludovic.desroches@atmel.com pm_runtime_set_autosuspend_delay(&pdev->dev, 50); 304f5f17813Sludovic.desroches@atmel.com pm_runtime_use_autosuspend(&pdev->dev); 305f5f17813Sludovic.desroches@atmel.com 306bb5f8ea4Sludovic.desroches@atmel.com ret = sdhci_add_host(host); 307bb5f8ea4Sludovic.desroches@atmel.com if (ret) 308f5f17813Sludovic.desroches@atmel.com goto pm_runtime_disable; 309f5f17813Sludovic.desroches@atmel.com 31064e5cd72Sludovic.desroches@atmel.com /* 31164e5cd72Sludovic.desroches@atmel.com * When calling sdhci_runtime_suspend_host(), the sdhci layer makes 31264e5cd72Sludovic.desroches@atmel.com * the assumption that all the clocks of the controller are disabled. 31364e5cd72Sludovic.desroches@atmel.com * It means we can't get irq from it when it is runtime suspended. 31464e5cd72Sludovic.desroches@atmel.com * For that reason, it is not planned to wake-up on a card detect irq 31564e5cd72Sludovic.desroches@atmel.com * from the controller. 31664e5cd72Sludovic.desroches@atmel.com * If we want to use runtime PM and to be able to wake-up on card 31764e5cd72Sludovic.desroches@atmel.com * insertion, we have to use a GPIO for the card detection or we can 31864e5cd72Sludovic.desroches@atmel.com * use polling. Be aware that using polling will resume/suspend the 31964e5cd72Sludovic.desroches@atmel.com * controller between each attempt. 32064e5cd72Sludovic.desroches@atmel.com * Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries 32164e5cd72Sludovic.desroches@atmel.com * to enable polling via device tree with broken-cd property. 32264e5cd72Sludovic.desroches@atmel.com */ 323860951c5SJaehoon Chung if (mmc_card_is_removable(host->mmc) && 324287980e4SArnd Bergmann mmc_gpio_get_cd(host->mmc) < 0) { 32564e5cd72Sludovic.desroches@atmel.com host->mmc->caps |= MMC_CAP_NEEDS_POLL; 32664e5cd72Sludovic.desroches@atmel.com host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; 32764e5cd72Sludovic.desroches@atmel.com } 32864e5cd72Sludovic.desroches@atmel.com 329f5f17813Sludovic.desroches@atmel.com pm_runtime_put_autosuspend(&pdev->dev); 330bb5f8ea4Sludovic.desroches@atmel.com 331bb5f8ea4Sludovic.desroches@atmel.com return 0; 332bb5f8ea4Sludovic.desroches@atmel.com 333f5f17813Sludovic.desroches@atmel.com pm_runtime_disable: 334f5f17813Sludovic.desroches@atmel.com pm_runtime_disable(&pdev->dev); 335f5f17813Sludovic.desroches@atmel.com pm_runtime_set_suspended(&pdev->dev); 3362df9d58fSJisheng Zhang pm_runtime_put_noidle(&pdev->dev); 337bb5f8ea4Sludovic.desroches@atmel.com clocks_disable_unprepare: 338bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->gck); 339bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->mainck); 340bb5f8ea4Sludovic.desroches@atmel.com hclock_disable_unprepare: 341bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->hclock); 342bb5f8ea4Sludovic.desroches@atmel.com sdhci_pltfm_free(pdev); 343bb5f8ea4Sludovic.desroches@atmel.com return ret; 344bb5f8ea4Sludovic.desroches@atmel.com } 345bb5f8ea4Sludovic.desroches@atmel.com 346bb5f8ea4Sludovic.desroches@atmel.com static int sdhci_at91_remove(struct platform_device *pdev) 347bb5f8ea4Sludovic.desroches@atmel.com { 348bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_host *host = platform_get_drvdata(pdev); 349bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 35010f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 35110f1c135SJisheng Zhang struct clk *gck = priv->gck; 35210f1c135SJisheng Zhang struct clk *hclock = priv->hclock; 35310f1c135SJisheng Zhang struct clk *mainck = priv->mainck; 354bb5f8ea4Sludovic.desroches@atmel.com 355f5f17813Sludovic.desroches@atmel.com pm_runtime_get_sync(&pdev->dev); 356f5f17813Sludovic.desroches@atmel.com pm_runtime_disable(&pdev->dev); 357f5f17813Sludovic.desroches@atmel.com pm_runtime_put_noidle(&pdev->dev); 358f5f17813Sludovic.desroches@atmel.com 359bb5f8ea4Sludovic.desroches@atmel.com sdhci_pltfm_unregister(pdev); 360bb5f8ea4Sludovic.desroches@atmel.com 36110f1c135SJisheng Zhang clk_disable_unprepare(gck); 36210f1c135SJisheng Zhang clk_disable_unprepare(hclock); 36310f1c135SJisheng Zhang clk_disable_unprepare(mainck); 364bb5f8ea4Sludovic.desroches@atmel.com 365bb5f8ea4Sludovic.desroches@atmel.com return 0; 366bb5f8ea4Sludovic.desroches@atmel.com } 367bb5f8ea4Sludovic.desroches@atmel.com 368bb5f8ea4Sludovic.desroches@atmel.com static struct platform_driver sdhci_at91_driver = { 369bb5f8ea4Sludovic.desroches@atmel.com .driver = { 370bb5f8ea4Sludovic.desroches@atmel.com .name = "sdhci-at91", 371bb5f8ea4Sludovic.desroches@atmel.com .of_match_table = sdhci_at91_dt_match, 372f5f17813Sludovic.desroches@atmel.com .pm = &sdhci_at91_dev_pm_ops, 373bb5f8ea4Sludovic.desroches@atmel.com }, 374bb5f8ea4Sludovic.desroches@atmel.com .probe = sdhci_at91_probe, 375bb5f8ea4Sludovic.desroches@atmel.com .remove = sdhci_at91_remove, 376bb5f8ea4Sludovic.desroches@atmel.com }; 377bb5f8ea4Sludovic.desroches@atmel.com 378bb5f8ea4Sludovic.desroches@atmel.com module_platform_driver(sdhci_at91_driver); 379bb5f8ea4Sludovic.desroches@atmel.com 380bb5f8ea4Sludovic.desroches@atmel.com MODULE_DESCRIPTION("SDHCI driver for at91"); 381bb5f8ea4Sludovic.desroches@atmel.com MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>"); 382bb5f8ea4Sludovic.desroches@atmel.com MODULE_LICENSE("GPL v2"); 383