diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-of-dwcmshc.c')
-rw-r--r-- | drivers/mmc/host/sdhci-of-dwcmshc.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c new file mode 100644 index 000000000000..1b7cd144fb01 --- /dev/null +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Synopsys DesignWare Cores Mobile Storage Host Controller + * + * Copyright (C) 2018 Synaptics Incorporated + * + * Author: Jisheng Zhang <jszhang@kernel.org> + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> + +#include "sdhci-pltfm.h" + +struct dwcmshc_priv { + struct clk *bus_clk; +}; + +static const struct sdhci_ops sdhci_dwcmshc_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = sdhci_reset, +}; + +static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { + .ops = &sdhci_dwcmshc_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, +}; + +static int dwcmshc_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + struct dwcmshc_priv *priv; + int err; + + host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata, + sizeof(struct dwcmshc_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); + + pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(pltfm_host->clk)) { + err = PTR_ERR(pltfm_host->clk); + dev_err(&pdev->dev, "failed to get core clk: %d\n", err); + goto free_pltfm; + } + err = clk_prepare_enable(pltfm_host->clk); + if (err) + goto free_pltfm; + + priv->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (!IS_ERR(priv->bus_clk)) + clk_prepare_enable(priv->bus_clk); + + err = mmc_of_parse(host->mmc); + if (err) + goto err_clk; + + sdhci_get_of_property(pdev); + + err = sdhci_add_host(host); + if (err) + goto err_clk; + + return 0; + +err_clk: + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(priv->bus_clk); +free_pltfm: + sdhci_pltfm_free(pdev); + return err; +} + +static int dwcmshc_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + + sdhci_remove_host(host, 0); + + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(priv->bus_clk); + + sdhci_pltfm_free(pdev); + + return 0; +} + +static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { + { .compatible = "snps,dwcmshc-sdhci" }, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); + +static struct platform_driver sdhci_dwcmshc_driver = { + .driver = { + .name = "sdhci-dwcmshc", + .of_match_table = sdhci_dwcmshc_dt_ids, + }, + .probe = dwcmshc_probe, + .remove = dwcmshc_remove, +}; +module_platform_driver(sdhci_dwcmshc_driver); + +MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC"); +MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); +MODULE_LICENSE("GPL v2"); |