ahci: st: Add support for ST's SATA IP
[linux-drm-fsl-dcu.git] / drivers / ata / ahci_st.c
1 /*
2  * Copyright (C) 2012 STMicroelectronics Limited
3  *
4  * Authors: Francesco Virlinzi <francesco.virlinzi@st.com>
5  *          Alexandre Torgue <alexandre.torgue@st.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/export.h>
15 #include <linux/platform_device.h>
16 #include <linux/clk.h>
17 #include <linux/of.h>
18 #include <linux/ahci_platform.h>
19 #include <linux/phy/phy.h>
20 #include <linux/libata.h>
21 #include <linux/reset.h>
22 #include <linux/io.h>
23 #include <linux/dma-mapping.h>
24
25 #include "ahci.h"
26
27 #define ST_AHCI_OOBR                    0xbc
28 #define ST_AHCI_OOBR_WE                 BIT(31)
29 #define ST_AHCI_OOBR_CWMIN_SHIFT        24
30 #define ST_AHCI_OOBR_CWMAX_SHIFT        16
31 #define ST_AHCI_OOBR_CIMIN_SHIFT        8
32 #define ST_AHCI_OOBR_CIMAX_SHIFT        0
33
34 struct st_ahci_drv_data {
35         struct platform_device *ahci;
36         struct phy *phy;
37         struct reset_control *pwr;
38         struct reset_control *sw_rst;
39         struct reset_control *pwr_rst;
40         struct ahci_host_priv *hpriv;
41 };
42
43 static void st_ahci_configure_oob(void __iomem *mmio)
44 {
45         unsigned long old_val, new_val;
46
47         new_val = (0x02 << ST_AHCI_OOBR_CWMIN_SHIFT) |
48                   (0x04 << ST_AHCI_OOBR_CWMAX_SHIFT) |
49                   (0x08 << ST_AHCI_OOBR_CIMIN_SHIFT) |
50                   (0x0C << ST_AHCI_OOBR_CIMAX_SHIFT);
51
52         old_val = readl(mmio + ST_AHCI_OOBR);
53         writel(old_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
54         writel(new_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
55         writel(new_val, mmio + ST_AHCI_OOBR);
56 }
57
58 static int st_ahci_deassert_resets(struct device *dev)
59 {
60         struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
61         int err;
62
63         if (drv_data->pwr) {
64                 err = reset_control_deassert(drv_data->pwr);
65                 if (err) {
66                         dev_err(dev, "unable to bring out of pwrdwn\n");
67                         return err;
68                 }
69         }
70
71         st_ahci_configure_oob(drv_data->hpriv->mmio);
72
73         if (drv_data->sw_rst) {
74                 err = reset_control_deassert(drv_data->sw_rst);
75                 if (err) {
76                         dev_err(dev, "unable to bring out of sw-rst\n");
77                         return err;
78                 }
79         }
80
81         if (drv_data->pwr_rst) {
82                 err = reset_control_deassert(drv_data->pwr_rst);
83                 if (err) {
84                         dev_err(dev, "unable to bring out of pwr-rst\n");
85                         return err;
86                 }
87         }
88
89         return 0;
90 }
91
92 static int st_ahci_probe_resets(struct platform_device *pdev)
93 {
94         struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev);
95
96         drv_data->pwr = devm_reset_control_get(&pdev->dev, "pwr-dwn");
97         if (IS_ERR(drv_data->pwr)) {
98                 dev_info(&pdev->dev, "power reset control not defined\n");
99                 drv_data->pwr = NULL;
100         }
101
102         drv_data->sw_rst = devm_reset_control_get(&pdev->dev, "sw-rst");
103         if (IS_ERR(drv_data->sw_rst)) {
104                 dev_info(&pdev->dev, "soft reset control not defined\n");
105                 drv_data->sw_rst = NULL;
106         }
107
108         drv_data->pwr_rst = devm_reset_control_get(&pdev->dev, "pwr-rst");
109         if (IS_ERR(drv_data->pwr_rst)) {
110                 dev_dbg(&pdev->dev, "power soft reset control not defined\n");
111                 drv_data->pwr_rst = NULL;
112         }
113
114         return st_ahci_deassert_resets(&pdev->dev);
115 }
116
117 static const struct ata_port_info st_ahci_port_info = {
118         .flags          = AHCI_FLAG_COMMON,
119         .pio_mask       = ATA_PIO4,
120         .udma_mask      = ATA_UDMA6,
121         .port_ops       = &ahci_platform_ops,
122 };
123
124 static int st_ahci_probe(struct platform_device *pdev)
125 {
126         struct st_ahci_drv_data *drv_data;
127         struct ahci_host_priv *hpriv;
128         int err;
129
130         drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
131         if (!drv_data)
132                 return -ENOMEM;
133
134         platform_set_drvdata(pdev, drv_data);
135
136         hpriv = ahci_platform_get_resources(pdev);
137         if (IS_ERR(hpriv))
138                 return PTR_ERR(hpriv);
139
140         drv_data->hpriv = hpriv;
141
142         err = st_ahci_probe_resets(pdev);
143         if (err)
144                 return err;
145
146         err = ahci_platform_enable_resources(hpriv);
147         if (err)
148                 return err;
149
150         err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, 0, 0);
151         if (err) {
152                 ahci_platform_disable_resources(hpriv);
153                 return err;
154         }
155
156         return 0;
157 }
158
159 static int st_ahci_remove(struct platform_device *pdev)
160 {
161         struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev);
162         struct ahci_host_priv *hpriv = drv_data->hpriv;
163         int err;
164
165         if (drv_data->pwr) {
166                 err = reset_control_assert(drv_data->pwr);
167                 if (err)
168                         dev_err(&pdev->dev, "unable to pwrdwn\n");
169         }
170
171         ahci_platform_disable_resources(hpriv);
172
173         return 0;
174 }
175
176 #ifdef CONFIG_PM_SLEEP
177 static int st_ahci_suspend(struct device *dev)
178 {
179         struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
180         struct ahci_host_priv *hpriv = drv_data->hpriv;
181         int err;
182
183         if (drv_data->pwr) {
184                 err = reset_control_assert(drv_data->pwr);
185                 if (err) {
186                         dev_err(dev, "unable to pwrdwn");
187                         return err;
188                 }
189         }
190
191         ahci_platform_disable_resources(hpriv);
192
193         return 0;
194 }
195
196 static int st_ahci_resume(struct device *dev)
197 {
198         struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
199         struct ahci_host_priv *hpriv = drv_data->hpriv;
200         int err;
201
202         err = ahci_platform_enable_resources(hpriv);
203         if (err)
204                 return err;
205
206         err = st_ahci_deassert_resets(dev);
207         if (err) {
208                 ahci_platform_disable_resources(hpriv);
209                 return err;
210         }
211
212         return 0;
213 }
214 #endif
215
216 static SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume);
217
218 static struct of_device_id st_ahci_match[] = {
219         { .compatible = "st,ahci", },
220         {},
221 };
222 MODULE_DEVICE_TABLE(of, st_ahci_match);
223
224 static struct platform_driver st_ahci_driver = {
225         .driver = {
226                 .name = "st_ahci",
227                 .owner = THIS_MODULE,
228                 .pm = &st_ahci_pm_ops,
229                 .of_match_table = of_match_ptr(st_ahci_match),
230         },
231         .probe = st_ahci_probe,
232         .remove = st_ahci_remove,
233 };
234 module_platform_driver(st_ahci_driver);
235
236 MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
237 MODULE_AUTHOR("Francesco Virlinzi <francesco.virlinzi@st.com>");
238 MODULE_DESCRIPTION("STMicroelectronics Sata Ahci driver");
239 MODULE_LICENSE("GPL v2");