Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/ata/ahci_st.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2012 STMicroelectronics Limited
4
*
5
* Authors: Francesco Virlinzi <[email protected]>
6
* Alexandre Torgue <[email protected]>
7
*/
8
9
#include <linux/init.h>
10
#include <linux/module.h>
11
#include <linux/export.h>
12
#include <linux/platform_device.h>
13
#include <linux/clk.h>
14
#include <linux/of.h>
15
#include <linux/ahci_platform.h>
16
#include <linux/libata.h>
17
#include <linux/reset.h>
18
#include <linux/io.h>
19
#include <linux/dma-mapping.h>
20
21
#include "ahci.h"
22
23
#define DRV_NAME "st_ahci"
24
25
#define ST_AHCI_OOBR 0xbc
26
#define ST_AHCI_OOBR_WE BIT(31)
27
#define ST_AHCI_OOBR_CWMIN_SHIFT 24
28
#define ST_AHCI_OOBR_CWMAX_SHIFT 16
29
#define ST_AHCI_OOBR_CIMIN_SHIFT 8
30
#define ST_AHCI_OOBR_CIMAX_SHIFT 0
31
32
struct st_ahci_drv_data {
33
struct reset_control *pwr;
34
struct reset_control *sw_rst;
35
struct reset_control *pwr_rst;
36
};
37
38
static void st_ahci_configure_oob(void __iomem *mmio)
39
{
40
unsigned long old_val, new_val;
41
42
new_val = (0x02 << ST_AHCI_OOBR_CWMIN_SHIFT) |
43
(0x04 << ST_AHCI_OOBR_CWMAX_SHIFT) |
44
(0x08 << ST_AHCI_OOBR_CIMIN_SHIFT) |
45
(0x0C << ST_AHCI_OOBR_CIMAX_SHIFT);
46
47
old_val = readl(mmio + ST_AHCI_OOBR);
48
writel(old_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
49
writel(new_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR);
50
writel(new_val, mmio + ST_AHCI_OOBR);
51
}
52
53
static int st_ahci_deassert_resets(struct ahci_host_priv *hpriv,
54
struct device *dev)
55
{
56
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
57
int err;
58
59
if (drv_data->pwr) {
60
err = reset_control_deassert(drv_data->pwr);
61
if (err) {
62
dev_err(dev, "unable to bring out of pwrdwn\n");
63
return err;
64
}
65
}
66
67
if (drv_data->sw_rst) {
68
err = reset_control_deassert(drv_data->sw_rst);
69
if (err) {
70
dev_err(dev, "unable to bring out of sw-rst\n");
71
return err;
72
}
73
}
74
75
if (drv_data->pwr_rst) {
76
err = reset_control_deassert(drv_data->pwr_rst);
77
if (err) {
78
dev_err(dev, "unable to bring out of pwr-rst\n");
79
return err;
80
}
81
}
82
83
return 0;
84
}
85
86
static void st_ahci_host_stop(struct ata_host *host)
87
{
88
struct ahci_host_priv *hpriv = host->private_data;
89
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
90
struct device *dev = host->dev;
91
int err;
92
93
if (drv_data->pwr) {
94
err = reset_control_assert(drv_data->pwr);
95
if (err)
96
dev_err(dev, "unable to pwrdwn\n");
97
}
98
99
ahci_platform_disable_resources(hpriv);
100
}
101
102
static int st_ahci_probe_resets(struct ahci_host_priv *hpriv,
103
struct device *dev)
104
{
105
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
106
107
drv_data->pwr = devm_reset_control_get(dev, "pwr-dwn");
108
if (IS_ERR(drv_data->pwr)) {
109
dev_info(dev, "power reset control not defined\n");
110
drv_data->pwr = NULL;
111
}
112
113
drv_data->sw_rst = devm_reset_control_get(dev, "sw-rst");
114
if (IS_ERR(drv_data->sw_rst)) {
115
dev_info(dev, "soft reset control not defined\n");
116
drv_data->sw_rst = NULL;
117
}
118
119
drv_data->pwr_rst = devm_reset_control_get(dev, "pwr-rst");
120
if (IS_ERR(drv_data->pwr_rst)) {
121
dev_dbg(dev, "power soft reset control not defined\n");
122
drv_data->pwr_rst = NULL;
123
}
124
125
return st_ahci_deassert_resets(hpriv, dev);
126
}
127
128
static struct ata_port_operations st_ahci_port_ops = {
129
.inherits = &ahci_platform_ops,
130
.host_stop = st_ahci_host_stop,
131
};
132
133
static const struct ata_port_info st_ahci_port_info = {
134
.flags = AHCI_FLAG_COMMON,
135
.pio_mask = ATA_PIO4,
136
.udma_mask = ATA_UDMA6,
137
.port_ops = &st_ahci_port_ops,
138
};
139
140
static const struct scsi_host_template ahci_platform_sht = {
141
AHCI_SHT(DRV_NAME),
142
};
143
144
static int st_ahci_probe(struct platform_device *pdev)
145
{
146
struct st_ahci_drv_data *drv_data;
147
struct ahci_host_priv *hpriv;
148
int err;
149
150
drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
151
if (!drv_data)
152
return -ENOMEM;
153
154
hpriv = ahci_platform_get_resources(pdev, 0);
155
if (IS_ERR(hpriv))
156
return PTR_ERR(hpriv);
157
hpriv->plat_data = drv_data;
158
159
err = st_ahci_probe_resets(hpriv, &pdev->dev);
160
if (err)
161
return err;
162
163
err = ahci_platform_enable_resources(hpriv);
164
if (err)
165
return err;
166
167
st_ahci_configure_oob(hpriv->mmio);
168
169
err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
170
&ahci_platform_sht);
171
if (err) {
172
ahci_platform_disable_resources(hpriv);
173
return err;
174
}
175
176
return 0;
177
}
178
179
static int st_ahci_suspend(struct device *dev)
180
{
181
struct ata_host *host = dev_get_drvdata(dev);
182
struct ahci_host_priv *hpriv = host->private_data;
183
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
184
int err;
185
186
err = ahci_platform_suspend_host(dev);
187
if (err)
188
return err;
189
190
if (drv_data->pwr) {
191
err = reset_control_assert(drv_data->pwr);
192
if (err) {
193
dev_err(dev, "unable to pwrdwn");
194
return err;
195
}
196
}
197
198
ahci_platform_disable_resources(hpriv);
199
200
return 0;
201
}
202
203
static int st_ahci_resume(struct device *dev)
204
{
205
struct ata_host *host = dev_get_drvdata(dev);
206
struct ahci_host_priv *hpriv = host->private_data;
207
int err;
208
209
err = ahci_platform_enable_resources(hpriv);
210
if (err)
211
return err;
212
213
err = st_ahci_deassert_resets(hpriv, dev);
214
if (err) {
215
ahci_platform_disable_resources(hpriv);
216
return err;
217
}
218
219
st_ahci_configure_oob(hpriv->mmio);
220
221
return ahci_platform_resume_host(dev);
222
}
223
224
static DEFINE_SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume);
225
226
static const struct of_device_id st_ahci_match[] = {
227
{ .compatible = "st,ahci", },
228
{ /* sentinel */ }
229
};
230
MODULE_DEVICE_TABLE(of, st_ahci_match);
231
232
static struct platform_driver st_ahci_driver = {
233
.driver = {
234
.name = DRV_NAME,
235
.pm = pm_sleep_ptr(&st_ahci_pm_ops),
236
.of_match_table = st_ahci_match,
237
},
238
.probe = st_ahci_probe,
239
.remove = ata_platform_remove_one,
240
};
241
module_platform_driver(st_ahci_driver);
242
243
MODULE_AUTHOR("Alexandre Torgue <[email protected]>");
244
MODULE_AUTHOR("Francesco Virlinzi <[email protected]>");
245
MODULE_DESCRIPTION("STMicroelectronics SATA AHCI Driver");
246
MODULE_LICENSE("GPL v2");
247
248