diff options
author | Hans de Goede <hdegoede@redhat.com> | 2014-02-22 10:53:31 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2014-02-22 15:35:41 -0500 |
commit | 156c5887948cd191417f18026aab9ce26e5a95da (patch) | |
tree | 65100ae8c89fec69d8f949ceb9b9b413a6af3468 | |
parent | 039ece38da45f5e6a94be3aa7611cf3634bc2461 (diff) |
ahci-platform: Add support for devices with more then 1 clock
The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the
imx AHCI controller needs 3 clocks to be enabled.
tj: Minor comment formatting updates.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r-- | Documentation/devicetree/bindings/ata/ahci-platform.txt | 1 | ||||
-rw-r--r-- | drivers/ata/ahci.h | 3 | ||||
-rw-r--r-- | drivers/ata/ahci_platform.c | 113 | ||||
-rw-r--r-- | include/linux/ahci_platform.h | 4 |
4 files changed, 93 insertions, 28 deletions
diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt index 89de1564950c..3ced07dc8f56 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.txt +++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt | |||
@@ -10,6 +10,7 @@ Required properties: | |||
10 | 10 | ||
11 | Optional properties: | 11 | Optional properties: |
12 | - dma-coherent : Present if dma operations are coherent | 12 | - dma-coherent : Present if dma operations are coherent |
13 | - clocks : a list of phandle + clock specifier pairs | ||
13 | 14 | ||
14 | Example: | 15 | Example: |
15 | sata@ffe08000 { | 16 | sata@ffe08000 { |
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 64d1a99de5e4..c12862bd48ee 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h | |||
@@ -51,6 +51,7 @@ | |||
51 | 51 | ||
52 | enum { | 52 | enum { |
53 | AHCI_MAX_PORTS = 32, | 53 | AHCI_MAX_PORTS = 32, |
54 | AHCI_MAX_CLKS = 3, | ||
54 | AHCI_MAX_SG = 168, /* hardware max is 64K */ | 55 | AHCI_MAX_SG = 168, /* hardware max is 64K */ |
55 | AHCI_DMA_BOUNDARY = 0xffffffff, | 56 | AHCI_DMA_BOUNDARY = 0xffffffff, |
56 | AHCI_MAX_CMDS = 32, | 57 | AHCI_MAX_CMDS = 32, |
@@ -321,7 +322,7 @@ struct ahci_host_priv { | |||
321 | u32 em_loc; /* enclosure management location */ | 322 | u32 em_loc; /* enclosure management location */ |
322 | u32 em_buf_sz; /* EM buffer size in byte */ | 323 | u32 em_buf_sz; /* EM buffer size in byte */ |
323 | u32 em_msg_type; /* EM message type */ | 324 | u32 em_msg_type; /* EM message type */ |
324 | struct clk *clk; /* Only for platforms supporting clk */ | 325 | struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ |
325 | void *plat_data; /* Other platform data */ | 326 | void *plat_data; /* Other platform data */ |
326 | /* | 327 | /* |
327 | * Optional ahci_start_engine override, if not set this gets set to the | 328 | * Optional ahci_start_engine override, if not set this gets set to the |
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 9302d8143393..8d5a9a7ce958 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c | |||
@@ -86,6 +86,60 @@ static struct scsi_host_template ahci_platform_sht = { | |||
86 | AHCI_SHT("ahci_platform"), | 86 | AHCI_SHT("ahci_platform"), |
87 | }; | 87 | }; |
88 | 88 | ||
89 | /** | ||
90 | * ahci_platform_enable_clks - Enable platform clocks | ||
91 | * @hpriv: host private area to store config values | ||
92 | * | ||
93 | * This function enables all the clks found in hpriv->clks, starting at | ||
94 | * index 0. If any clk fails to enable it disables all the clks already | ||
95 | * enabled in reverse order, and then returns an error. | ||
96 | * | ||
97 | * RETURNS: | ||
98 | * 0 on success otherwise a negative error code | ||
99 | */ | ||
100 | int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) | ||
101 | { | ||
102 | int c, rc; | ||
103 | |||
104 | for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { | ||
105 | rc = clk_prepare_enable(hpriv->clks[c]); | ||
106 | if (rc) | ||
107 | goto disable_unprepare_clk; | ||
108 | } | ||
109 | return 0; | ||
110 | |||
111 | disable_unprepare_clk: | ||
112 | while (--c >= 0) | ||
113 | clk_disable_unprepare(hpriv->clks[c]); | ||
114 | return rc; | ||
115 | } | ||
116 | EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); | ||
117 | |||
118 | /** | ||
119 | * ahci_platform_disable_clks - Disable platform clocks | ||
120 | * @hpriv: host private area to store config values | ||
121 | * | ||
122 | * This function disables all the clks found in hpriv->clks, in reverse | ||
123 | * order of ahci_platform_enable_clks (starting at the end of the array). | ||
124 | */ | ||
125 | void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) | ||
126 | { | ||
127 | int c; | ||
128 | |||
129 | for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) | ||
130 | if (hpriv->clks[c]) | ||
131 | clk_disable_unprepare(hpriv->clks[c]); | ||
132 | } | ||
133 | EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); | ||
134 | |||
135 | static void ahci_put_clks(struct ahci_host_priv *hpriv) | ||
136 | { | ||
137 | int c; | ||
138 | |||
139 | for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) | ||
140 | clk_put(hpriv->clks[c]); | ||
141 | } | ||
142 | |||
89 | static int ahci_probe(struct platform_device *pdev) | 143 | static int ahci_probe(struct platform_device *pdev) |
90 | { | 144 | { |
91 | struct device *dev = &pdev->dev; | 145 | struct device *dev = &pdev->dev; |
@@ -96,6 +150,7 @@ static int ahci_probe(struct platform_device *pdev) | |||
96 | struct ahci_host_priv *hpriv; | 150 | struct ahci_host_priv *hpriv; |
97 | struct ata_host *host; | 151 | struct ata_host *host; |
98 | struct resource *mem; | 152 | struct resource *mem; |
153 | struct clk *clk; | ||
99 | int irq; | 154 | int irq; |
100 | int n_ports; | 155 | int n_ports; |
101 | int i; | 156 | int i; |
@@ -130,17 +185,31 @@ static int ahci_probe(struct platform_device *pdev) | |||
130 | return -ENOMEM; | 185 | return -ENOMEM; |
131 | } | 186 | } |
132 | 187 | ||
133 | hpriv->clk = clk_get(dev, NULL); | 188 | for (i = 0; i < AHCI_MAX_CLKS; i++) { |
134 | if (IS_ERR(hpriv->clk)) { | 189 | /* |
135 | dev_err(dev, "can't get clock\n"); | 190 | * For now we must use clk_get(dev, NULL) for the first clock, |
136 | } else { | 191 | * because some platforms (da850, spear13xx) are not yet |
137 | rc = clk_prepare_enable(hpriv->clk); | 192 | * converted to use devicetree for clocks. For new platforms |
138 | if (rc) { | 193 | * this is equivalent to of_clk_get(dev->of_node, 0). |
139 | dev_err(dev, "clock prepare enable failed"); | 194 | */ |
140 | goto free_clk; | 195 | if (i == 0) |
196 | clk = clk_get(dev, NULL); | ||
197 | else | ||
198 | clk = of_clk_get(dev->of_node, i); | ||
199 | |||
200 | if (IS_ERR(clk)) { | ||
201 | rc = PTR_ERR(clk); | ||
202 | if (rc == -EPROBE_DEFER) | ||
203 | goto free_clk; | ||
204 | break; | ||
141 | } | 205 | } |
206 | hpriv->clks[i] = clk; | ||
142 | } | 207 | } |
143 | 208 | ||
209 | rc = ahci_enable_clks(dev, hpriv); | ||
210 | if (rc) | ||
211 | goto free_clk; | ||
212 | |||
144 | /* | 213 | /* |
145 | * Some platforms might need to prepare for mmio region access, | 214 | * Some platforms might need to prepare for mmio region access, |
146 | * which could be done in the following init call. So, the mmio | 215 | * which could be done in the following init call. So, the mmio |
@@ -221,11 +290,9 @@ pdata_exit: | |||
221 | if (pdata && pdata->exit) | 290 | if (pdata && pdata->exit) |
222 | pdata->exit(dev); | 291 | pdata->exit(dev); |
223 | disable_unprepare_clk: | 292 | disable_unprepare_clk: |
224 | if (!IS_ERR(hpriv->clk)) | 293 | ahci_disable_clks(hpriv); |
225 | clk_disable_unprepare(hpriv->clk); | ||
226 | free_clk: | 294 | free_clk: |
227 | if (!IS_ERR(hpriv->clk)) | 295 | ahci_put_clks(hpriv); |
228 | clk_put(hpriv->clk); | ||
229 | return rc; | 296 | return rc; |
230 | } | 297 | } |
231 | 298 | ||
@@ -238,10 +305,8 @@ static void ahci_host_stop(struct ata_host *host) | |||
238 | if (pdata && pdata->exit) | 305 | if (pdata && pdata->exit) |
239 | pdata->exit(dev); | 306 | pdata->exit(dev); |
240 | 307 | ||
241 | if (!IS_ERR(hpriv->clk)) { | 308 | ahci_disable_clks(hpriv); |
242 | clk_disable_unprepare(hpriv->clk); | 309 | ahci_put_clks(hpriv); |
243 | clk_put(hpriv->clk); | ||
244 | } | ||
245 | } | 310 | } |
246 | 311 | ||
247 | #ifdef CONFIG_PM_SLEEP | 312 | #ifdef CONFIG_PM_SLEEP |
@@ -276,8 +341,7 @@ static int ahci_suspend(struct device *dev) | |||
276 | if (pdata && pdata->suspend) | 341 | if (pdata && pdata->suspend) |
277 | return pdata->suspend(dev); | 342 | return pdata->suspend(dev); |
278 | 343 | ||
279 | if (!IS_ERR(hpriv->clk)) | 344 | ahci_disable_clks(hpriv); |
280 | clk_disable_unprepare(hpriv->clk); | ||
281 | 345 | ||
282 | return 0; | 346 | return 0; |
283 | } | 347 | } |
@@ -289,13 +353,9 @@ static int ahci_resume(struct device *dev) | |||
289 | struct ahci_host_priv *hpriv = host->private_data; | 353 | struct ahci_host_priv *hpriv = host->private_data; |
290 | int rc; | 354 | int rc; |
291 | 355 | ||
292 | if (!IS_ERR(hpriv->clk)) { | 356 | rc = ahci_enable_clks(dev, hpriv); |
293 | rc = clk_prepare_enable(hpriv->clk); | 357 | if (rc) |
294 | if (rc) { | 358 | return rc; |
295 | dev_err(dev, "clock prepare enable failed"); | ||
296 | return rc; | ||
297 | } | ||
298 | } | ||
299 | 359 | ||
300 | if (pdata && pdata->resume) { | 360 | if (pdata && pdata->resume) { |
301 | rc = pdata->resume(dev); | 361 | rc = pdata->resume(dev); |
@@ -316,8 +376,7 @@ static int ahci_resume(struct device *dev) | |||
316 | return 0; | 376 | return 0; |
317 | 377 | ||
318 | disable_unprepare_clk: | 378 | disable_unprepare_clk: |
319 | if (!IS_ERR(hpriv->clk)) | 379 | ahci_disable_clks(hpriv); |
320 | clk_disable_unprepare(hpriv->clk); | ||
321 | 380 | ||
322 | return rc; | 381 | return rc; |
323 | } | 382 | } |
diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 73a25005d88a..769d06525320 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | struct device; | 20 | struct device; |
21 | struct ata_port_info; | 21 | struct ata_port_info; |
22 | struct ahci_host_priv; | ||
22 | 23 | ||
23 | struct ahci_platform_data { | 24 | struct ahci_platform_data { |
24 | int (*init)(struct device *dev, void __iomem *addr); | 25 | int (*init)(struct device *dev, void __iomem *addr); |
@@ -30,4 +31,7 @@ struct ahci_platform_data { | |||
30 | unsigned int mask_port_map; | 31 | unsigned int mask_port_map; |
31 | }; | 32 | }; |
32 | 33 | ||
34 | int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); | ||
35 | void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); | ||
36 | |||
33 | #endif /* _AHCI_PLATFORM_H */ | 37 | #endif /* _AHCI_PLATFORM_H */ |