19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2bb5f8ea4Sludovic.desroches@atmel.com /* 3bb5f8ea4Sludovic.desroches@atmel.com * Atmel SDMMC controller driver. 4bb5f8ea4Sludovic.desroches@atmel.com * 5bb5f8ea4Sludovic.desroches@atmel.com * Copyright (C) 2015 Atmel, 6bb5f8ea4Sludovic.desroches@atmel.com * 2015 Ludovic Desroches <ludovic.desroches@atmel.com> 7bb5f8ea4Sludovic.desroches@atmel.com */ 8bb5f8ea4Sludovic.desroches@atmel.com 9bb5f8ea4Sludovic.desroches@atmel.com #include <linux/clk.h> 104e289a7dSLudovic Desroches #include <linux/delay.h> 11bb5f8ea4Sludovic.desroches@atmel.com #include <linux/err.h> 12bb5f8ea4Sludovic.desroches@atmel.com #include <linux/io.h> 134406433dSLudovic Desroches #include <linux/kernel.h> 14bb5f8ea4Sludovic.desroches@atmel.com #include <linux/mmc/host.h> 1564e5cd72Sludovic.desroches@atmel.com #include <linux/mmc/slot-gpio.h> 16bb5f8ea4Sludovic.desroches@atmel.com #include <linux/module.h> 17bb5f8ea4Sludovic.desroches@atmel.com #include <linux/of.h> 18bb5f8ea4Sludovic.desroches@atmel.com #include <linux/of_device.h> 19f5f17813Sludovic.desroches@atmel.com #include <linux/pm.h> 20f5f17813Sludovic.desroches@atmel.com #include <linux/pm_runtime.h> 21bb5f8ea4Sludovic.desroches@atmel.com 22bb5f8ea4Sludovic.desroches@atmel.com #include "sdhci-pltfm.h" 23bb5f8ea4Sludovic.desroches@atmel.com 24d0918764SLudovic Desroches #define SDMMC_MC1R 0x204 25d0918764SLudovic Desroches #define SDMMC_MC1R_DDR BIT(3) 267a1e3f14SLudovic Desroches #define SDMMC_MC1R_FCD BIT(7) 27bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR 0x230 28bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR_CAPWREN BIT(0) 29bb5f8ea4Sludovic.desroches@atmel.com #define SDMMC_CACR_KEY (0x46 << 8) 30727d836aSNicolas Ferre #define SDMMC_CALCR 0x240 31727d836aSNicolas Ferre #define SDMMC_CALCR_EN BIT(0) 32727d836aSNicolas Ferre #define SDMMC_CALCR_ALWYSON BIT(4) 33bb5f8ea4Sludovic.desroches@atmel.com 344406433dSLudovic Desroches #define SDHCI_AT91_PRESET_COMMON_CONF 0x400 /* drv type B, programmable clock mode */ 354406433dSLudovic Desroches 363976656dSLudovic Desroches struct sdhci_at91_soc_data { 373976656dSLudovic Desroches const struct sdhci_pltfm_data *pdata; 383976656dSLudovic Desroches bool baseclk_is_generated_internally; 393976656dSLudovic Desroches unsigned int divider_for_baseclk; 403976656dSLudovic Desroches }; 413976656dSLudovic Desroches 42bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_at91_priv { 433976656dSLudovic Desroches const struct sdhci_at91_soc_data *soc_data; 44bb5f8ea4Sludovic.desroches@atmel.com struct clk *hclock; 45bb5f8ea4Sludovic.desroches@atmel.com struct clk *gck; 46bb5f8ea4Sludovic.desroches@atmel.com struct clk *mainck; 47e2b372ebSQuentin Schulz bool restore_needed; 48727d836aSNicolas Ferre bool cal_always_on; 49bb5f8ea4Sludovic.desroches@atmel.com }; 50bb5f8ea4Sludovic.desroches@atmel.com 517a1e3f14SLudovic Desroches static void sdhci_at91_set_force_card_detect(struct sdhci_host *host) 527a1e3f14SLudovic Desroches { 537a1e3f14SLudovic Desroches u8 mc1r; 547a1e3f14SLudovic Desroches 557a1e3f14SLudovic Desroches mc1r = readb(host->ioaddr + SDMMC_MC1R); 567a1e3f14SLudovic Desroches mc1r |= SDMMC_MC1R_FCD; 577a1e3f14SLudovic Desroches writeb(mc1r, host->ioaddr + SDMMC_MC1R); 587a1e3f14SLudovic Desroches } 597a1e3f14SLudovic Desroches 604e289a7dSLudovic Desroches static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock) 614e289a7dSLudovic Desroches { 624e289a7dSLudovic Desroches u16 clk; 634e289a7dSLudovic Desroches unsigned long timeout; 644e289a7dSLudovic Desroches 654e289a7dSLudovic Desroches host->mmc->actual_clock = 0; 664e289a7dSLudovic Desroches 674e289a7dSLudovic Desroches /* 684e289a7dSLudovic Desroches * There is no requirement to disable the internal clock before 694e289a7dSLudovic Desroches * changing the SD clock configuration. Moreover, disabling the 704e289a7dSLudovic Desroches * internal clock, changing the configuration and re-enabling the 714e289a7dSLudovic Desroches * internal clock causes some bugs. It can prevent to get the internal 724e289a7dSLudovic Desroches * clock stable flag ready and an unexpected switch to the base clock 734e289a7dSLudovic Desroches * when using presets. 744e289a7dSLudovic Desroches */ 754e289a7dSLudovic Desroches clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 764e289a7dSLudovic Desroches clk &= SDHCI_CLOCK_INT_EN; 774e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 784e289a7dSLudovic Desroches 794e289a7dSLudovic Desroches if (clock == 0) 804e289a7dSLudovic Desroches return; 814e289a7dSLudovic Desroches 824e289a7dSLudovic Desroches clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 834e289a7dSLudovic Desroches 844e289a7dSLudovic Desroches clk |= SDHCI_CLOCK_INT_EN; 854e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 864e289a7dSLudovic Desroches 874e289a7dSLudovic Desroches /* Wait max 20 ms */ 884e289a7dSLudovic Desroches timeout = 20; 894e289a7dSLudovic Desroches while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) 904e289a7dSLudovic Desroches & SDHCI_CLOCK_INT_STABLE)) { 914e289a7dSLudovic Desroches if (timeout == 0) { 924e289a7dSLudovic Desroches pr_err("%s: Internal clock never stabilised.\n", 934e289a7dSLudovic Desroches mmc_hostname(host->mmc)); 944e289a7dSLudovic Desroches return; 954e289a7dSLudovic Desroches } 964e289a7dSLudovic Desroches timeout--; 974e289a7dSLudovic Desroches mdelay(1); 984e289a7dSLudovic Desroches } 994e289a7dSLudovic Desroches 1004e289a7dSLudovic Desroches clk |= SDHCI_CLOCK_CARD_EN; 1014e289a7dSLudovic Desroches sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 1024e289a7dSLudovic Desroches } 1034e289a7dSLudovic Desroches 104519c51afSColin Ian King static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, 105519c51afSColin Ian King unsigned int timing) 106d0918764SLudovic Desroches { 107d0918764SLudovic Desroches if (timing == MMC_TIMING_MMC_DDR52) 108d0918764SLudovic Desroches sdhci_writeb(host, SDMMC_MC1R_DDR, SDMMC_MC1R); 109d0918764SLudovic Desroches sdhci_set_uhs_signaling(host, timing); 110d0918764SLudovic Desroches } 111d0918764SLudovic Desroches 1127a1e3f14SLudovic Desroches static void sdhci_at91_reset(struct sdhci_host *host, u8 mask) 1137a1e3f14SLudovic Desroches { 114727d836aSNicolas Ferre struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 115727d836aSNicolas Ferre struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 116727d836aSNicolas Ferre 1177a1e3f14SLudovic Desroches sdhci_reset(host, mask); 1187a1e3f14SLudovic Desroches 11953dd0a7cSMichał Mirosław if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) 12053dd0a7cSMichał Mirosław || mmc_gpio_get_cd(host->mmc) >= 0) 1217a1e3f14SLudovic Desroches sdhci_at91_set_force_card_detect(host); 122727d836aSNicolas Ferre 123727d836aSNicolas Ferre if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) 124727d836aSNicolas Ferre sdhci_writel(host, SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN, 125727d836aSNicolas Ferre SDMMC_CALCR); 1267a1e3f14SLudovic Desroches } 1277a1e3f14SLudovic Desroches 128bb5f8ea4Sludovic.desroches@atmel.com static const struct sdhci_ops sdhci_at91_sama5d2_ops = { 1294e289a7dSLudovic Desroches .set_clock = sdhci_at91_set_clock, 130bb5f8ea4Sludovic.desroches@atmel.com .set_bus_width = sdhci_set_bus_width, 1317a1e3f14SLudovic Desroches .reset = sdhci_at91_reset, 132d0918764SLudovic Desroches .set_uhs_signaling = sdhci_at91_set_uhs_signaling, 133*98160562SNicolas Saenz Julienne .set_power = sdhci_set_power_and_bus_voltage, 134bb5f8ea4Sludovic.desroches@atmel.com }; 135bb5f8ea4Sludovic.desroches@atmel.com 1363976656dSLudovic Desroches static const struct sdhci_pltfm_data sdhci_sama5d2_pdata = { 137bb5f8ea4Sludovic.desroches@atmel.com .ops = &sdhci_at91_sama5d2_ops, 138bb5f8ea4Sludovic.desroches@atmel.com }; 139bb5f8ea4Sludovic.desroches@atmel.com 1403976656dSLudovic Desroches static const struct sdhci_at91_soc_data soc_data_sama5d2 = { 1413976656dSLudovic Desroches .pdata = &sdhci_sama5d2_pdata, 1423976656dSLudovic Desroches .baseclk_is_generated_internally = false, 1433976656dSLudovic Desroches }; 1443976656dSLudovic Desroches 1453976656dSLudovic Desroches static const struct sdhci_at91_soc_data soc_data_sam9x60 = { 1463976656dSLudovic Desroches .pdata = &sdhci_sama5d2_pdata, 1473976656dSLudovic Desroches .baseclk_is_generated_internally = true, 1483976656dSLudovic Desroches .divider_for_baseclk = 2, 1493976656dSLudovic Desroches }; 1503976656dSLudovic Desroches 151bb5f8ea4Sludovic.desroches@atmel.com static const struct of_device_id sdhci_at91_dt_match[] = { 152bb5f8ea4Sludovic.desroches@atmel.com { .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 }, 1533976656dSLudovic Desroches { .compatible = "microchip,sam9x60-sdhci", .data = &soc_data_sam9x60 }, 154bb5f8ea4Sludovic.desroches@atmel.com {} 155bb5f8ea4Sludovic.desroches@atmel.com }; 156d9943c68SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, sdhci_at91_dt_match); 157bb5f8ea4Sludovic.desroches@atmel.com 158c8a019e7SQuentin Schulz static int sdhci_at91_set_clks_presets(struct device *dev) 159c8a019e7SQuentin Schulz { 160c8a019e7SQuentin Schulz struct sdhci_host *host = dev_get_drvdata(dev); 161c8a019e7SQuentin Schulz struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 162c8a019e7SQuentin Schulz struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 163c8a019e7SQuentin Schulz unsigned int caps0, caps1; 164c8a019e7SQuentin Schulz unsigned int clk_base, clk_mul; 1653976656dSLudovic Desroches unsigned int gck_rate, clk_base_rate; 166c8a019e7SQuentin Schulz unsigned int preset_div; 167c8a019e7SQuentin Schulz 168c8a019e7SQuentin Schulz clk_prepare_enable(priv->hclock); 169c8a019e7SQuentin Schulz caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES); 170c8a019e7SQuentin Schulz caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1); 1713976656dSLudovic Desroches 1723976656dSLudovic Desroches gck_rate = clk_get_rate(priv->gck); 1733976656dSLudovic Desroches if (priv->soc_data->baseclk_is_generated_internally) 1743976656dSLudovic Desroches clk_base_rate = gck_rate / priv->soc_data->divider_for_baseclk; 1753976656dSLudovic Desroches else 1763976656dSLudovic Desroches clk_base_rate = clk_get_rate(priv->mainck); 1773976656dSLudovic Desroches 1783976656dSLudovic Desroches clk_base = clk_base_rate / 1000000; 1793976656dSLudovic Desroches clk_mul = gck_rate / clk_base_rate - 1; 1803976656dSLudovic Desroches 1813976656dSLudovic Desroches caps0 &= ~SDHCI_CLOCK_V3_BASE_MASK; 1823976656dSLudovic Desroches caps0 |= (clk_base << SDHCI_CLOCK_BASE_SHIFT) & SDHCI_CLOCK_V3_BASE_MASK; 1833976656dSLudovic Desroches caps1 &= ~SDHCI_CLOCK_MUL_MASK; 1843976656dSLudovic Desroches caps1 |= (clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK; 185c8a019e7SQuentin Schulz /* Set capabilities in r/w mode. */ 1863976656dSLudovic Desroches writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR); 1873976656dSLudovic Desroches writel(caps0, host->ioaddr + SDHCI_CAPABILITIES); 188c8a019e7SQuentin Schulz writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1); 189c8a019e7SQuentin Schulz /* Set capabilities in ro mode. */ 190c8a019e7SQuentin Schulz writel(0, host->ioaddr + SDMMC_CACR); 1913976656dSLudovic Desroches 1923976656dSLudovic Desroches dev_info(dev, "update clk mul to %u as gck rate is %u Hz and clk base is %u Hz\n", 1933976656dSLudovic Desroches clk_mul, gck_rate, clk_base_rate); 194c8a019e7SQuentin Schulz 195c8a019e7SQuentin Schulz /* 196c8a019e7SQuentin Schulz * We have to set preset values because it depends on the clk_mul 197c8a019e7SQuentin Schulz * value. Moreover, SDR104 is supported in a degraded mode since the 198c8a019e7SQuentin Schulz * maximum sd clock value is 120 MHz instead of 208 MHz. For that 199c8a019e7SQuentin Schulz * reason, we need to use presets to support SDR104. 200c8a019e7SQuentin Schulz */ 2013976656dSLudovic Desroches preset_div = DIV_ROUND_UP(gck_rate, 24000000) - 1; 202c8a019e7SQuentin Schulz writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 203c8a019e7SQuentin Schulz host->ioaddr + SDHCI_PRESET_FOR_SDR12); 2043976656dSLudovic Desroches preset_div = DIV_ROUND_UP(gck_rate, 50000000) - 1; 205c8a019e7SQuentin Schulz writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 206c8a019e7SQuentin Schulz host->ioaddr + SDHCI_PRESET_FOR_SDR25); 2073976656dSLudovic Desroches preset_div = DIV_ROUND_UP(gck_rate, 100000000) - 1; 208c8a019e7SQuentin Schulz writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 209c8a019e7SQuentin Schulz host->ioaddr + SDHCI_PRESET_FOR_SDR50); 2103976656dSLudovic Desroches preset_div = DIV_ROUND_UP(gck_rate, 120000000) - 1; 211c8a019e7SQuentin Schulz writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 212c8a019e7SQuentin Schulz host->ioaddr + SDHCI_PRESET_FOR_SDR104); 2133976656dSLudovic Desroches preset_div = DIV_ROUND_UP(gck_rate, 50000000) - 1; 214c8a019e7SQuentin Schulz writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, 215c8a019e7SQuentin Schulz host->ioaddr + SDHCI_PRESET_FOR_DDR50); 216c8a019e7SQuentin Schulz 217c8a019e7SQuentin Schulz clk_prepare_enable(priv->mainck); 218c8a019e7SQuentin Schulz clk_prepare_enable(priv->gck); 219c8a019e7SQuentin Schulz 220c8a019e7SQuentin Schulz return 0; 221c8a019e7SQuentin Schulz } 222c8a019e7SQuentin Schulz 223e2b372ebSQuentin Schulz #ifdef CONFIG_PM_SLEEP 224e2b372ebSQuentin Schulz static int sdhci_at91_suspend(struct device *dev) 225e2b372ebSQuentin Schulz { 226e2b372ebSQuentin Schulz struct sdhci_host *host = dev_get_drvdata(dev); 227e2b372ebSQuentin Schulz struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 228e2b372ebSQuentin Schulz struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 229e2b372ebSQuentin Schulz int ret; 230e2b372ebSQuentin Schulz 231e2b372ebSQuentin Schulz ret = pm_runtime_force_suspend(dev); 232e2b372ebSQuentin Schulz 233e2b372ebSQuentin Schulz priv->restore_needed = true; 234e2b372ebSQuentin Schulz 235e2b372ebSQuentin Schulz return ret; 236e2b372ebSQuentin Schulz } 237e2b372ebSQuentin Schulz #endif /* CONFIG_PM_SLEEP */ 238e2b372ebSQuentin Schulz 239f5f17813Sludovic.desroches@atmel.com #ifdef CONFIG_PM 240f5f17813Sludovic.desroches@atmel.com static int sdhci_at91_runtime_suspend(struct device *dev) 241f5f17813Sludovic.desroches@atmel.com { 242f5f17813Sludovic.desroches@atmel.com struct sdhci_host *host = dev_get_drvdata(dev); 243f5f17813Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 24410f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 245f5f17813Sludovic.desroches@atmel.com int ret; 246f5f17813Sludovic.desroches@atmel.com 247f5f17813Sludovic.desroches@atmel.com ret = sdhci_runtime_suspend_host(host); 248f5f17813Sludovic.desroches@atmel.com 249d38dcad4SAdrian Hunter if (host->tuning_mode != SDHCI_TUNING_MODE_3) 250d38dcad4SAdrian Hunter mmc_retune_needed(host->mmc); 251d38dcad4SAdrian Hunter 252f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->gck); 253f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->hclock); 254f5f17813Sludovic.desroches@atmel.com clk_disable_unprepare(priv->mainck); 255f5f17813Sludovic.desroches@atmel.com 256f5f17813Sludovic.desroches@atmel.com return ret; 257f5f17813Sludovic.desroches@atmel.com } 258f5f17813Sludovic.desroches@atmel.com 259f5f17813Sludovic.desroches@atmel.com static int sdhci_at91_runtime_resume(struct device *dev) 260f5f17813Sludovic.desroches@atmel.com { 261f5f17813Sludovic.desroches@atmel.com struct sdhci_host *host = dev_get_drvdata(dev); 262f5f17813Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 26310f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 264f5f17813Sludovic.desroches@atmel.com int ret; 265f5f17813Sludovic.desroches@atmel.com 266e2b372ebSQuentin Schulz if (priv->restore_needed) { 267e2b372ebSQuentin Schulz ret = sdhci_at91_set_clks_presets(dev); 268e2b372ebSQuentin Schulz if (ret) 269e2b372ebSQuentin Schulz return ret; 270e2b372ebSQuentin Schulz 271e2b372ebSQuentin Schulz priv->restore_needed = false; 272e2b372ebSQuentin Schulz goto out; 273e2b372ebSQuentin Schulz } 274e2b372ebSQuentin Schulz 275f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->mainck); 276f5f17813Sludovic.desroches@atmel.com if (ret) { 277f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable mainck\n"); 278f5f17813Sludovic.desroches@atmel.com return ret; 279f5f17813Sludovic.desroches@atmel.com } 280f5f17813Sludovic.desroches@atmel.com 281f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->hclock); 282f5f17813Sludovic.desroches@atmel.com if (ret) { 283f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable hclock\n"); 284f5f17813Sludovic.desroches@atmel.com return ret; 285f5f17813Sludovic.desroches@atmel.com } 286f5f17813Sludovic.desroches@atmel.com 287f5f17813Sludovic.desroches@atmel.com ret = clk_prepare_enable(priv->gck); 288f5f17813Sludovic.desroches@atmel.com if (ret) { 289f5f17813Sludovic.desroches@atmel.com dev_err(dev, "can't enable gck\n"); 290f5f17813Sludovic.desroches@atmel.com return ret; 291f5f17813Sludovic.desroches@atmel.com } 292f5f17813Sludovic.desroches@atmel.com 293e2b372ebSQuentin Schulz out: 294c6303c5dSBaolin Wang return sdhci_runtime_resume_host(host, 0); 295f5f17813Sludovic.desroches@atmel.com } 296f5f17813Sludovic.desroches@atmel.com #endif /* CONFIG_PM */ 297f5f17813Sludovic.desroches@atmel.com 298f5f17813Sludovic.desroches@atmel.com static const struct dev_pm_ops sdhci_at91_dev_pm_ops = { 299e2b372ebSQuentin Schulz SET_SYSTEM_SLEEP_PM_OPS(sdhci_at91_suspend, pm_runtime_force_resume) 300f5f17813Sludovic.desroches@atmel.com SET_RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, 301f5f17813Sludovic.desroches@atmel.com sdhci_at91_runtime_resume, 302f5f17813Sludovic.desroches@atmel.com NULL) 303f5f17813Sludovic.desroches@atmel.com }; 304f5f17813Sludovic.desroches@atmel.com 305bb5f8ea4Sludovic.desroches@atmel.com static int sdhci_at91_probe(struct platform_device *pdev) 306bb5f8ea4Sludovic.desroches@atmel.com { 307bb5f8ea4Sludovic.desroches@atmel.com const struct of_device_id *match; 3083976656dSLudovic Desroches const struct sdhci_at91_soc_data *soc_data; 309bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_host *host; 310bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host; 311bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_at91_priv *priv; 312bb5f8ea4Sludovic.desroches@atmel.com int ret; 313bb5f8ea4Sludovic.desroches@atmel.com 314bb5f8ea4Sludovic.desroches@atmel.com match = of_match_device(sdhci_at91_dt_match, &pdev->dev); 315bb5f8ea4Sludovic.desroches@atmel.com if (!match) 316bb5f8ea4Sludovic.desroches@atmel.com return -EINVAL; 317bb5f8ea4Sludovic.desroches@atmel.com soc_data = match->data; 318bb5f8ea4Sludovic.desroches@atmel.com 3193976656dSLudovic Desroches host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*priv)); 32010f1c135SJisheng Zhang if (IS_ERR(host)) 32110f1c135SJisheng Zhang return PTR_ERR(host); 32210f1c135SJisheng Zhang 32310f1c135SJisheng Zhang pltfm_host = sdhci_priv(host); 32410f1c135SJisheng Zhang priv = sdhci_pltfm_priv(pltfm_host); 3253976656dSLudovic Desroches priv->soc_data = soc_data; 326bb5f8ea4Sludovic.desroches@atmel.com 327bb5f8ea4Sludovic.desroches@atmel.com priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); 328bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->mainck)) { 3293976656dSLudovic Desroches if (soc_data->baseclk_is_generated_internally) { 3303976656dSLudovic Desroches priv->mainck = NULL; 3313976656dSLudovic Desroches } else { 332bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get baseclk\n"); 333a04184ceSMichał Mirosław ret = PTR_ERR(priv->mainck); 334a04184ceSMichał Mirosław goto sdhci_pltfm_free; 335bb5f8ea4Sludovic.desroches@atmel.com } 3363976656dSLudovic Desroches } 337bb5f8ea4Sludovic.desroches@atmel.com 338bb5f8ea4Sludovic.desroches@atmel.com priv->hclock = devm_clk_get(&pdev->dev, "hclock"); 339bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->hclock)) { 340bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get hclock\n"); 341a04184ceSMichał Mirosław ret = PTR_ERR(priv->hclock); 342a04184ceSMichał Mirosław goto sdhci_pltfm_free; 343bb5f8ea4Sludovic.desroches@atmel.com } 344bb5f8ea4Sludovic.desroches@atmel.com 345bb5f8ea4Sludovic.desroches@atmel.com priv->gck = devm_clk_get(&pdev->dev, "multclk"); 346bb5f8ea4Sludovic.desroches@atmel.com if (IS_ERR(priv->gck)) { 347bb5f8ea4Sludovic.desroches@atmel.com dev_err(&pdev->dev, "failed to get multclk\n"); 348a04184ceSMichał Mirosław ret = PTR_ERR(priv->gck); 349a04184ceSMichał Mirosław goto sdhci_pltfm_free; 350bb5f8ea4Sludovic.desroches@atmel.com } 351bb5f8ea4Sludovic.desroches@atmel.com 352c8a019e7SQuentin Schulz ret = sdhci_at91_set_clks_presets(&pdev->dev); 353c8a019e7SQuentin Schulz if (ret) 354c8a019e7SQuentin Schulz goto sdhci_pltfm_free; 355bb5f8ea4Sludovic.desroches@atmel.com 356e2b372ebSQuentin Schulz priv->restore_needed = false; 357e2b372ebSQuentin Schulz 358727d836aSNicolas Ferre /* 359727d836aSNicolas Ferre * if SDCAL pin is wrongly connected, we must enable 360727d836aSNicolas Ferre * the analog calibration cell permanently. 361727d836aSNicolas Ferre */ 362727d836aSNicolas Ferre priv->cal_always_on = 363727d836aSNicolas Ferre device_property_read_bool(&pdev->dev, 364727d836aSNicolas Ferre "microchip,sdcal-inverted"); 365727d836aSNicolas Ferre 366bb5f8ea4Sludovic.desroches@atmel.com ret = mmc_of_parse(host->mmc); 367bb5f8ea4Sludovic.desroches@atmel.com if (ret) 368bb5f8ea4Sludovic.desroches@atmel.com goto clocks_disable_unprepare; 369bb5f8ea4Sludovic.desroches@atmel.com 370bb5f8ea4Sludovic.desroches@atmel.com sdhci_get_of_property(pdev); 371bb5f8ea4Sludovic.desroches@atmel.com 372f5f17813Sludovic.desroches@atmel.com pm_runtime_get_noresume(&pdev->dev); 373f5f17813Sludovic.desroches@atmel.com pm_runtime_set_active(&pdev->dev); 374f5f17813Sludovic.desroches@atmel.com pm_runtime_enable(&pdev->dev); 375f5f17813Sludovic.desroches@atmel.com pm_runtime_set_autosuspend_delay(&pdev->dev, 50); 376f5f17813Sludovic.desroches@atmel.com pm_runtime_use_autosuspend(&pdev->dev); 377f5f17813Sludovic.desroches@atmel.com 3787871aa60SEugen Hristev /* HS200 is broken at this moment */ 379fed23c58SEugen Hristev host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; 3807871aa60SEugen Hristev 381bb5f8ea4Sludovic.desroches@atmel.com ret = sdhci_add_host(host); 382bb5f8ea4Sludovic.desroches@atmel.com if (ret) 383f5f17813Sludovic.desroches@atmel.com goto pm_runtime_disable; 384f5f17813Sludovic.desroches@atmel.com 38564e5cd72Sludovic.desroches@atmel.com /* 38664e5cd72Sludovic.desroches@atmel.com * When calling sdhci_runtime_suspend_host(), the sdhci layer makes 38764e5cd72Sludovic.desroches@atmel.com * the assumption that all the clocks of the controller are disabled. 38864e5cd72Sludovic.desroches@atmel.com * It means we can't get irq from it when it is runtime suspended. 38964e5cd72Sludovic.desroches@atmel.com * For that reason, it is not planned to wake-up on a card detect irq 39064e5cd72Sludovic.desroches@atmel.com * from the controller. 39164e5cd72Sludovic.desroches@atmel.com * If we want to use runtime PM and to be able to wake-up on card 39264e5cd72Sludovic.desroches@atmel.com * insertion, we have to use a GPIO for the card detection or we can 39364e5cd72Sludovic.desroches@atmel.com * use polling. Be aware that using polling will resume/suspend the 39464e5cd72Sludovic.desroches@atmel.com * controller between each attempt. 39564e5cd72Sludovic.desroches@atmel.com * Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries 39664e5cd72Sludovic.desroches@atmel.com * to enable polling via device tree with broken-cd property. 39764e5cd72Sludovic.desroches@atmel.com */ 398860951c5SJaehoon Chung if (mmc_card_is_removable(host->mmc) && 399287980e4SArnd Bergmann mmc_gpio_get_cd(host->mmc) < 0) { 40064e5cd72Sludovic.desroches@atmel.com host->mmc->caps |= MMC_CAP_NEEDS_POLL; 40164e5cd72Sludovic.desroches@atmel.com host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; 40264e5cd72Sludovic.desroches@atmel.com } 40364e5cd72Sludovic.desroches@atmel.com 4047a1e3f14SLudovic Desroches /* 4057a1e3f14SLudovic Desroches * If the device attached to the MMC bus is not removable, it is safer 4067a1e3f14SLudovic Desroches * to set the Force Card Detect bit. People often don't connect the 4077a1e3f14SLudovic Desroches * card detect signal and use this pin for another purpose. If the card 4087a1e3f14SLudovic Desroches * detect pin is not muxed to SDHCI controller, a default value is 4097a1e3f14SLudovic Desroches * used. This value can be different from a SoC revision to another 4107a1e3f14SLudovic Desroches * one. Problems come when this default value is not card present. To 4117a1e3f14SLudovic Desroches * avoid this case, if the device is non removable then the card 4127a1e3f14SLudovic Desroches * detection procedure using the SDMCC_CD signal is bypassed. 4137a1e3f14SLudovic Desroches * This bit is reset when a software reset for all command is performed 4147a1e3f14SLudovic Desroches * so we need to implement our own reset function to set back this bit. 41553dd0a7cSMichał Mirosław * 41653dd0a7cSMichał Mirosław * WA: SAMA5D2 doesn't drive CMD if using CD GPIO line. 4177a1e3f14SLudovic Desroches */ 41853dd0a7cSMichał Mirosław if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) 41953dd0a7cSMichał Mirosław || mmc_gpio_get_cd(host->mmc) >= 0) 4207a1e3f14SLudovic Desroches sdhci_at91_set_force_card_detect(host); 4217a1e3f14SLudovic Desroches 422f5f17813Sludovic.desroches@atmel.com pm_runtime_put_autosuspend(&pdev->dev); 423bb5f8ea4Sludovic.desroches@atmel.com 424bb5f8ea4Sludovic.desroches@atmel.com return 0; 425bb5f8ea4Sludovic.desroches@atmel.com 426f5f17813Sludovic.desroches@atmel.com pm_runtime_disable: 427f5f17813Sludovic.desroches@atmel.com pm_runtime_disable(&pdev->dev); 428f5f17813Sludovic.desroches@atmel.com pm_runtime_set_suspended(&pdev->dev); 4292df9d58fSJisheng Zhang pm_runtime_put_noidle(&pdev->dev); 430bb5f8ea4Sludovic.desroches@atmel.com clocks_disable_unprepare: 431bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->gck); 432bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->mainck); 433bb5f8ea4Sludovic.desroches@atmel.com clk_disable_unprepare(priv->hclock); 434c8a019e7SQuentin Schulz sdhci_pltfm_free: 435bb5f8ea4Sludovic.desroches@atmel.com sdhci_pltfm_free(pdev); 436bb5f8ea4Sludovic.desroches@atmel.com return ret; 437bb5f8ea4Sludovic.desroches@atmel.com } 438bb5f8ea4Sludovic.desroches@atmel.com 439bb5f8ea4Sludovic.desroches@atmel.com static int sdhci_at91_remove(struct platform_device *pdev) 440bb5f8ea4Sludovic.desroches@atmel.com { 441bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_host *host = platform_get_drvdata(pdev); 442bb5f8ea4Sludovic.desroches@atmel.com struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 44310f1c135SJisheng Zhang struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); 44410f1c135SJisheng Zhang struct clk *gck = priv->gck; 44510f1c135SJisheng Zhang struct clk *hclock = priv->hclock; 44610f1c135SJisheng Zhang struct clk *mainck = priv->mainck; 447bb5f8ea4Sludovic.desroches@atmel.com 448f5f17813Sludovic.desroches@atmel.com pm_runtime_get_sync(&pdev->dev); 449f5f17813Sludovic.desroches@atmel.com pm_runtime_disable(&pdev->dev); 450f5f17813Sludovic.desroches@atmel.com pm_runtime_put_noidle(&pdev->dev); 451f5f17813Sludovic.desroches@atmel.com 452bb5f8ea4Sludovic.desroches@atmel.com sdhci_pltfm_unregister(pdev); 453bb5f8ea4Sludovic.desroches@atmel.com 45410f1c135SJisheng Zhang clk_disable_unprepare(gck); 45510f1c135SJisheng Zhang clk_disable_unprepare(hclock); 45610f1c135SJisheng Zhang clk_disable_unprepare(mainck); 457bb5f8ea4Sludovic.desroches@atmel.com 458bb5f8ea4Sludovic.desroches@atmel.com return 0; 459bb5f8ea4Sludovic.desroches@atmel.com } 460bb5f8ea4Sludovic.desroches@atmel.com 461bb5f8ea4Sludovic.desroches@atmel.com static struct platform_driver sdhci_at91_driver = { 462bb5f8ea4Sludovic.desroches@atmel.com .driver = { 463bb5f8ea4Sludovic.desroches@atmel.com .name = "sdhci-at91", 464bb5f8ea4Sludovic.desroches@atmel.com .of_match_table = sdhci_at91_dt_match, 465f5f17813Sludovic.desroches@atmel.com .pm = &sdhci_at91_dev_pm_ops, 466bb5f8ea4Sludovic.desroches@atmel.com }, 467bb5f8ea4Sludovic.desroches@atmel.com .probe = sdhci_at91_probe, 468bb5f8ea4Sludovic.desroches@atmel.com .remove = sdhci_at91_remove, 469bb5f8ea4Sludovic.desroches@atmel.com }; 470bb5f8ea4Sludovic.desroches@atmel.com 471bb5f8ea4Sludovic.desroches@atmel.com module_platform_driver(sdhci_at91_driver); 472bb5f8ea4Sludovic.desroches@atmel.com 473bb5f8ea4Sludovic.desroches@atmel.com MODULE_DESCRIPTION("SDHCI driver for at91"); 474bb5f8ea4Sludovic.desroches@atmel.com MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>"); 475bb5f8ea4Sludovic.desroches@atmel.com MODULE_LICENSE("GPL v2"); 476