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 19554232e8SJisheng Zhang /* DWCMSHC specific Mode Select value */ 20554232e8SJisheng Zhang #define DWCMSHC_CTRL_HS400 0x7 21554232e8SJisheng Zhang 22b85c997dSJisheng Zhang #define BOUNDARY_OK(addr, len) \ 23b85c997dSJisheng Zhang ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) 24b85c997dSJisheng Zhang 25e438cf49SJisheng Zhang struct dwcmshc_priv { 26e438cf49SJisheng Zhang struct clk *bus_clk; 27e438cf49SJisheng Zhang }; 28e438cf49SJisheng Zhang 29b85c997dSJisheng Zhang /* 30b85c997dSJisheng Zhang * If DMA addr spans 128MB boundary, we split the DMA transfer into two 31b85c997dSJisheng Zhang * so that each DMA transfer doesn't exceed the boundary. 32b85c997dSJisheng Zhang */ 33b85c997dSJisheng Zhang static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, 34b85c997dSJisheng Zhang dma_addr_t addr, int len, unsigned int cmd) 35b85c997dSJisheng Zhang { 36b85c997dSJisheng Zhang int tmplen, offset; 37b85c997dSJisheng Zhang 38b85c997dSJisheng Zhang if (likely(!len || BOUNDARY_OK(addr, len))) { 39b85c997dSJisheng Zhang sdhci_adma_write_desc(host, desc, addr, len, cmd); 40b85c997dSJisheng Zhang return; 41b85c997dSJisheng Zhang } 42b85c997dSJisheng Zhang 43b85c997dSJisheng Zhang offset = addr & (SZ_128M - 1); 44b85c997dSJisheng Zhang tmplen = SZ_128M - offset; 45b85c997dSJisheng Zhang sdhci_adma_write_desc(host, desc, addr, tmplen, cmd); 46b85c997dSJisheng Zhang 47b85c997dSJisheng Zhang addr += tmplen; 48b85c997dSJisheng Zhang len -= tmplen; 49b85c997dSJisheng Zhang sdhci_adma_write_desc(host, desc, addr, len, cmd); 50b85c997dSJisheng Zhang } 51b85c997dSJisheng Zhang 52554232e8SJisheng Zhang static void dwcmshc_set_uhs_signaling(struct sdhci_host *host, 53554232e8SJisheng Zhang unsigned int timing) 54554232e8SJisheng Zhang { 55554232e8SJisheng Zhang u16 ctrl_2; 56554232e8SJisheng Zhang 57554232e8SJisheng Zhang ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 58554232e8SJisheng Zhang /* Select Bus Speed Mode for host */ 59554232e8SJisheng Zhang ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 60554232e8SJisheng Zhang if ((timing == MMC_TIMING_MMC_HS200) || 61554232e8SJisheng Zhang (timing == MMC_TIMING_UHS_SDR104)) 62554232e8SJisheng Zhang ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 63554232e8SJisheng Zhang else if (timing == MMC_TIMING_UHS_SDR12) 64554232e8SJisheng Zhang ctrl_2 |= SDHCI_CTRL_UHS_SDR12; 65554232e8SJisheng Zhang else if ((timing == MMC_TIMING_UHS_SDR25) || 66554232e8SJisheng Zhang (timing == MMC_TIMING_MMC_HS)) 67554232e8SJisheng Zhang ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 68554232e8SJisheng Zhang else if (timing == MMC_TIMING_UHS_SDR50) 69554232e8SJisheng Zhang ctrl_2 |= SDHCI_CTRL_UHS_SDR50; 70554232e8SJisheng Zhang else if ((timing == MMC_TIMING_UHS_DDR50) || 71554232e8SJisheng Zhang (timing == MMC_TIMING_MMC_DDR52)) 72554232e8SJisheng Zhang ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 73554232e8SJisheng Zhang else if (timing == MMC_TIMING_MMC_HS400) 74554232e8SJisheng Zhang ctrl_2 |= DWCMSHC_CTRL_HS400; 75554232e8SJisheng Zhang sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 76554232e8SJisheng Zhang } 77554232e8SJisheng Zhang 78e438cf49SJisheng Zhang static const struct sdhci_ops sdhci_dwcmshc_ops = { 79e438cf49SJisheng Zhang .set_clock = sdhci_set_clock, 80e438cf49SJisheng Zhang .set_bus_width = sdhci_set_bus_width, 81554232e8SJisheng Zhang .set_uhs_signaling = dwcmshc_set_uhs_signaling, 82e438cf49SJisheng Zhang .get_max_clock = sdhci_pltfm_clk_get_max_clock, 83e438cf49SJisheng Zhang .reset = sdhci_reset, 84b85c997dSJisheng Zhang .adma_write_desc = dwcmshc_adma_write_desc, 85e438cf49SJisheng Zhang }; 86e438cf49SJisheng Zhang 87e438cf49SJisheng Zhang static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { 88e438cf49SJisheng Zhang .ops = &sdhci_dwcmshc_ops, 89e438cf49SJisheng Zhang .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 90e438cf49SJisheng Zhang }; 91e438cf49SJisheng Zhang 92e438cf49SJisheng Zhang static int dwcmshc_probe(struct platform_device *pdev) 93e438cf49SJisheng Zhang { 94e438cf49SJisheng Zhang struct sdhci_pltfm_host *pltfm_host; 95e438cf49SJisheng Zhang struct sdhci_host *host; 96e438cf49SJisheng Zhang struct dwcmshc_priv *priv; 97e438cf49SJisheng Zhang int err; 98b85c997dSJisheng Zhang u32 extra; 99e438cf49SJisheng Zhang 100e438cf49SJisheng Zhang host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata, 101e438cf49SJisheng Zhang sizeof(struct dwcmshc_priv)); 102e438cf49SJisheng Zhang if (IS_ERR(host)) 103e438cf49SJisheng Zhang return PTR_ERR(host); 104e438cf49SJisheng Zhang 105b85c997dSJisheng Zhang /* 106b85c997dSJisheng Zhang * extra adma table cnt for cross 128M boundary handling. 107b85c997dSJisheng Zhang */ 108b85c997dSJisheng Zhang extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M); 109b85c997dSJisheng Zhang if (extra > SDHCI_MAX_SEGS) 110b85c997dSJisheng Zhang extra = SDHCI_MAX_SEGS; 111b85c997dSJisheng Zhang host->adma_table_cnt += extra; 112b85c997dSJisheng Zhang 113e438cf49SJisheng Zhang pltfm_host = sdhci_priv(host); 114e438cf49SJisheng Zhang priv = sdhci_pltfm_priv(pltfm_host); 115e438cf49SJisheng Zhang 116e438cf49SJisheng Zhang pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); 117e438cf49SJisheng Zhang if (IS_ERR(pltfm_host->clk)) { 118e438cf49SJisheng Zhang err = PTR_ERR(pltfm_host->clk); 119e438cf49SJisheng Zhang dev_err(&pdev->dev, "failed to get core clk: %d\n", err); 120e438cf49SJisheng Zhang goto free_pltfm; 121e438cf49SJisheng Zhang } 122e438cf49SJisheng Zhang err = clk_prepare_enable(pltfm_host->clk); 123e438cf49SJisheng Zhang if (err) 124e438cf49SJisheng Zhang goto free_pltfm; 125e438cf49SJisheng Zhang 126e438cf49SJisheng Zhang priv->bus_clk = devm_clk_get(&pdev->dev, "bus"); 127e438cf49SJisheng Zhang if (!IS_ERR(priv->bus_clk)) 128e438cf49SJisheng Zhang clk_prepare_enable(priv->bus_clk); 129e438cf49SJisheng Zhang 130e438cf49SJisheng Zhang err = mmc_of_parse(host->mmc); 131e438cf49SJisheng Zhang if (err) 132e438cf49SJisheng Zhang goto err_clk; 133e438cf49SJisheng Zhang 134e438cf49SJisheng Zhang sdhci_get_of_property(pdev); 135e438cf49SJisheng Zhang 136e438cf49SJisheng Zhang err = sdhci_add_host(host); 137e438cf49SJisheng Zhang if (err) 138e438cf49SJisheng Zhang goto err_clk; 139e438cf49SJisheng Zhang 140e438cf49SJisheng Zhang return 0; 141e438cf49SJisheng Zhang 142e438cf49SJisheng Zhang err_clk: 143e438cf49SJisheng Zhang clk_disable_unprepare(pltfm_host->clk); 144e438cf49SJisheng Zhang clk_disable_unprepare(priv->bus_clk); 145e438cf49SJisheng Zhang free_pltfm: 146e438cf49SJisheng Zhang sdhci_pltfm_free(pdev); 147e438cf49SJisheng Zhang return err; 148e438cf49SJisheng Zhang } 149e438cf49SJisheng Zhang 150e438cf49SJisheng Zhang static int dwcmshc_remove(struct platform_device *pdev) 151e438cf49SJisheng Zhang { 152e438cf49SJisheng Zhang struct sdhci_host *host = platform_get_drvdata(pdev); 153e438cf49SJisheng Zhang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 154e438cf49SJisheng Zhang struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 155e438cf49SJisheng Zhang 156e438cf49SJisheng Zhang sdhci_remove_host(host, 0); 157e438cf49SJisheng Zhang 158e438cf49SJisheng Zhang clk_disable_unprepare(pltfm_host->clk); 159e438cf49SJisheng Zhang clk_disable_unprepare(priv->bus_clk); 160e438cf49SJisheng Zhang 161e438cf49SJisheng Zhang sdhci_pltfm_free(pdev); 162e438cf49SJisheng Zhang 163e438cf49SJisheng Zhang return 0; 164e438cf49SJisheng Zhang } 165e438cf49SJisheng Zhang 166bccce2ecSJisheng Zhang #ifdef CONFIG_PM_SLEEP 167bccce2ecSJisheng Zhang static int dwcmshc_suspend(struct device *dev) 168bccce2ecSJisheng Zhang { 169bccce2ecSJisheng Zhang struct sdhci_host *host = dev_get_drvdata(dev); 170bccce2ecSJisheng Zhang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 171bccce2ecSJisheng Zhang struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 172bccce2ecSJisheng Zhang int ret; 173bccce2ecSJisheng Zhang 174bccce2ecSJisheng Zhang ret = sdhci_suspend_host(host); 175bccce2ecSJisheng Zhang if (ret) 176bccce2ecSJisheng Zhang return ret; 177bccce2ecSJisheng Zhang 178bccce2ecSJisheng Zhang clk_disable_unprepare(pltfm_host->clk); 179bccce2ecSJisheng Zhang if (!IS_ERR(priv->bus_clk)) 180bccce2ecSJisheng Zhang clk_disable_unprepare(priv->bus_clk); 181bccce2ecSJisheng Zhang 182bccce2ecSJisheng Zhang return ret; 183bccce2ecSJisheng Zhang } 184bccce2ecSJisheng Zhang 185bccce2ecSJisheng Zhang static int dwcmshc_resume(struct device *dev) 186bccce2ecSJisheng Zhang { 187bccce2ecSJisheng Zhang struct sdhci_host *host = dev_get_drvdata(dev); 188bccce2ecSJisheng Zhang struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 189bccce2ecSJisheng Zhang struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 190bccce2ecSJisheng Zhang int ret; 191bccce2ecSJisheng Zhang 192bccce2ecSJisheng Zhang ret = clk_prepare_enable(pltfm_host->clk); 193bccce2ecSJisheng Zhang if (ret) 194bccce2ecSJisheng Zhang return ret; 195bccce2ecSJisheng Zhang 196bccce2ecSJisheng Zhang if (!IS_ERR(priv->bus_clk)) { 197bccce2ecSJisheng Zhang ret = clk_prepare_enable(priv->bus_clk); 198bccce2ecSJisheng Zhang if (ret) 199bccce2ecSJisheng Zhang return ret; 200bccce2ecSJisheng Zhang } 201bccce2ecSJisheng Zhang 202bccce2ecSJisheng Zhang return sdhci_resume_host(host); 203bccce2ecSJisheng Zhang } 204bccce2ecSJisheng Zhang #endif 205bccce2ecSJisheng Zhang 206bccce2ecSJisheng Zhang static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume); 207bccce2ecSJisheng Zhang 208e438cf49SJisheng Zhang static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { 209e438cf49SJisheng Zhang { .compatible = "snps,dwcmshc-sdhci" }, 210e438cf49SJisheng Zhang {} 211e438cf49SJisheng Zhang }; 212e438cf49SJisheng Zhang MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); 213e438cf49SJisheng Zhang 214e438cf49SJisheng Zhang static struct platform_driver sdhci_dwcmshc_driver = { 215e438cf49SJisheng Zhang .driver = { 216e438cf49SJisheng Zhang .name = "sdhci-dwcmshc", 217e438cf49SJisheng Zhang .of_match_table = sdhci_dwcmshc_dt_ids, 218bccce2ecSJisheng Zhang .pm = &dwcmshc_pmops, 219e438cf49SJisheng Zhang }, 220e438cf49SJisheng Zhang .probe = dwcmshc_probe, 221e438cf49SJisheng Zhang .remove = dwcmshc_remove, 222e438cf49SJisheng Zhang }; 223e438cf49SJisheng Zhang module_platform_driver(sdhci_dwcmshc_driver); 224e438cf49SJisheng Zhang 225e438cf49SJisheng Zhang MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC"); 226e438cf49SJisheng Zhang MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); 227e438cf49SJisheng Zhang MODULE_LICENSE("GPL v2"); 228