1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
7 #include <linux/kernel.h>
8 #include <linux/module.h>
10 #include <linux/of_address.h>
11 #include <linux/time.h>
12 #include <linux/delay.h>
13 #include <linux/clk.h>
14 #include <linux/slab.h>
15 #include <linux/platform_device.h>
16 #include <linux/phy/phy.h>
18 struct qcom_ipq806x_sata_phy
{
24 #define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
26 #define SATA_PHY_P0_PARAM0 0x200
27 #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)
28 #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)
29 #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)
30 #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)
31 #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)
32 #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)
34 #define SATA_PHY_P0_PARAM1 0x204
35 #define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)
36 #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)
37 #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)
38 #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)
39 #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)
40 #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)
41 #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)
43 #define SATA_PHY_P0_PARAM2 0x208
44 #define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)
45 #define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)
47 #define SATA_PHY_P0_PARAM3 0x20C
48 #define SATA_PHY_SSC_EN 0x8
49 #define SATA_PHY_P0_PARAM4 0x210
50 #define SATA_PHY_REF_SSP_EN 0x2
51 #define SATA_PHY_RESET 0x1
53 static int qcom_ipq806x_sata_phy_init(struct phy
*generic_phy
)
55 struct qcom_ipq806x_sata_phy
*phy
= phy_get_drvdata(generic_phy
);
58 /* Setting SSC_EN to 1 */
59 reg
= readl_relaxed(phy
->mmio
+ SATA_PHY_P0_PARAM3
);
60 reg
= reg
| SATA_PHY_SSC_EN
;
61 writel_relaxed(reg
, phy
->mmio
+ SATA_PHY_P0_PARAM3
);
63 reg
= readl_relaxed(phy
->mmio
+ SATA_PHY_P0_PARAM0
) &
64 ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK
|
65 SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK
|
66 SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK
);
67 reg
|= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
68 writel_relaxed(reg
, phy
->mmio
+ SATA_PHY_P0_PARAM0
);
70 reg
= readl_relaxed(phy
->mmio
+ SATA_PHY_P0_PARAM1
) &
71 ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK
|
72 SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK
|
73 SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK
);
74 reg
|= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
75 SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
76 SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
77 writel_relaxed(reg
, phy
->mmio
+ SATA_PHY_P0_PARAM1
);
79 reg
= readl_relaxed(phy
->mmio
+ SATA_PHY_P0_PARAM2
) &
80 ~SATA_PHY_P0_PARAM2_RX_EQ_MASK
;
81 reg
|= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
82 writel_relaxed(reg
, phy
->mmio
+ SATA_PHY_P0_PARAM2
);
84 /* Setting PHY_RESET to 1 */
85 reg
= readl_relaxed(phy
->mmio
+ SATA_PHY_P0_PARAM4
);
86 reg
= reg
| SATA_PHY_RESET
;
87 writel_relaxed(reg
, phy
->mmio
+ SATA_PHY_P0_PARAM4
);
89 /* Setting REF_SSP_EN to 1 */
90 reg
= readl_relaxed(phy
->mmio
+ SATA_PHY_P0_PARAM4
);
91 reg
= reg
| SATA_PHY_REF_SSP_EN
| SATA_PHY_RESET
;
92 writel_relaxed(reg
, phy
->mmio
+ SATA_PHY_P0_PARAM4
);
94 /* make sure all changes complete before we let the PHY out of reset */
97 /* sleep for max. 50us more to combine processor wakeups */
98 usleep_range(20, 20 + 50);
100 /* Clearing PHY_RESET to 0 */
101 reg
= readl_relaxed(phy
->mmio
+ SATA_PHY_P0_PARAM4
);
102 reg
= reg
& ~SATA_PHY_RESET
;
103 writel_relaxed(reg
, phy
->mmio
+ SATA_PHY_P0_PARAM4
);
108 static int qcom_ipq806x_sata_phy_exit(struct phy
*generic_phy
)
110 struct qcom_ipq806x_sata_phy
*phy
= phy_get_drvdata(generic_phy
);
113 /* Setting PHY_RESET to 1 */
114 reg
= readl_relaxed(phy
->mmio
+ SATA_PHY_P0_PARAM4
);
115 reg
= reg
| SATA_PHY_RESET
;
116 writel_relaxed(reg
, phy
->mmio
+ SATA_PHY_P0_PARAM4
);
121 static const struct phy_ops qcom_ipq806x_sata_phy_ops
= {
122 .init
= qcom_ipq806x_sata_phy_init
,
123 .exit
= qcom_ipq806x_sata_phy_exit
,
124 .owner
= THIS_MODULE
,
127 static int qcom_ipq806x_sata_phy_probe(struct platform_device
*pdev
)
129 struct qcom_ipq806x_sata_phy
*phy
;
130 struct device
*dev
= &pdev
->dev
;
131 struct phy_provider
*phy_provider
;
132 struct phy
*generic_phy
;
135 phy
= devm_kzalloc(dev
, sizeof(*phy
), GFP_KERNEL
);
139 phy
->mmio
= devm_platform_ioremap_resource(pdev
, 0);
140 if (IS_ERR(phy
->mmio
))
141 return PTR_ERR(phy
->mmio
);
143 generic_phy
= devm_phy_create(dev
, NULL
, &qcom_ipq806x_sata_phy_ops
);
144 if (IS_ERR(generic_phy
)) {
145 dev_err(dev
, "%s: failed to create phy\n", __func__
);
146 return PTR_ERR(generic_phy
);
150 phy_set_drvdata(generic_phy
, phy
);
151 platform_set_drvdata(pdev
, phy
);
153 phy
->cfg_clk
= devm_clk_get(dev
, "cfg");
154 if (IS_ERR(phy
->cfg_clk
)) {
155 dev_err(dev
, "Failed to get sata cfg clock\n");
156 return PTR_ERR(phy
->cfg_clk
);
159 ret
= clk_prepare_enable(phy
->cfg_clk
);
163 phy_provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
164 if (IS_ERR(phy_provider
)) {
165 clk_disable_unprepare(phy
->cfg_clk
);
166 dev_err(dev
, "%s: failed to register phy\n", __func__
);
167 return PTR_ERR(phy_provider
);
173 static int qcom_ipq806x_sata_phy_remove(struct platform_device
*pdev
)
175 struct qcom_ipq806x_sata_phy
*phy
= platform_get_drvdata(pdev
);
177 clk_disable_unprepare(phy
->cfg_clk
);
182 static const struct of_device_id qcom_ipq806x_sata_phy_of_match
[] = {
183 { .compatible
= "qcom,ipq806x-sata-phy" },
186 MODULE_DEVICE_TABLE(of
, qcom_ipq806x_sata_phy_of_match
);
188 static struct platform_driver qcom_ipq806x_sata_phy_driver
= {
189 .probe
= qcom_ipq806x_sata_phy_probe
,
190 .remove
= qcom_ipq806x_sata_phy_remove
,
192 .name
= "qcom-ipq806x-sata-phy",
193 .of_match_table
= qcom_ipq806x_sata_phy_of_match
,
196 module_platform_driver(qcom_ipq806x_sata_phy_driver
);
198 MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
199 MODULE_LICENSE("GPL v2");