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