aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSoren Brinkmann <soren.brinkmann@xilinx.com>2013-12-02 13:02:36 -0500
committerChris Ball <chris@printf.net>2014-01-13 12:48:34 -0500
commite3ec3a3d11adf33c2e6ead1642b7e601b7a1b2df (patch)
treed033fbdccaeb97c1faf50a88334ff999b47b7dee
parent036f29d554e84fa288411d950c2f0ae797be9146 (diff)
mmc: arasan: Add driver for Arasan SDHCI
Add a driver for Arasan's SDHCI controller core. Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com> Acked-by: Rob Herring <rob.herring@calxeda.com> [binding] Acked-by: Michal Simek <monstr@monstr.eu> Signed-off-by: Chris Ball <chris@printf.net>
-rw-r--r--Documentation/devicetree/bindings/mmc/arasan,sdhci.txt27
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/mmc/host/Kconfig12
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c224
5 files changed, 265 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
new file mode 100644
index 000000000000..98ee2abbe138
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
@@ -0,0 +1,27 @@
1Device Tree Bindings for the Arasan SDHCI Controller
2
3 The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. Only
4 deviations are documented here.
5
6 [1] Documentation/devicetree/bindings/mmc/mmc.txt
7 [2] Documentation/devicetree/bindings/clock/clock-bindings.txt
8 [3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
9
10Required Properties:
11 - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a'
12 - reg: From mmc bindings: Register location and length.
13 - clocks: From clock bindings: Handles to clock inputs.
14 - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
15 - interrupts: Interrupt specifier
16 - interrupt-parent: Phandle for the interrupt controller that services
17 interrupts for this device.
18
19Example:
20 sdhci@e0100000 {
21 compatible = "arasan,sdhci-8.9a";
22 reg = <0xe0100000 0x1000>;
23 clock-names = "clk_xin", "clk_ahb";
24 clocks = <&clkc 21>, <&clkc 32>;
25 interrupt-parent = <&gic>;
26 interrupts = <0 24 4>;
27 } ;
diff --git a/MAINTAINERS b/MAINTAINERS
index 7cda615c8dd7..518bf717b02b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1371,6 +1371,7 @@ T: git git://git.xilinx.com/linux-xlnx.git
1371S: Supported 1371S: Supported
1372F: arch/arm/mach-zynq/ 1372F: arch/arm/mach-zynq/
1373F: drivers/cpuidle/cpuidle-zynq.c 1373F: drivers/cpuidle/cpuidle-zynq.c
1374F: drivers/mmc/host/sdhci-of-arasan.c
1374 1375
1375ARM SMMU DRIVER 1376ARM SMMU DRIVER
1376M: Will Deacon <will.deacon@arm.com> 1377M: Will Deacon <will.deacon@arm.com>
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 6da5a085f5f8..c79c1ea722c3 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM
104 104
105 If unsure, say N. 105 If unsure, say N.
106 106
107config MMC_SDHCI_OF_ARASAN
108 tristate "SDHCI OF support for the Arasan SDHCI controllers"
109 depends on MMC_SDHCI_PLTFM
110 depends on OF
111 help
112 This selects the Arasan Secure Digital Host Controller Interface
113 (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
114
115 If you have a controller with this interface, say Y or M here.
116
117 If unsure, say N.
118
107config MMC_SDHCI_OF_ESDHC 119config MMC_SDHCI_OF_ESDHC
108 tristate "SDHCI OF support for the Freescale eSDHC controller" 120 tristate "SDHCI OF support for the Freescale eSDHC controller"
109 depends on MMC_SDHCI_PLTFM 121 depends on MMC_SDHCI_PLTFM
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index a69fd640698d..3483b6b6b880 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
59obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o 59obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
60obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o 60obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
61obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o 61obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
62obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o
62obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o 63obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
63obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o 64obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
64obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o 65obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
new file mode 100644
index 000000000000..f7c7cf62437d
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -0,0 +1,224 @@
1/*
2 * Arasan Secure Digital Host Controller Interface.
3 * Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu>
4 * Copyright (c) 2012 Wind River Systems, Inc.
5 * Copyright (C) 2013 Pengutronix e.K.
6 * Copyright (C) 2013 Xilinx Inc.
7 *
8 * Based on sdhci-of-esdhc.c
9 *
10 * Copyright (c) 2007 Freescale Semiconductor, Inc.
11 * Copyright (c) 2009 MontaVista Software, Inc.
12 *
13 * Authors: Xiaobo Xie <X.Xie@freescale.com>
14 * Anton Vorontsov <avorontsov@ru.mvista.com>
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or (at
19 * your option) any later version.
20 */
21
22#include <linux/module.h>
23#include "sdhci-pltfm.h"
24
25#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
26
27#define CLK_CTRL_TIMEOUT_SHIFT 16
28#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
29#define CLK_CTRL_TIMEOUT_MIN_EXP 13
30
31/**
32 * struct sdhci_arasan_data
33 * @clk_ahb: Pointer to the AHB clock
34 */
35struct sdhci_arasan_data {
36 struct clk *clk_ahb;
37};
38
39static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
40{
41 u32 div;
42 unsigned long freq;
43 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
44
45 div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET);
46 div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT;
47
48 freq = clk_get_rate(pltfm_host->clk);
49 freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div);
50
51 return freq;
52}
53
54static struct sdhci_ops sdhci_arasan_ops = {
55 .get_max_clock = sdhci_pltfm_clk_get_max_clock,
56 .get_timeout_clock = sdhci_arasan_get_timeout_clock,
57};
58
59static struct sdhci_pltfm_data sdhci_arasan_pdata = {
60 .ops = &sdhci_arasan_ops,
61};
62
63#ifdef CONFIG_PM_SLEEP
64/**
65 * sdhci_arasan_suspend - Suspend method for the driver
66 * @dev: Address of the device structure
67 * Returns 0 on success and error value on error
68 *
69 * Put the device in a low power state.
70 */
71static int sdhci_arasan_suspend(struct device *dev)
72{
73 struct platform_device *pdev = to_platform_device(dev);
74 struct sdhci_host *host = platform_get_drvdata(pdev);
75 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
76 struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
77 int ret;
78
79 ret = sdhci_suspend_host(host);
80 if (ret)
81 return ret;
82
83 clk_disable(pltfm_host->clk);
84 clk_disable(sdhci_arasan->clk_ahb);
85
86 return 0;
87}
88
89/**
90 * sdhci_arasan_resume - Resume method for the driver
91 * @dev: Address of the device structure
92 * Returns 0 on success and error value on error
93 *
94 * Resume operation after suspend
95 */
96static int sdhci_arasan_resume(struct device *dev)
97{
98 struct platform_device *pdev = to_platform_device(dev);
99 struct sdhci_host *host = platform_get_drvdata(pdev);
100 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
101 struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
102 int ret;
103
104 ret = clk_enable(sdhci_arasan->clk_ahb);
105 if (ret) {
106 dev_err(dev, "Cannot enable AHB clock.\n");
107 return ret;
108 }
109
110 ret = clk_enable(pltfm_host->clk);
111 if (ret) {
112 dev_err(dev, "Cannot enable SD clock.\n");
113 clk_disable(sdhci_arasan->clk_ahb);
114 return ret;
115 }
116
117 return sdhci_resume_host(host);
118}
119#endif /* ! CONFIG_PM_SLEEP */
120
121static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
122 sdhci_arasan_resume);
123
124static int sdhci_arasan_probe(struct platform_device *pdev)
125{
126 int ret;
127 struct clk *clk_xin;
128 struct sdhci_host *host;
129 struct sdhci_pltfm_host *pltfm_host;
130 struct sdhci_arasan_data *sdhci_arasan;
131
132 sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan),
133 GFP_KERNEL);
134 if (!sdhci_arasan)
135 return -ENOMEM;
136
137 sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
138 if (IS_ERR(sdhci_arasan->clk_ahb)) {
139 dev_err(&pdev->dev, "clk_ahb clock not found.\n");
140 return PTR_ERR(sdhci_arasan->clk_ahb);
141 }
142
143 clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
144 if (IS_ERR(clk_xin)) {
145 dev_err(&pdev->dev, "clk_xin clock not found.\n");
146 return PTR_ERR(clk_xin);
147 }
148
149 ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
150 if (ret) {
151 dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
152 return ret;
153 }
154
155 ret = clk_prepare_enable(clk_xin);
156 if (ret) {
157 dev_err(&pdev->dev, "Unable to enable SD clock.\n");
158 goto clk_dis_ahb;
159 }
160
161 host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
162 if (IS_ERR(host)) {
163 ret = PTR_ERR(host);
164 dev_err(&pdev->dev, "platform init failed (%u)\n", ret);
165 goto clk_disable_all;
166 }
167
168 sdhci_get_of_property(pdev);
169 pltfm_host = sdhci_priv(host);
170 pltfm_host->priv = sdhci_arasan;
171 pltfm_host->clk = clk_xin;
172
173 ret = sdhci_add_host(host);
174 if (ret) {
175 dev_err(&pdev->dev, "platform register failed (%u)\n", ret);
176 goto err_pltfm_free;
177 }
178
179 return 0;
180
181err_pltfm_free:
182 sdhci_pltfm_free(pdev);
183clk_disable_all:
184 clk_disable_unprepare(clk_xin);
185clk_dis_ahb:
186 clk_disable_unprepare(sdhci_arasan->clk_ahb);
187
188 return ret;
189}
190
191static int sdhci_arasan_remove(struct platform_device *pdev)
192{
193 struct sdhci_host *host = platform_get_drvdata(pdev);
194 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
195 struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
196
197 clk_disable_unprepare(pltfm_host->clk);
198 clk_disable_unprepare(sdhci_arasan->clk_ahb);
199
200 return sdhci_pltfm_unregister(pdev);
201}
202
203static const struct of_device_id sdhci_arasan_of_match[] = {
204 { .compatible = "arasan,sdhci-8.9a" },
205 { }
206};
207MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
208
209static struct platform_driver sdhci_arasan_driver = {
210 .driver = {
211 .name = "sdhci-arasan",
212 .owner = THIS_MODULE,
213 .of_match_table = sdhci_arasan_of_match,
214 .pm = &sdhci_arasan_dev_pm_ops,
215 },
216 .probe = sdhci_arasan_probe,
217 .remove = sdhci_arasan_remove,
218};
219
220module_platform_driver(sdhci_arasan_driver);
221
222MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller");
223MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>");
224MODULE_LICENSE("GPL");