diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/Kconfig | 9 | ||||
-rw-r--r-- | drivers/ata/Makefile | 1 | ||||
-rw-r--r-- | drivers/ata/ahci_st.c | 239 |
3 files changed, 249 insertions, 0 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 2e6a6f790327..46258ead19aa 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig | |||
@@ -97,6 +97,15 @@ config SATA_AHCI_PLATFORM | |||
97 | 97 | ||
98 | If unsure, say N. | 98 | If unsure, say N. |
99 | 99 | ||
100 | config SATA_AHCI_ST | ||
101 | tristate "ST SATA support" | ||
102 | depends on SATA_AHCI_PLATFORM | ||
103 | select GENERIC_PHY | ||
104 | help | ||
105 | This option enables support for ST SATA controller. | ||
106 | |||
107 | If unsure, say N. | ||
108 | |||
100 | config AHCI_IMX | 109 | config AHCI_IMX |
101 | tristate "Freescale i.MX AHCI SATA support" | 110 | tristate "Freescale i.MX AHCI SATA support" |
102 | depends on SATA_AHCI_PLATFORM && MFD_SYSCON | 111 | depends on SATA_AHCI_PLATFORM && MFD_SYSCON |
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 246050b4c198..6bbd6dadef50 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile | |||
@@ -5,6 +5,7 @@ obj-$(CONFIG_ATA) += libata.o | |||
5 | obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o | 5 | obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o |
6 | obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o | 6 | obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o |
7 | obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o | 7 | obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o |
8 | obj-$(CONFIG_SATA_AHCI_ST) += ahci_st.o | ||
8 | obj-$(CONFIG_SATA_FSL) += sata_fsl.o | 9 | obj-$(CONFIG_SATA_FSL) += sata_fsl.o |
9 | obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o | 10 | obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o |
10 | obj-$(CONFIG_SATA_SIL24) += sata_sil24.o | 11 | obj-$(CONFIG_SATA_SIL24) += sata_sil24.o |
diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c new file mode 100644 index 000000000000..2f951332227f --- /dev/null +++ b/drivers/ata/ahci_st.c | |||
@@ -0,0 +1,239 @@ | |||
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"); | ||