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 }; 116 117 static int dwcmshc_probe(struct platform_device *pdev) 118 { 119 struct sdhci_pltfm_host *pltfm_host; 120 struct sdhci_host *host; 121 struct dwcmshc_priv *priv; 122 int err; 123 u32 extra; 124 125 host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata, 126 sizeof(struct dwcmshc_priv)); 127 if (IS_ERR(host)) 128 return PTR_ERR(host); 129 130 /* 131 * extra adma table cnt for cross 128M boundary handling. 132 */ 133 extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M); 134 if (extra > SDHCI_MAX_SEGS) 135 extra = SDHCI_MAX_SEGS; 136 host->adma_table_cnt += extra; 137 138 pltfm_host = sdhci_priv(host); 139 priv = sdhci_pltfm_priv(pltfm_host); 140 141 pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); 142 if (IS_ERR(pltfm_host->clk)) { 143 err = PTR_ERR(pltfm_host->clk); 144 dev_err(&pdev->dev, "failed to get core clk: %d\n", err); 145 goto free_pltfm; 146 } 147 err = clk_prepare_enable(pltfm_host->clk); 148 if (err) 149 goto free_pltfm; 150 151 priv->bus_clk = devm_clk_get(&pdev->dev, "bus"); 152 if (!IS_ERR(priv->bus_clk)) 153 clk_prepare_enable(priv->bus_clk); 154 155 err = mmc_of_parse(host->mmc); 156 if (err) 157 goto err_clk; 158 159 sdhci_get_of_property(pdev); 160 161 host->mmc_host_ops.request = dwcmshc_request; 162 163 err = sdhci_add_host(host); 164 if (err) 165 goto err_clk; 166 167 return 0; 168 169 err_clk: 170 clk_disable_unprepare(pltfm_host->clk); 171 clk_disable_unprepare(priv->bus_clk); 172 free_pltfm: 173 sdhci_pltfm_free(pdev); 174 return err; 175 } 176 177 static int dwcmshc_remove(struct platform_device *pdev) 178 { 179 struct sdhci_host *host = platform_get_drvdata(pdev); 180 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 181 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 182 183 sdhci_remove_host(host, 0); 184 185 clk_disable_unprepare(pltfm_host->clk); 186 clk_disable_unprepare(priv->bus_clk); 187 188 sdhci_pltfm_free(pdev); 189 190 return 0; 191 } 192 193 #ifdef CONFIG_PM_SLEEP 194 static int dwcmshc_suspend(struct device *dev) 195 { 196 struct sdhci_host *host = dev_get_drvdata(dev); 197 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 198 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 199 int ret; 200 201 ret = sdhci_suspend_host(host); 202 if (ret) 203 return ret; 204 205 clk_disable_unprepare(pltfm_host->clk); 206 if (!IS_ERR(priv->bus_clk)) 207 clk_disable_unprepare(priv->bus_clk); 208 209 return ret; 210 } 211 212 static int dwcmshc_resume(struct device *dev) 213 { 214 struct sdhci_host *host = dev_get_drvdata(dev); 215 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 216 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 217 int ret; 218 219 ret = clk_prepare_enable(pltfm_host->clk); 220 if (ret) 221 return ret; 222 223 if (!IS_ERR(priv->bus_clk)) { 224 ret = clk_prepare_enable(priv->bus_clk); 225 if (ret) 226 return ret; 227 } 228 229 return sdhci_resume_host(host); 230 } 231 #endif 232 233 static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume); 234 235 static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { 236 { .compatible = "snps,dwcmshc-sdhci" }, 237 {} 238 }; 239 MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); 240 241 static struct platform_driver sdhci_dwcmshc_driver = { 242 .driver = { 243 .name = "sdhci-dwcmshc", 244 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 245 .of_match_table = sdhci_dwcmshc_dt_ids, 246 .pm = &dwcmshc_pmops, 247 }, 248 .probe = dwcmshc_probe, 249 .remove = dwcmshc_remove, 250 }; 251 module_platform_driver(sdhci_dwcmshc_driver); 252 253 MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC"); 254 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); 255 MODULE_LICENSE("GPL v2"); 256