xref: /openbmc/linux/drivers/mmc/host/sdhci-st.c (revision f52d9c4f)
1f52d9c4fSPeter Griffin /*
2f52d9c4fSPeter Griffin  * Support for SDHCI on STMicroelectronics SoCs
3f52d9c4fSPeter Griffin  *
4f52d9c4fSPeter Griffin  * Copyright (C) 2014 STMicroelectronics Ltd
5f52d9c4fSPeter Griffin  * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
6f52d9c4fSPeter Griffin  * Contributors: Peter Griffin <peter.griffin@linaro.org>
7f52d9c4fSPeter Griffin  *
8f52d9c4fSPeter Griffin  * Based on sdhci-cns3xxx.c
9f52d9c4fSPeter Griffin  *
10f52d9c4fSPeter Griffin  * This program is free software; you can redistribute it and/or modify
11f52d9c4fSPeter Griffin  * it under the terms of the GNU General Public License version 2 as
12f52d9c4fSPeter Griffin  * published by the Free Software Foundation.
13f52d9c4fSPeter Griffin  *
14f52d9c4fSPeter Griffin  * This program is distributed in the hope that it will be useful,
15f52d9c4fSPeter Griffin  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16f52d9c4fSPeter Griffin  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17f52d9c4fSPeter Griffin  * GNU General Public License for more details.
18f52d9c4fSPeter Griffin  *
19f52d9c4fSPeter Griffin  */
20f52d9c4fSPeter Griffin 
21f52d9c4fSPeter Griffin #include <linux/io.h>
22f52d9c4fSPeter Griffin #include <linux/of.h>
23f52d9c4fSPeter Griffin #include <linux/module.h>
24f52d9c4fSPeter Griffin #include <linux/err.h>
25f52d9c4fSPeter Griffin #include <linux/mmc/host.h>
26f52d9c4fSPeter Griffin 
27f52d9c4fSPeter Griffin #include "sdhci-pltfm.h"
28f52d9c4fSPeter Griffin 
29f52d9c4fSPeter Griffin static u32 sdhci_st_readl(struct sdhci_host *host, int reg)
30f52d9c4fSPeter Griffin {
31f52d9c4fSPeter Griffin 	u32 ret;
32f52d9c4fSPeter Griffin 
33f52d9c4fSPeter Griffin 	switch (reg) {
34f52d9c4fSPeter Griffin 	case SDHCI_CAPABILITIES:
35f52d9c4fSPeter Griffin 		ret = readl_relaxed(host->ioaddr + reg);
36f52d9c4fSPeter Griffin 		/* Support 3.3V and 1.8V */
37f52d9c4fSPeter Griffin 		ret &= ~SDHCI_CAN_VDD_300;
38f52d9c4fSPeter Griffin 		break;
39f52d9c4fSPeter Griffin 	default:
40f52d9c4fSPeter Griffin 		ret = readl_relaxed(host->ioaddr + reg);
41f52d9c4fSPeter Griffin 	}
42f52d9c4fSPeter Griffin 	return ret;
43f52d9c4fSPeter Griffin }
44f52d9c4fSPeter Griffin 
45f52d9c4fSPeter Griffin static const struct sdhci_ops sdhci_st_ops = {
46f52d9c4fSPeter Griffin 	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
47f52d9c4fSPeter Griffin 	.set_clock = sdhci_set_clock,
48f52d9c4fSPeter Griffin 	.set_bus_width = sdhci_set_bus_width,
49f52d9c4fSPeter Griffin 	.read_l = sdhci_st_readl,
50f52d9c4fSPeter Griffin 	.reset = sdhci_reset,
51f52d9c4fSPeter Griffin };
52f52d9c4fSPeter Griffin 
53f52d9c4fSPeter Griffin static const struct sdhci_pltfm_data sdhci_st_pdata = {
54f52d9c4fSPeter Griffin 	.ops = &sdhci_st_ops,
55f52d9c4fSPeter Griffin 	.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
56f52d9c4fSPeter Griffin 	    SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
57f52d9c4fSPeter Griffin };
58f52d9c4fSPeter Griffin 
59f52d9c4fSPeter Griffin 
60f52d9c4fSPeter Griffin static int sdhci_st_probe(struct platform_device *pdev)
61f52d9c4fSPeter Griffin {
62f52d9c4fSPeter Griffin 	struct sdhci_host *host;
63f52d9c4fSPeter Griffin 	struct sdhci_pltfm_host *pltfm_host;
64f52d9c4fSPeter Griffin 	struct clk *clk;
65f52d9c4fSPeter Griffin 	int ret = 0;
66f52d9c4fSPeter Griffin 	u16 host_version;
67f52d9c4fSPeter Griffin 
68f52d9c4fSPeter Griffin 	clk =  devm_clk_get(&pdev->dev, "mmc");
69f52d9c4fSPeter Griffin 	if (IS_ERR(clk)) {
70f52d9c4fSPeter Griffin 		dev_err(&pdev->dev, "Peripheral clk not found\n");
71f52d9c4fSPeter Griffin 		return PTR_ERR(clk);
72f52d9c4fSPeter Griffin 	}
73f52d9c4fSPeter Griffin 
74f52d9c4fSPeter Griffin 	host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0);
75f52d9c4fSPeter Griffin 	if (IS_ERR(host)) {
76f52d9c4fSPeter Griffin 		dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n");
77f52d9c4fSPeter Griffin 		return PTR_ERR(host);
78f52d9c4fSPeter Griffin 	}
79f52d9c4fSPeter Griffin 
80f52d9c4fSPeter Griffin 	ret = mmc_of_parse(host->mmc);
81f52d9c4fSPeter Griffin 
82f52d9c4fSPeter Griffin 	if (ret) {
83f52d9c4fSPeter Griffin 		dev_err(&pdev->dev, "Failed mmc_of_parse\n");
84f52d9c4fSPeter Griffin 		return ret;
85f52d9c4fSPeter Griffin 	}
86f52d9c4fSPeter Griffin 
87f52d9c4fSPeter Griffin 	clk_prepare_enable(clk);
88f52d9c4fSPeter Griffin 
89f52d9c4fSPeter Griffin 	pltfm_host = sdhci_priv(host);
90f52d9c4fSPeter Griffin 	pltfm_host->clk = clk;
91f52d9c4fSPeter Griffin 
92f52d9c4fSPeter Griffin 	ret = sdhci_add_host(host);
93f52d9c4fSPeter Griffin 	if (ret) {
94f52d9c4fSPeter Griffin 		dev_err(&pdev->dev, "Failed sdhci_add_host\n");
95f52d9c4fSPeter Griffin 		goto err_out;
96f52d9c4fSPeter Griffin 	}
97f52d9c4fSPeter Griffin 
98f52d9c4fSPeter Griffin 	platform_set_drvdata(pdev, host);
99f52d9c4fSPeter Griffin 
100f52d9c4fSPeter Griffin 	host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
101f52d9c4fSPeter Griffin 
102f52d9c4fSPeter Griffin 	dev_info(&pdev->dev, "SDHCI ST Initialised: Host Version: 0x%x Vendor Version 0x%x\n",
103f52d9c4fSPeter Griffin 		((host_version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT),
104f52d9c4fSPeter Griffin 		((host_version & SDHCI_VENDOR_VER_MASK) >>
105f52d9c4fSPeter Griffin 		SDHCI_VENDOR_VER_SHIFT));
106f52d9c4fSPeter Griffin 
107f52d9c4fSPeter Griffin 	return 0;
108f52d9c4fSPeter Griffin 
109f52d9c4fSPeter Griffin err_out:
110f52d9c4fSPeter Griffin 	clk_disable_unprepare(clk);
111f52d9c4fSPeter Griffin 	sdhci_pltfm_free(pdev);
112f52d9c4fSPeter Griffin 
113f52d9c4fSPeter Griffin 	return ret;
114f52d9c4fSPeter Griffin }
115f52d9c4fSPeter Griffin 
116f52d9c4fSPeter Griffin static int sdhci_st_remove(struct platform_device *pdev)
117f52d9c4fSPeter Griffin {
118f52d9c4fSPeter Griffin 	struct sdhci_host *host = platform_get_drvdata(pdev);
119f52d9c4fSPeter Griffin 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
120f52d9c4fSPeter Griffin 
121f52d9c4fSPeter Griffin 	clk_disable_unprepare(pltfm_host->clk);
122f52d9c4fSPeter Griffin 
123f52d9c4fSPeter Griffin 	return sdhci_pltfm_unregister(pdev);
124f52d9c4fSPeter Griffin }
125f52d9c4fSPeter Griffin 
126f52d9c4fSPeter Griffin #ifdef CONFIG_PM_SLEEP
127f52d9c4fSPeter Griffin static int sdhci_st_suspend(struct device *dev)
128f52d9c4fSPeter Griffin {
129f52d9c4fSPeter Griffin 	struct sdhci_host *host = dev_get_drvdata(dev);
130f52d9c4fSPeter Griffin 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
131f52d9c4fSPeter Griffin 	int ret = sdhci_suspend_host(host);
132f52d9c4fSPeter Griffin 
133f52d9c4fSPeter Griffin 	if (ret)
134f52d9c4fSPeter Griffin 		goto out;
135f52d9c4fSPeter Griffin 
136f52d9c4fSPeter Griffin 	clk_disable_unprepare(pltfm_host->clk);
137f52d9c4fSPeter Griffin out:
138f52d9c4fSPeter Griffin 	return ret;
139f52d9c4fSPeter Griffin }
140f52d9c4fSPeter Griffin 
141f52d9c4fSPeter Griffin static int sdhci_st_resume(struct device *dev)
142f52d9c4fSPeter Griffin {
143f52d9c4fSPeter Griffin 	struct sdhci_host *host = dev_get_drvdata(dev);
144f52d9c4fSPeter Griffin 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
145f52d9c4fSPeter Griffin 
146f52d9c4fSPeter Griffin 	clk_prepare_enable(pltfm_host->clk);
147f52d9c4fSPeter Griffin 
148f52d9c4fSPeter Griffin 	return sdhci_resume_host(host);
149f52d9c4fSPeter Griffin }
150f52d9c4fSPeter Griffin #endif
151f52d9c4fSPeter Griffin 
152f52d9c4fSPeter Griffin static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume);
153f52d9c4fSPeter Griffin 
154f52d9c4fSPeter Griffin static const struct of_device_id st_sdhci_match[] = {
155f52d9c4fSPeter Griffin 	{ .compatible = "st,sdhci" },
156f52d9c4fSPeter Griffin 	{},
157f52d9c4fSPeter Griffin };
158f52d9c4fSPeter Griffin 
159f52d9c4fSPeter Griffin MODULE_DEVICE_TABLE(of, st_sdhci_match);
160f52d9c4fSPeter Griffin 
161f52d9c4fSPeter Griffin static struct platform_driver sdhci_st_driver = {
162f52d9c4fSPeter Griffin 	.probe = sdhci_st_probe,
163f52d9c4fSPeter Griffin 	.remove = sdhci_st_remove,
164f52d9c4fSPeter Griffin 	.driver = {
165f52d9c4fSPeter Griffin 		   .name = "sdhci-st",
166f52d9c4fSPeter Griffin 		   .pm = &sdhci_st_pmops,
167f52d9c4fSPeter Griffin 		   .of_match_table = of_match_ptr(st_sdhci_match),
168f52d9c4fSPeter Griffin 		  },
169f52d9c4fSPeter Griffin };
170f52d9c4fSPeter Griffin 
171f52d9c4fSPeter Griffin module_platform_driver(sdhci_st_driver);
172f52d9c4fSPeter Griffin 
173f52d9c4fSPeter Griffin MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs");
174f52d9c4fSPeter Griffin MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
175f52d9c4fSPeter Griffin MODULE_LICENSE("GPL v2");
176f52d9c4fSPeter Griffin MODULE_ALIAS("platform:st-sdhci");
177