2 * copyright (c) 2013 Freescale Semiconductor, Inc.
3 * Freescale IMX AHCI SATA platform driver
5 * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/regmap.h>
24 #include <linux/ahci_platform.h>
25 #include <linux/of_device.h>
26 #include <linux/mfd/syscon.h>
27 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
28 #include <linux/libata.h>
32 PORT_PHY_CTL
= 0x178, /* Port0 PHY Control */
33 PORT_PHY_CTL_PDDQ_LOC
= 0x100000, /* PORT_PHY_CTL bits */
34 HOST_TIMER1MS
= 0xe0, /* Timer 1-ms */
42 struct imx_ahci_priv
{
43 struct platform_device
*ahci_pdev
;
44 enum ahci_imx_type type
;
51 static int ahci_imx_hotplug
;
52 module_param_named(hotplug
, ahci_imx_hotplug
, int, 0644);
53 MODULE_PARM_DESC(hotplug
, "AHCI IMX hot-plug support (0=Don't support, 1=support)");
55 static void ahci_imx_host_stop(struct ata_host
*host
);
57 static int imx_sata_enable(struct ahci_host_priv
*hpriv
)
59 struct imx_ahci_priv
*imxpriv
= hpriv
->plat_data
;
62 if (imxpriv
->no_device
)
65 if (hpriv
->target_pwr
) {
66 ret
= regulator_enable(hpriv
->target_pwr
);
71 ret
= ahci_platform_enable_clks(hpriv
);
73 goto disable_regulator
;
75 if (imxpriv
->type
== AHCI_IMX6Q
) {
77 * set PHY Paremeters, two steps to configure the GPR13,
78 * one write for rest of parameters, mask of first write
79 * is 0x07ffffff, and the other one write for setting
82 regmap_update_bits(imxpriv
->gpr
, IOMUXC_GPR13
,
83 IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK
|
84 IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK
|
85 IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK
|
86 IMX6Q_GPR13_SATA_SPD_MODE_MASK
|
87 IMX6Q_GPR13_SATA_MPLL_SS_EN
|
88 IMX6Q_GPR13_SATA_TX_ATTEN_MASK
|
89 IMX6Q_GPR13_SATA_TX_BOOST_MASK
|
90 IMX6Q_GPR13_SATA_TX_LVL_MASK
|
91 IMX6Q_GPR13_SATA_MPLL_CLK_EN
|
92 IMX6Q_GPR13_SATA_TX_EDGE_RATE
,
93 IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB
|
94 IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M
|
95 IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F
|
96 IMX6Q_GPR13_SATA_SPD_MODE_3P0G
|
97 IMX6Q_GPR13_SATA_MPLL_SS_EN
|
98 IMX6Q_GPR13_SATA_TX_ATTEN_9_16
|
99 IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB
|
100 IMX6Q_GPR13_SATA_TX_LVL_1_025_V
);
101 regmap_update_bits(imxpriv
->gpr
, IOMUXC_GPR13
,
102 IMX6Q_GPR13_SATA_MPLL_CLK_EN
,
103 IMX6Q_GPR13_SATA_MPLL_CLK_EN
);
106 usleep_range(1000, 2000);
111 if (hpriv
->target_pwr
)
112 regulator_disable(hpriv
->target_pwr
);
117 static void imx_sata_disable(struct ahci_host_priv
*hpriv
)
119 struct imx_ahci_priv
*imxpriv
= hpriv
->plat_data
;
121 if (imxpriv
->no_device
)
124 if (imxpriv
->type
== AHCI_IMX6Q
) {
125 regmap_update_bits(imxpriv
->gpr
, IOMUXC_GPR13
,
126 IMX6Q_GPR13_SATA_MPLL_CLK_EN
,
127 !IMX6Q_GPR13_SATA_MPLL_CLK_EN
);
130 ahci_platform_disable_clks(hpriv
);
132 if (hpriv
->target_pwr
)
133 regulator_disable(hpriv
->target_pwr
);
136 static void ahci_imx_error_handler(struct ata_port
*ap
)
139 struct ata_device
*dev
;
140 struct ata_host
*host
= dev_get_drvdata(ap
->dev
);
141 struct ahci_host_priv
*hpriv
= host
->private_data
;
142 void __iomem
*mmio
= hpriv
->mmio
;
143 struct imx_ahci_priv
*imxpriv
= hpriv
->plat_data
;
145 ahci_error_handler(ap
);
147 if (!(imxpriv
->first_time
) || ahci_imx_hotplug
)
150 imxpriv
->first_time
= false;
152 ata_for_each_dev(dev
, &ap
->link
, ENABLED
)
155 * Disable link to save power. An imx ahci port can't be recovered
156 * without full reset once the pddq mode is enabled making it
157 * impossible to use as part of libata LPM.
159 reg_val
= readl(mmio
+ PORT_PHY_CTL
);
160 writel(reg_val
| PORT_PHY_CTL_PDDQ_LOC
, mmio
+ PORT_PHY_CTL
);
161 imx_sata_disable(hpriv
);
162 imxpriv
->no_device
= true;
165 static int ahci_imx_softreset(struct ata_link
*link
, unsigned int *class,
166 unsigned long deadline
)
168 struct ata_port
*ap
= link
->ap
;
169 struct ata_host
*host
= dev_get_drvdata(ap
->dev
);
170 struct ahci_host_priv
*hpriv
= host
->private_data
;
171 struct imx_ahci_priv
*imxpriv
= hpriv
->plat_data
;
174 if (imxpriv
->type
== AHCI_IMX53
)
175 ret
= ahci_pmp_retry_srst_ops
.softreset(link
, class, deadline
);
176 else if (imxpriv
->type
== AHCI_IMX6Q
)
177 ret
= ahci_ops
.softreset(link
, class, deadline
);
182 static struct ata_port_operations ahci_imx_ops
= {
183 .inherits
= &ahci_ops
,
184 .host_stop
= ahci_imx_host_stop
,
185 .error_handler
= ahci_imx_error_handler
,
186 .softreset
= ahci_imx_softreset
,
189 static const struct ata_port_info ahci_imx_port_info
= {
190 .flags
= AHCI_FLAG_COMMON
,
191 .pio_mask
= ATA_PIO4
,
192 .udma_mask
= ATA_UDMA6
,
193 .port_ops
= &ahci_imx_ops
,
196 static const struct of_device_id imx_ahci_of_match
[] = {
197 { .compatible
= "fsl,imx53-ahci", .data
= (void *)AHCI_IMX53
},
198 { .compatible
= "fsl,imx6q-ahci", .data
= (void *)AHCI_IMX6Q
},
201 MODULE_DEVICE_TABLE(of
, imx_ahci_of_match
);
203 static int imx_ahci_probe(struct platform_device
*pdev
)
205 struct device
*dev
= &pdev
->dev
;
206 const struct of_device_id
*of_id
;
207 struct ahci_host_priv
*hpriv
;
208 struct imx_ahci_priv
*imxpriv
;
209 unsigned int reg_val
;
212 of_id
= of_match_device(imx_ahci_of_match
, dev
);
216 imxpriv
= devm_kzalloc(dev
, sizeof(*imxpriv
), GFP_KERNEL
);
220 imxpriv
->no_device
= false;
221 imxpriv
->first_time
= true;
222 imxpriv
->type
= (enum ahci_imx_type
)of_id
->data
;
223 imxpriv
->ahb_clk
= devm_clk_get(dev
, "ahb");
224 if (IS_ERR(imxpriv
->ahb_clk
)) {
225 dev_err(dev
, "can't get ahb clock.\n");
226 return PTR_ERR(imxpriv
->ahb_clk
);
229 if (imxpriv
->type
== AHCI_IMX6Q
) {
230 imxpriv
->gpr
= syscon_regmap_lookup_by_compatible(
231 "fsl,imx6q-iomuxc-gpr");
232 if (IS_ERR(imxpriv
->gpr
)) {
234 "failed to find fsl,imx6q-iomux-gpr regmap\n");
235 return PTR_ERR(imxpriv
->gpr
);
239 hpriv
= ahci_platform_get_resources(pdev
);
241 return PTR_ERR(hpriv
);
243 hpriv
->plat_data
= imxpriv
;
245 ret
= imx_sata_enable(hpriv
);
250 * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL,
251 * and IP vendor specific register HOST_TIMER1MS.
252 * Configure CAP_SSS (support stagered spin up).
253 * Implement the port0.
254 * Get the ahb clock rate, and configure the TIMER1MS register.
256 reg_val
= readl(hpriv
->mmio
+ HOST_CAP
);
257 if (!(reg_val
& HOST_CAP_SSS
)) {
258 reg_val
|= HOST_CAP_SSS
;
259 writel(reg_val
, hpriv
->mmio
+ HOST_CAP
);
261 reg_val
= readl(hpriv
->mmio
+ HOST_PORTS_IMPL
);
262 if (!(reg_val
& 0x1)) {
264 writel(reg_val
, hpriv
->mmio
+ HOST_PORTS_IMPL
);
267 reg_val
= clk_get_rate(imxpriv
->ahb_clk
) / 1000;
268 writel(reg_val
, hpriv
->mmio
+ HOST_TIMER1MS
);
270 ret
= ahci_platform_init_host(pdev
, hpriv
, &ahci_imx_port_info
, 0, 0);
272 imx_sata_disable(hpriv
);
277 static void ahci_imx_host_stop(struct ata_host
*host
)
279 struct ahci_host_priv
*hpriv
= host
->private_data
;
281 imx_sata_disable(hpriv
);
284 #ifdef CONFIG_PM_SLEEP
285 static int imx_ahci_suspend(struct device
*dev
)
287 struct ata_host
*host
= dev_get_drvdata(dev
);
288 struct ahci_host_priv
*hpriv
= host
->private_data
;
291 ret
= ahci_platform_suspend_host(dev
);
295 imx_sata_disable(hpriv
);
300 static int imx_ahci_resume(struct device
*dev
)
302 struct ata_host
*host
= dev_get_drvdata(dev
);
303 struct ahci_host_priv
*hpriv
= host
->private_data
;
306 ret
= imx_sata_enable(hpriv
);
310 return ahci_platform_resume_host(dev
);
314 static SIMPLE_DEV_PM_OPS(ahci_imx_pm_ops
, imx_ahci_suspend
, imx_ahci_resume
);
316 static struct platform_driver imx_ahci_driver
= {
317 .probe
= imx_ahci_probe
,
318 .remove
= ata_platform_remove_one
,
321 .owner
= THIS_MODULE
,
322 .of_match_table
= imx_ahci_of_match
,
323 .pm
= &ahci_imx_pm_ops
,
326 module_platform_driver(imx_ahci_driver
);
328 MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver");
329 MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>");
330 MODULE_LICENSE("GPL");
331 MODULE_ALIAS("ahci:imx");