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