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