09c4f220b48ad274af2a25de6f0f1eb41510da62
[openwrt/staging/luka.git] / target / linux / ipq806x / patches / 0110-ahci-platform-Add-support-for-devices-with-more-then.patch
1 From b7ad421c184b827806c7f65be3a980cfc855b589 Mon Sep 17 00:00:00 2001
2 From: Hans de Goede <hdegoede@redhat.com>
3 Date: Sat, 22 Feb 2014 16:53:31 +0100
4 Subject: [PATCH 110/182] ahci-platform: Add support for devices with more
5 then 1 clock
6
7 The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the
8 imx AHCI controller needs 3 clocks to be enabled.
9
10 tj: Minor comment formatting updates.
11
12 Signed-off-by: Hans de Goede <hdegoede@redhat.com>
13 Signed-off-by: Tejun Heo <tj@kernel.org>
14 ---
15 .../devicetree/bindings/ata/ahci-platform.txt | 1 +
16 drivers/ata/ahci.h | 3 +-
17 drivers/ata/ahci_platform.c | 113 +++++++++++++++-----
18 include/linux/ahci_platform.h | 4 +
19 4 files changed, 93 insertions(+), 28 deletions(-)
20
21 diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt
22 index 89de156..3ced07d 100644
23 --- a/Documentation/devicetree/bindings/ata/ahci-platform.txt
24 +++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt
25 @@ -10,6 +10,7 @@ Required properties:
26
27 Optional properties:
28 - dma-coherent : Present if dma operations are coherent
29 +- clocks : a list of phandle + clock specifier pairs
30
31 Example:
32 sata@ffe08000 {
33 diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
34 index 64d1a99..c12862b 100644
35 --- a/drivers/ata/ahci.h
36 +++ b/drivers/ata/ahci.h
37 @@ -51,6 +51,7 @@
38
39 enum {
40 AHCI_MAX_PORTS = 32,
41 + AHCI_MAX_CLKS = 3,
42 AHCI_MAX_SG = 168, /* hardware max is 64K */
43 AHCI_DMA_BOUNDARY = 0xffffffff,
44 AHCI_MAX_CMDS = 32,
45 @@ -321,7 +322,7 @@ struct ahci_host_priv {
46 u32 em_loc; /* enclosure management location */
47 u32 em_buf_sz; /* EM buffer size in byte */
48 u32 em_msg_type; /* EM message type */
49 - struct clk *clk; /* Only for platforms supporting clk */
50 + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
51 void *plat_data; /* Other platform data */
52 /*
53 * Optional ahci_start_engine override, if not set this gets set to the
54 diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
55 index 4b231ba..2342a42 100644
56 --- a/drivers/ata/ahci_platform.c
57 +++ b/drivers/ata/ahci_platform.c
58 @@ -87,6 +87,60 @@ static struct scsi_host_template ahci_platform_sht = {
59 AHCI_SHT("ahci_platform"),
60 };
61
62 +/**
63 + * ahci_platform_enable_clks - Enable platform clocks
64 + * @hpriv: host private area to store config values
65 + *
66 + * This function enables all the clks found in hpriv->clks, starting at
67 + * index 0. If any clk fails to enable it disables all the clks already
68 + * enabled in reverse order, and then returns an error.
69 + *
70 + * RETURNS:
71 + * 0 on success otherwise a negative error code
72 + */
73 +int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
74 +{
75 + int c, rc;
76 +
77 + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
78 + rc = clk_prepare_enable(hpriv->clks[c]);
79 + if (rc)
80 + goto disable_unprepare_clk;
81 + }
82 + return 0;
83 +
84 +disable_unprepare_clk:
85 + while (--c >= 0)
86 + clk_disable_unprepare(hpriv->clks[c]);
87 + return rc;
88 +}
89 +EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
90 +
91 +/**
92 + * ahci_platform_disable_clks - Disable platform clocks
93 + * @hpriv: host private area to store config values
94 + *
95 + * This function disables all the clks found in hpriv->clks, in reverse
96 + * order of ahci_platform_enable_clks (starting at the end of the array).
97 + */
98 +void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
99 +{
100 + int c;
101 +
102 + for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
103 + if (hpriv->clks[c])
104 + clk_disable_unprepare(hpriv->clks[c]);
105 +}
106 +EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
107 +
108 +static void ahci_put_clks(struct ahci_host_priv *hpriv)
109 +{
110 + int c;
111 +
112 + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
113 + clk_put(hpriv->clks[c]);
114 +}
115 +
116 static int ahci_probe(struct platform_device *pdev)
117 {
118 struct device *dev = &pdev->dev;
119 @@ -97,6 +151,7 @@ static int ahci_probe(struct platform_device *pdev)
120 struct ahci_host_priv *hpriv;
121 struct ata_host *host;
122 struct resource *mem;
123 + struct clk *clk;
124 int irq;
125 int n_ports;
126 int i;
127 @@ -131,17 +186,31 @@ static int ahci_probe(struct platform_device *pdev)
128 return -ENOMEM;
129 }
130
131 - hpriv->clk = clk_get(dev, NULL);
132 - if (IS_ERR(hpriv->clk)) {
133 - dev_err(dev, "can't get clock\n");
134 - } else {
135 - rc = clk_prepare_enable(hpriv->clk);
136 - if (rc) {
137 - dev_err(dev, "clock prepare enable failed");
138 - goto free_clk;
139 + for (i = 0; i < AHCI_MAX_CLKS; i++) {
140 + /*
141 + * For now we must use clk_get(dev, NULL) for the first clock,
142 + * because some platforms (da850, spear13xx) are not yet
143 + * converted to use devicetree for clocks. For new platforms
144 + * this is equivalent to of_clk_get(dev->of_node, 0).
145 + */
146 + if (i == 0)
147 + clk = clk_get(dev, NULL);
148 + else
149 + clk = of_clk_get(dev->of_node, i);
150 +
151 + if (IS_ERR(clk)) {
152 + rc = PTR_ERR(clk);
153 + if (rc == -EPROBE_DEFER)
154 + goto free_clk;
155 + break;
156 }
157 + hpriv->clks[i] = clk;
158 }
159
160 + rc = ahci_enable_clks(dev, hpriv);
161 + if (rc)
162 + goto free_clk;
163 +
164 /*
165 * Some platforms might need to prepare for mmio region access,
166 * which could be done in the following init call. So, the mmio
167 @@ -222,11 +291,9 @@ pdata_exit:
168 if (pdata && pdata->exit)
169 pdata->exit(dev);
170 disable_unprepare_clk:
171 - if (!IS_ERR(hpriv->clk))
172 - clk_disable_unprepare(hpriv->clk);
173 + ahci_disable_clks(hpriv);
174 free_clk:
175 - if (!IS_ERR(hpriv->clk))
176 - clk_put(hpriv->clk);
177 + ahci_put_clks(hpriv);
178 return rc;
179 }
180
181 @@ -239,10 +306,8 @@ static void ahci_host_stop(struct ata_host *host)
182 if (pdata && pdata->exit)
183 pdata->exit(dev);
184
185 - if (!IS_ERR(hpriv->clk)) {
186 - clk_disable_unprepare(hpriv->clk);
187 - clk_put(hpriv->clk);
188 - }
189 + ahci_disable_clks(hpriv);
190 + ahci_put_clks(hpriv);
191 }
192
193 #ifdef CONFIG_PM_SLEEP
194 @@ -277,8 +342,7 @@ static int ahci_suspend(struct device *dev)
195 if (pdata && pdata->suspend)
196 return pdata->suspend(dev);
197
198 - if (!IS_ERR(hpriv->clk))
199 - clk_disable_unprepare(hpriv->clk);
200 + ahci_disable_clks(hpriv);
201
202 return 0;
203 }
204 @@ -290,13 +354,9 @@ static int ahci_resume(struct device *dev)
205 struct ahci_host_priv *hpriv = host->private_data;
206 int rc;
207
208 - if (!IS_ERR(hpriv->clk)) {
209 - rc = clk_prepare_enable(hpriv->clk);
210 - if (rc) {
211 - dev_err(dev, "clock prepare enable failed");
212 - return rc;
213 - }
214 - }
215 + rc = ahci_enable_clks(dev, hpriv);
216 + if (rc)
217 + return rc;
218
219 if (pdata && pdata->resume) {
220 rc = pdata->resume(dev);
221 @@ -317,8 +377,7 @@ static int ahci_resume(struct device *dev)
222 return 0;
223
224 disable_unprepare_clk:
225 - if (!IS_ERR(hpriv->clk))
226 - clk_disable_unprepare(hpriv->clk);
227 + ahci_disable_clks(hpriv);
228
229 return rc;
230 }
231 diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h
232 index 73a2500..769d065 100644
233 --- a/include/linux/ahci_platform.h
234 +++ b/include/linux/ahci_platform.h
235 @@ -19,6 +19,7 @@
236
237 struct device;
238 struct ata_port_info;
239 +struct ahci_host_priv;
240
241 struct ahci_platform_data {
242 int (*init)(struct device *dev, void __iomem *addr);
243 @@ -30,4 +31,7 @@ struct ahci_platform_data {
244 unsigned int mask_port_map;
245 };
246
247 +int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
248 +void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
249 +
250 #endif /* _AHCI_PLATFORM_H */
251 --
252 1.7.10.4
253