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/iopoll.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of.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 | /* PHY registers */ |
19 | #define UNIPHY_PLL_REFCLK_CFG 0x000 |
20 | #define UNIPHY_PLL_PWRGEN_CFG 0x014 |
21 | #define UNIPHY_PLL_GLB_CFG 0x020 |
22 | #define UNIPHY_PLL_SDM_CFG0 0x038 |
23 | #define UNIPHY_PLL_SDM_CFG1 0x03C |
24 | #define UNIPHY_PLL_SDM_CFG2 0x040 |
25 | #define UNIPHY_PLL_SDM_CFG3 0x044 |
26 | #define UNIPHY_PLL_SDM_CFG4 0x048 |
27 | #define UNIPHY_PLL_SSC_CFG0 0x04C |
28 | #define UNIPHY_PLL_SSC_CFG1 0x050 |
29 | #define UNIPHY_PLL_SSC_CFG2 0x054 |
30 | #define UNIPHY_PLL_SSC_CFG3 0x058 |
31 | #define UNIPHY_PLL_LKDET_CFG0 0x05C |
32 | #define UNIPHY_PLL_LKDET_CFG1 0x060 |
33 | #define UNIPHY_PLL_LKDET_CFG2 0x064 |
34 | #define UNIPHY_PLL_CAL_CFG0 0x06C |
35 | #define UNIPHY_PLL_CAL_CFG8 0x08C |
36 | #define UNIPHY_PLL_CAL_CFG9 0x090 |
37 | #define UNIPHY_PLL_CAL_CFG10 0x094 |
38 | #define UNIPHY_PLL_CAL_CFG11 0x098 |
39 | #define UNIPHY_PLL_STATUS 0x0C0 |
40 | |
41 | #define SATA_PHY_SER_CTRL 0x100 |
42 | #define SATA_PHY_TX_DRIV_CTRL0 0x104 |
43 | #define SATA_PHY_TX_DRIV_CTRL1 0x108 |
44 | #define SATA_PHY_TX_IMCAL0 0x11C |
45 | #define SATA_PHY_TX_IMCAL2 0x124 |
46 | #define SATA_PHY_RX_IMCAL0 0x128 |
47 | #define SATA_PHY_EQUAL 0x13C |
48 | #define SATA_PHY_OOB_TERM 0x144 |
49 | #define SATA_PHY_CDR_CTRL0 0x148 |
50 | #define SATA_PHY_CDR_CTRL1 0x14C |
51 | #define SATA_PHY_CDR_CTRL2 0x150 |
52 | #define SATA_PHY_CDR_CTRL3 0x154 |
53 | #define SATA_PHY_PI_CTRL0 0x168 |
54 | #define SATA_PHY_POW_DWN_CTRL0 0x180 |
55 | #define SATA_PHY_POW_DWN_CTRL1 0x184 |
56 | #define SATA_PHY_TX_DATA_CTRL 0x188 |
57 | #define SATA_PHY_ALIGNP 0x1A4 |
58 | #define SATA_PHY_TX_IMCAL_STAT 0x1E4 |
59 | #define SATA_PHY_RX_IMCAL_STAT 0x1E8 |
60 | |
61 | #define UNIPHY_PLL_LOCK BIT(0) |
62 | #define SATA_PHY_TX_CAL BIT(0) |
63 | #define SATA_PHY_RX_CAL BIT(0) |
64 | |
65 | /* default timeout set to 1 sec */ |
66 | #define TIMEOUT_MS 10000 |
67 | #define DELAY_INTERVAL_US 100 |
68 | |
69 | struct qcom_apq8064_sata_phy { |
70 | void __iomem *mmio; |
71 | struct clk *cfg_clk; |
72 | struct device *dev; |
73 | }; |
74 | |
75 | /* Helper function to do poll and timeout */ |
76 | static int poll_timeout(void __iomem *addr, u32 mask) |
77 | { |
78 | u32 val; |
79 | |
80 | return readl_relaxed_poll_timeout(addr, val, (val & mask), |
81 | DELAY_INTERVAL_US, TIMEOUT_MS * 1000); |
82 | } |
83 | |
84 | static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) |
85 | { |
86 | struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(phy: generic_phy); |
87 | void __iomem *base = phy->mmio; |
88 | int ret = 0; |
89 | |
90 | /* SATA phy initialization */ |
91 | writel_relaxed(0x01, base + SATA_PHY_SER_CTRL); |
92 | writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0); |
93 | /* Make sure the power down happens before power up */ |
94 | mb(); |
95 | usleep_range(min: 10, max: 60); |
96 | |
97 | writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); |
98 | writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); |
99 | writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); |
100 | writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); |
101 | writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2); |
102 | |
103 | /* Write UNIPHYPLL registers to configure PLL */ |
104 | writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG); |
105 | writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG); |
106 | |
107 | writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0); |
108 | writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8); |
109 | writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9); |
110 | writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10); |
111 | writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11); |
112 | |
113 | writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0); |
114 | writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1); |
115 | writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2); |
116 | writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3); |
117 | writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4); |
118 | |
119 | writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0); |
120 | writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1); |
121 | writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2); |
122 | writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3); |
123 | |
124 | writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0); |
125 | writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1); |
126 | |
127 | writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG); |
128 | /* make sure global config LDO power down happens before power up */ |
129 | mb(); |
130 | |
131 | writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG); |
132 | writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2); |
133 | |
134 | /* PLL Lock wait */ |
135 | ret = poll_timeout(addr: base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); |
136 | if (ret) { |
137 | dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n" ); |
138 | return ret; |
139 | } |
140 | |
141 | /* TX Calibration */ |
142 | ret = poll_timeout(addr: base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); |
143 | if (ret) { |
144 | dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n" ); |
145 | return ret; |
146 | } |
147 | |
148 | /* RX Calibration */ |
149 | ret = poll_timeout(addr: base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); |
150 | if (ret) { |
151 | dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n" ); |
152 | return ret; |
153 | } |
154 | |
155 | /* SATA phy calibrated successfully, power up to functional mode */ |
156 | writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); |
157 | writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); |
158 | writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); |
159 | |
160 | writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1); |
161 | writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0); |
162 | writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1); |
163 | writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2); |
164 | writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0); |
165 | writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3); |
166 | writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); |
167 | |
168 | writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL); |
169 | writel_relaxed(0x43, base + SATA_PHY_ALIGNP); |
170 | writel_relaxed(0x04, base + SATA_PHY_OOB_TERM); |
171 | |
172 | writel_relaxed(0x01, base + SATA_PHY_EQUAL); |
173 | writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0); |
174 | writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1); |
175 | |
176 | return 0; |
177 | } |
178 | |
179 | static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy) |
180 | { |
181 | struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(phy: generic_phy); |
182 | void __iomem *base = phy->mmio; |
183 | |
184 | /* Power down PHY */ |
185 | writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0); |
186 | writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1); |
187 | |
188 | /* Power down PLL block */ |
189 | writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG); |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | static const struct phy_ops qcom_apq8064_sata_phy_ops = { |
195 | .init = qcom_apq8064_sata_phy_init, |
196 | .exit = qcom_apq8064_sata_phy_exit, |
197 | .owner = THIS_MODULE, |
198 | }; |
199 | |
200 | static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) |
201 | { |
202 | struct qcom_apq8064_sata_phy *phy; |
203 | struct device *dev = &pdev->dev; |
204 | struct phy_provider *phy_provider; |
205 | struct phy *generic_phy; |
206 | int ret; |
207 | |
208 | phy = devm_kzalloc(dev, size: sizeof(*phy), GFP_KERNEL); |
209 | if (!phy) |
210 | return -ENOMEM; |
211 | |
212 | phy->mmio = devm_platform_ioremap_resource(pdev, index: 0); |
213 | if (IS_ERR(ptr: phy->mmio)) |
214 | return PTR_ERR(ptr: phy->mmio); |
215 | |
216 | generic_phy = devm_phy_create(dev, NULL, ops: &qcom_apq8064_sata_phy_ops); |
217 | if (IS_ERR(ptr: generic_phy)) { |
218 | dev_err(dev, "%s: failed to create phy\n" , __func__); |
219 | return PTR_ERR(ptr: generic_phy); |
220 | } |
221 | |
222 | phy->dev = dev; |
223 | phy_set_drvdata(phy: generic_phy, data: phy); |
224 | platform_set_drvdata(pdev, data: phy); |
225 | |
226 | phy->cfg_clk = devm_clk_get(dev, id: "cfg" ); |
227 | if (IS_ERR(ptr: phy->cfg_clk)) { |
228 | dev_err(dev, "Failed to get sata cfg clock\n" ); |
229 | return PTR_ERR(ptr: phy->cfg_clk); |
230 | } |
231 | |
232 | ret = clk_prepare_enable(clk: phy->cfg_clk); |
233 | if (ret) |
234 | return ret; |
235 | |
236 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
237 | if (IS_ERR(ptr: phy_provider)) { |
238 | clk_disable_unprepare(clk: phy->cfg_clk); |
239 | dev_err(dev, "%s: failed to register phy\n" , __func__); |
240 | return PTR_ERR(ptr: phy_provider); |
241 | } |
242 | |
243 | return 0; |
244 | } |
245 | |
246 | static void qcom_apq8064_sata_phy_remove(struct platform_device *pdev) |
247 | { |
248 | struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev); |
249 | |
250 | clk_disable_unprepare(clk: phy->cfg_clk); |
251 | } |
252 | |
253 | static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = { |
254 | { .compatible = "qcom,apq8064-sata-phy" }, |
255 | { }, |
256 | }; |
257 | MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match); |
258 | |
259 | static struct platform_driver qcom_apq8064_sata_phy_driver = { |
260 | .probe = qcom_apq8064_sata_phy_probe, |
261 | .remove_new = qcom_apq8064_sata_phy_remove, |
262 | .driver = { |
263 | .name = "qcom-apq8064-sata-phy" , |
264 | .of_match_table = qcom_apq8064_sata_phy_of_match, |
265 | } |
266 | }; |
267 | module_platform_driver(qcom_apq8064_sata_phy_driver); |
268 | |
269 | MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver" ); |
270 | MODULE_LICENSE("GPL v2" ); |
271 | |