Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1/*
  2 * SDHCI support for SiRF primaII and marco SoCs
  3 *
  4 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
  5 *
  6 * Licensed under GPLv2 or later.
  7 */
  8
  9#include <linux/delay.h>
 10#include <linux/device.h>
 11#include <linux/mmc/host.h>
 12#include <linux/module.h>
 13#include <linux/of.h>
 14#include <linux/of_gpio.h>
 15#include <linux/mmc/slot-gpio.h>
 16#include "sdhci-pltfm.h"
 17
 18struct sdhci_sirf_priv {
 19	struct clk *clk;
 20	int gpio_cd;
 21};
 22
 23static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
 24{
 25	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 26	struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
 27	return clk_get_rate(priv->clk);
 28}
 29
 30static struct sdhci_ops sdhci_sirf_ops = {
 31	.get_max_clock	= sdhci_sirf_get_max_clk,
 32};
 33
 34static struct sdhci_pltfm_data sdhci_sirf_pdata = {
 35	.ops = &sdhci_sirf_ops,
 36	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
 37		SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
 38		SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
 39		SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
 40		SDHCI_QUIRK_DELAY_AFTER_POWER,
 41};
 42
 43static int sdhci_sirf_probe(struct platform_device *pdev)
 44{
 45	struct sdhci_host *host;
 46	struct sdhci_pltfm_host *pltfm_host;
 47	struct sdhci_sirf_priv *priv;
 48	struct clk *clk;
 49	int gpio_cd;
 50	int ret;
 51
 52	clk = devm_clk_get(&pdev->dev, NULL);
 53	if (IS_ERR(clk)) {
 54		dev_err(&pdev->dev, "unable to get clock");
 55		return PTR_ERR(clk);
 56	}
 57
 58	if (pdev->dev.of_node)
 59		gpio_cd = of_get_named_gpio(pdev->dev.of_node, "cd-gpios", 0);
 60	else
 61		gpio_cd = -EINVAL;
 62
 63	host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, sizeof(struct sdhci_sirf_priv));
 64	if (IS_ERR(host))
 65		return PTR_ERR(host);
 66
 67	pltfm_host = sdhci_priv(host);
 68	priv = sdhci_pltfm_priv(pltfm_host);
 69	priv->clk = clk;
 70	priv->gpio_cd = gpio_cd;
 71
 72	sdhci_get_of_property(pdev);
 73
 74	ret = clk_prepare_enable(priv->clk);
 75	if (ret)
 76		goto err_clk_prepare;
 77
 78	ret = sdhci_add_host(host);
 79	if (ret)
 80		goto err_sdhci_add;
 81
 82	/*
 83	 * We must request the IRQ after sdhci_add_host(), as the tasklet only
 84	 * gets setup in sdhci_add_host() and we oops.
 85	 */
 86	if (gpio_is_valid(priv->gpio_cd)) {
 87		ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd, 0);
 88		if (ret) {
 89			dev_err(&pdev->dev, "card detect irq request failed: %d\n",
 90				ret);
 91			goto err_request_cd;
 92		}
 93	}
 94
 95	return 0;
 96
 97err_request_cd:
 98	sdhci_remove_host(host, 0);
 99err_sdhci_add:
100	clk_disable_unprepare(priv->clk);
101err_clk_prepare:
102	sdhci_pltfm_free(pdev);
103	return ret;
104}
105
106static int sdhci_sirf_remove(struct platform_device *pdev)
107{
108	struct sdhci_host *host = platform_get_drvdata(pdev);
109	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
110	struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
111
112	sdhci_pltfm_unregister(pdev);
113
114	if (gpio_is_valid(priv->gpio_cd))
115		mmc_gpio_free_cd(host->mmc);
116
117	clk_disable_unprepare(priv->clk);
118	return 0;
119}
120
121#ifdef CONFIG_PM_SLEEP
122static int sdhci_sirf_suspend(struct device *dev)
123{
124	struct sdhci_host *host = dev_get_drvdata(dev);
125	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
126	struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
127	int ret;
128
129	ret = sdhci_suspend_host(host);
130	if (ret)
131		return ret;
132
133	clk_disable(priv->clk);
134
135	return 0;
136}
137
138static int sdhci_sirf_resume(struct device *dev)
139{
140	struct sdhci_host *host = dev_get_drvdata(dev);
141	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
142	struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
143	int ret;
144
145	ret = clk_enable(priv->clk);
146	if (ret) {
147		dev_dbg(dev, "Resume: Error enabling clock\n");
148		return ret;
149	}
150
151	return sdhci_resume_host(host);
152}
153
154static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops, sdhci_sirf_suspend, sdhci_sirf_resume);
155#endif
156
157static const struct of_device_id sdhci_sirf_of_match[] = {
158	{ .compatible = "sirf,prima2-sdhc" },
159	{ }
160};
161MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
162
163static struct platform_driver sdhci_sirf_driver = {
164	.driver		= {
165		.name	= "sdhci-sirf",
166		.owner	= THIS_MODULE,
167		.of_match_table = sdhci_sirf_of_match,
168#ifdef CONFIG_PM_SLEEP
169		.pm	= &sdhci_sirf_pm_ops,
170#endif
171	},
172	.probe		= sdhci_sirf_probe,
173	.remove		= sdhci_sirf_remove,
174};
175
176module_platform_driver(sdhci_sirf_driver);
177
178MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco");
179MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
180MODULE_LICENSE("GPL v2");