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/module.h> 12 #include <linux/of.h> 13 14 #include "sdhci-pltfm.h" 15 16 struct dwcmshc_priv { 17 struct clk *bus_clk; 18 }; 19 20 static const struct sdhci_ops sdhci_dwcmshc_ops = { 21 .set_clock = sdhci_set_clock, 22 .set_bus_width = sdhci_set_bus_width, 23 .set_uhs_signaling = sdhci_set_uhs_signaling, 24 .get_max_clock = sdhci_pltfm_clk_get_max_clock, 25 .reset = sdhci_reset, 26 }; 27 28 static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { 29 .ops = &sdhci_dwcmshc_ops, 30 .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 31 }; 32 33 static int dwcmshc_probe(struct platform_device *pdev) 34 { 35 struct sdhci_pltfm_host *pltfm_host; 36 struct sdhci_host *host; 37 struct dwcmshc_priv *priv; 38 int err; 39 40 host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata, 41 sizeof(struct dwcmshc_priv)); 42 if (IS_ERR(host)) 43 return PTR_ERR(host); 44 45 pltfm_host = sdhci_priv(host); 46 priv = sdhci_pltfm_priv(pltfm_host); 47 48 pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); 49 if (IS_ERR(pltfm_host->clk)) { 50 err = PTR_ERR(pltfm_host->clk); 51 dev_err(&pdev->dev, "failed to get core clk: %d\n", err); 52 goto free_pltfm; 53 } 54 err = clk_prepare_enable(pltfm_host->clk); 55 if (err) 56 goto free_pltfm; 57 58 priv->bus_clk = devm_clk_get(&pdev->dev, "bus"); 59 if (!IS_ERR(priv->bus_clk)) 60 clk_prepare_enable(priv->bus_clk); 61 62 err = mmc_of_parse(host->mmc); 63 if (err) 64 goto err_clk; 65 66 sdhci_get_of_property(pdev); 67 68 err = sdhci_add_host(host); 69 if (err) 70 goto err_clk; 71 72 return 0; 73 74 err_clk: 75 clk_disable_unprepare(pltfm_host->clk); 76 clk_disable_unprepare(priv->bus_clk); 77 free_pltfm: 78 sdhci_pltfm_free(pdev); 79 return err; 80 } 81 82 static int dwcmshc_remove(struct platform_device *pdev) 83 { 84 struct sdhci_host *host = platform_get_drvdata(pdev); 85 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 86 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 87 88 sdhci_remove_host(host, 0); 89 90 clk_disable_unprepare(pltfm_host->clk); 91 clk_disable_unprepare(priv->bus_clk); 92 93 sdhci_pltfm_free(pdev); 94 95 return 0; 96 } 97 98 static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { 99 { .compatible = "snps,dwcmshc-sdhci" }, 100 {} 101 }; 102 MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); 103 104 static struct platform_driver sdhci_dwcmshc_driver = { 105 .driver = { 106 .name = "sdhci-dwcmshc", 107 .of_match_table = sdhci_dwcmshc_dt_ids, 108 }, 109 .probe = dwcmshc_probe, 110 .remove = dwcmshc_remove, 111 }; 112 module_platform_driver(sdhci_dwcmshc_driver); 113 114 MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC"); 115 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); 116 MODULE_LICENSE("GPL v2"); 117