1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2014, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/io.h> |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/of.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> |
17 | |
18 | struct qcom_ipq806x_sata_phy { |
19 | void __iomem *mmio; |
20 | struct clk *cfg_clk; |
21 | struct device *dev; |
22 | }; |
23 | |
24 | #define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) |
25 | |
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) |
33 | |
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) |
42 | |
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) |
46 | |
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 |
52 | |
53 | static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) |
54 | { |
55 | struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(phy: generic_phy); |
56 | u32 reg; |
57 | |
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); |
62 | |
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); |
69 | |
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); |
78 | |
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); |
83 | |
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); |
88 | |
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); |
93 | |
94 | /* make sure all changes complete before we let the PHY out of reset */ |
95 | mb(); |
96 | |
97 | /* sleep for max. 50us more to combine processor wakeups */ |
98 | usleep_range(min: 20, max: 20 + 50); |
99 | |
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); |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) |
109 | { |
110 | struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(phy: generic_phy); |
111 | u32 reg; |
112 | |
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); |
117 | |
118 | return 0; |
119 | } |
120 | |
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, |
125 | }; |
126 | |
127 | static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) |
128 | { |
129 | struct qcom_ipq806x_sata_phy *phy; |
130 | struct device *dev = &pdev->dev; |
131 | struct phy_provider *phy_provider; |
132 | struct phy *generic_phy; |
133 | int ret; |
134 | |
135 | phy = devm_kzalloc(dev, size: sizeof(*phy), GFP_KERNEL); |
136 | if (!phy) |
137 | return -ENOMEM; |
138 | |
139 | phy->mmio = devm_platform_ioremap_resource(pdev, index: 0); |
140 | if (IS_ERR(ptr: phy->mmio)) |
141 | return PTR_ERR(ptr: phy->mmio); |
142 | |
143 | generic_phy = devm_phy_create(dev, NULL, ops: &qcom_ipq806x_sata_phy_ops); |
144 | if (IS_ERR(ptr: generic_phy)) { |
145 | dev_err(dev, "%s: failed to create phy\n" , __func__); |
146 | return PTR_ERR(ptr: generic_phy); |
147 | } |
148 | |
149 | phy->dev = dev; |
150 | phy_set_drvdata(phy: generic_phy, data: phy); |
151 | platform_set_drvdata(pdev, data: phy); |
152 | |
153 | phy->cfg_clk = devm_clk_get(dev, id: "cfg" ); |
154 | if (IS_ERR(ptr: phy->cfg_clk)) { |
155 | dev_err(dev, "Failed to get sata cfg clock\n" ); |
156 | return PTR_ERR(ptr: phy->cfg_clk); |
157 | } |
158 | |
159 | ret = clk_prepare_enable(clk: phy->cfg_clk); |
160 | if (ret) |
161 | return ret; |
162 | |
163 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
164 | if (IS_ERR(ptr: phy_provider)) { |
165 | clk_disable_unprepare(clk: phy->cfg_clk); |
166 | dev_err(dev, "%s: failed to register phy\n" , __func__); |
167 | return PTR_ERR(ptr: phy_provider); |
168 | } |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | static void qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) |
174 | { |
175 | struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); |
176 | |
177 | clk_disable_unprepare(clk: phy->cfg_clk); |
178 | } |
179 | |
180 | static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { |
181 | { .compatible = "qcom,ipq806x-sata-phy" }, |
182 | { }, |
183 | }; |
184 | MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); |
185 | |
186 | static struct platform_driver qcom_ipq806x_sata_phy_driver = { |
187 | .probe = qcom_ipq806x_sata_phy_probe, |
188 | .remove_new = qcom_ipq806x_sata_phy_remove, |
189 | .driver = { |
190 | .name = "qcom-ipq806x-sata-phy" , |
191 | .of_match_table = qcom_ipq806x_sata_phy_of_match, |
192 | } |
193 | }; |
194 | module_platform_driver(qcom_ipq806x_sata_phy_driver); |
195 | |
196 | MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver" ); |
197 | MODULE_LICENSE("GPL v2" ); |
198 | |