1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for Synopsys DesignWare Cores Mobile Storage Host Controller 4 * 5 * Copyright (C) 2018 Synaptics Incorporated 6 * 7 * Author: Jisheng Zhang <jszhang@kernel.org> 8 */ 9 10 #include <linux/clk.h> 11 #include <linux/dma-mapping.h> 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/sizes.h> 16 17 #include "sdhci-pltfm.h" 18 19 #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) 20 21 /* DWCMSHC specific Mode Select value */ 22 #define DWCMSHC_CTRL_HS400 0x7 23 24 #define BOUNDARY_OK(addr, len) \ 25 ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) 26 27 struct dwcmshc_priv { 28 struct clk *bus_clk; 29 }; 30 31 /* 32 * If DMA addr spans 128MB boundary, we split the DMA transfer into two 33 * so that each DMA transfer doesn't exceed the boundary. 34 */ 35 static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, 36 dma_addr_t addr, int len, unsigned int cmd) 37 { 38 int tmplen, offset; 39 40 if (likely(!len || BOUNDARY_OK(addr, len))) { 41 sdhci_adma_write_desc(host, desc, addr, len, cmd); 42 return; 43 } 44 45 offset = addr & (SZ_128M - 1); 46 tmplen = SZ_128M - offset; 47 sdhci_adma_write_desc(host, desc, addr, tmplen, cmd); 48 49 addr += tmplen; 50 len -= tmplen; 51 sdhci_adma_write_desc(host, desc, addr, len, cmd); 52 } 53 54 static void dwcmshc_check_auto_cmd23(struct mmc_host *mmc, 55 struct mmc_request *mrq) 56 { 57 struct sdhci_host *host = mmc_priv(mmc); 58 59 /* 60 * No matter V4 is enabled or not, ARGUMENT2 register is 32-bit 61 * block count register which doesn't support stuff bits of 62 * CMD23 argument on dwcmsch host controller. 63 */ 64 if (mrq->sbc && (mrq->sbc->arg & SDHCI_DWCMSHC_ARG2_STUFF)) 65 host->flags &= ~SDHCI_AUTO_CMD23; 66 else 67 host->flags |= SDHCI_AUTO_CMD23; 68 } 69 70 static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq) 71 { 72 dwcmshc_check_auto_cmd23(mmc, mrq); 73 74 sdhci_request(mmc, mrq); 75 } 76 77 static void dwcmshc_set_uhs_signaling(struct sdhci_host *host, 78 unsigned int timing) 79 { 80 u16 ctrl_2; 81 82 ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 83 /* Select Bus Speed Mode for host */ 84 ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 85 if ((timing == MMC_TIMING_MMC_HS200) || 86 (timing == MMC_TIMING_UHS_SDR104)) 87 ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 88 else if (timing == MMC_TIMING_UHS_SDR12) 89 ctrl_2 |= SDHCI_CTRL_UHS_SDR12; 90 else if ((timing == MMC_TIMING_UHS_SDR25) || 91 (timing == MMC_TIMING_MMC_HS)) 92 ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 93 else if (timing == MMC_TIMING_UHS_SDR50) 94 ctrl_2 |= SDHCI_CTRL_UHS_SDR50; 95 else if ((timing == MMC_TIMING_UHS_DDR50) || 96 (timing == MMC_TIMING_MMC_DDR52)) 97 ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 98 else if (timing == MMC_TIMING_MMC_HS400) 99 ctrl_2 |= DWCMSHC_CTRL_HS400; 100 sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 101 } 102 103 static const struct sdhci_ops sdhci_dwcmshc_ops = { 104 .set_clock = sdhci_set_clock, 105 .set_bus_width = sdhci_set_bus_width, 106 .set_uhs_signaling = dwcmshc_set_uhs_signaling, 107 .get_max_clock = sdhci_pltfm_clk_get_max_clock, 108 .reset = sdhci_reset, 109 .adma_write_desc = dwcmshc_adma_write_desc, 110 }; 111 112 static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { 113 .ops = &sdhci_dwcmshc_ops, 114 .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 115 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 116 }; 117 118 static int dwcmshc_probe(struct platform_device *pdev) 119 { 120 struct sdhci_pltfm_host *pltfm_host; 121 struct sdhci_host *host; 122 struct dwcmshc_priv *priv; 123 int err; 124 u32 extra; 125 126 host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata, 127 sizeof(struct dwcmshc_priv)); 128 if (IS_ERR(host)) 129 return PTR_ERR(host); 130 131 /* 132 * extra adma table cnt for cross 128M boundary handling. 133 */ 134 extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M); 135 if (extra > SDHCI_MAX_SEGS) 136 extra = SDHCI_MAX_SEGS; 137 host->adma_table_cnt += extra; 138 139 pltfm_host = sdhci_priv(host); 140 priv = sdhci_pltfm_priv(pltfm_host); 141 142 pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); 143 if (IS_ERR(pltfm_host->clk)) { 144 err = PTR_ERR(pltfm_host->clk); 145 dev_err(&pdev->dev, "failed to get core clk: %d\n", err); 146 goto free_pltfm; 147 } 148 err = clk_prepare_enable(pltfm_host->clk); 149 if (err) 150 goto free_pltfm; 151 152 priv->bus_clk = devm_clk_get(&pdev->dev, "bus"); 153 if (!IS_ERR(priv->bus_clk)) 154 clk_prepare_enable(priv->bus_clk); 155 156 err = mmc_of_parse(host->mmc); 157 if (err) 158 goto err_clk; 159 160 sdhci_get_of_property(pdev); 161 162 host->mmc_host_ops.request = dwcmshc_request; 163 164 err = sdhci_add_host(host); 165 if (err) 166 goto err_clk; 167 168 return 0; 169 170 err_clk: 171 clk_disable_unprepare(pltfm_host->clk); 172 clk_disable_unprepare(priv->bus_clk); 173 free_pltfm: 174 sdhci_pltfm_free(pdev); 175 return err; 176 } 177 178 static int dwcmshc_remove(struct platform_device *pdev) 179 { 180 struct sdhci_host *host = platform_get_drvdata(pdev); 181 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 182 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 183 184 sdhci_remove_host(host, 0); 185 186 clk_disable_unprepare(pltfm_host->clk); 187 clk_disable_unprepare(priv->bus_clk); 188 189 sdhci_pltfm_free(pdev); 190 191 return 0; 192 } 193 194 #ifdef CONFIG_PM_SLEEP 195 static int dwcmshc_suspend(struct device *dev) 196 { 197 struct sdhci_host *host = dev_get_drvdata(dev); 198 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 199 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 200 int ret; 201 202 ret = sdhci_suspend_host(host); 203 if (ret) 204 return ret; 205 206 clk_disable_unprepare(pltfm_host->clk); 207 if (!IS_ERR(priv->bus_clk)) 208 clk_disable_unprepare(priv->bus_clk); 209 210 return ret; 211 } 212 213 static int dwcmshc_resume(struct device *dev) 214 { 215 struct sdhci_host *host = dev_get_drvdata(dev); 216 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 217 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 218 int ret; 219 220 ret = clk_prepare_enable(pltfm_host->clk); 221 if (ret) 222 return ret; 223 224 if (!IS_ERR(priv->bus_clk)) { 225 ret = clk_prepare_enable(priv->bus_clk); 226 if (ret) 227 return ret; 228 } 229 230 return sdhci_resume_host(host); 231 } 232 #endif 233 234 static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume); 235 236 static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { 237 { .compatible = "snps,dwcmshc-sdhci" }, 238 {} 239 }; 240 MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); 241 242 static struct platform_driver sdhci_dwcmshc_driver = { 243 .driver = { 244 .name = "sdhci-dwcmshc", 245 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 246 .of_match_table = sdhci_dwcmshc_dt_ids, 247 .pm = &dwcmshc_pmops, 248 }, 249 .probe = dwcmshc_probe, 250 .remove = dwcmshc_remove, 251 }; 252 module_platform_driver(sdhci_dwcmshc_driver); 253 254 MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC"); 255 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); 256 MODULE_LICENSE("GPL v2"); 257