Commit | Line | Data |
---|---|---|
57f6ce07 | 1 | /* |
a70143bb | 2 | * phy-ti-pipe3 - PIPE3 PHY driver. |
57f6ce07 KVA |
3 | * |
4 | * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * Author: Kishon Vijay Abraham I <kishon@ti.com> | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/module.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/slab.h> | |
a70143bb | 22 | #include <linux/phy/phy.h> |
57f6ce07 KVA |
23 | #include <linux/of.h> |
24 | #include <linux/clk.h> | |
25 | #include <linux/err.h> | |
a70143bb | 26 | #include <linux/io.h> |
57f6ce07 KVA |
27 | #include <linux/pm_runtime.h> |
28 | #include <linux/delay.h> | |
14da699b | 29 | #include <linux/phy/omap_control_phy.h> |
918ee0d2 | 30 | #include <linux/of_platform.h> |
c934b361 RQ |
31 | #include <linux/mfd/syscon.h> |
32 | #include <linux/regmap.h> | |
57f6ce07 | 33 | |
57f6ce07 KVA |
34 | #define PLL_STATUS 0x00000004 |
35 | #define PLL_GO 0x00000008 | |
36 | #define PLL_CONFIGURATION1 0x0000000C | |
37 | #define PLL_CONFIGURATION2 0x00000010 | |
38 | #define PLL_CONFIGURATION3 0x00000014 | |
39 | #define PLL_CONFIGURATION4 0x00000020 | |
40 | ||
41 | #define PLL_REGM_MASK 0x001FFE00 | |
42 | #define PLL_REGM_SHIFT 0x9 | |
43 | #define PLL_REGM_F_MASK 0x0003FFFF | |
44 | #define PLL_REGM_F_SHIFT 0x0 | |
45 | #define PLL_REGN_MASK 0x000001FE | |
46 | #define PLL_REGN_SHIFT 0x1 | |
47 | #define PLL_SELFREQDCO_MASK 0x0000000E | |
48 | #define PLL_SELFREQDCO_SHIFT 0x1 | |
49 | #define PLL_SD_MASK 0x0003FC00 | |
1562864f | 50 | #define PLL_SD_SHIFT 10 |
57f6ce07 | 51 | #define SET_PLL_GO 0x1 |
629138db RQ |
52 | #define PLL_LDOPWDN BIT(15) |
53 | #define PLL_TICOPWDN BIT(16) | |
57f6ce07 KVA |
54 | #define PLL_LOCK 0x2 |
55 | #define PLL_IDLE 0x1 | |
56 | ||
c934b361 RQ |
57 | #define SATA_PLL_SOFT_RESET BIT(18) |
58 | ||
c396a1c7 KVA |
59 | #define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 |
60 | #define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14 | |
61 | ||
62 | #define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 | |
63 | #define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22 | |
64 | ||
65 | #define PIPE3_PHY_TX_RX_POWERON 0x3 | |
66 | #define PIPE3_PHY_TX_RX_POWEROFF 0x0 | |
67 | ||
3f2362c5 KVA |
68 | #define PCIE_PCS_MASK 0xFF0000 |
69 | #define PCIE_PCS_DELAY_COUNT_SHIFT 0x10 | |
70 | ||
57f6ce07 KVA |
71 | /* |
72 | * This is an Empirical value that works, need to confirm the actual | |
a70143bb KVA |
73 | * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status |
74 | * to be correctly reflected in the PIPE3PHY_PLL_STATUS register. | |
57f6ce07 | 75 | */ |
629138db RQ |
76 | #define PLL_IDLE_TIME 100 /* in milliseconds */ |
77 | #define PLL_LOCK_TIME 100 /* in milliseconds */ | |
57f6ce07 | 78 | |
a70143bb KVA |
79 | struct pipe3_dpll_params { |
80 | u16 m; | |
81 | u8 n; | |
82 | u8 freq:3; | |
83 | u8 sd; | |
84 | u32 mf; | |
85 | }; | |
86 | ||
61f54674 RQ |
87 | struct pipe3_dpll_map { |
88 | unsigned long rate; | |
89 | struct pipe3_dpll_params params; | |
90 | }; | |
91 | ||
a70143bb KVA |
92 | struct ti_pipe3 { |
93 | void __iomem *pll_ctrl_base; | |
94 | struct device *dev; | |
95 | struct device *control_dev; | |
96 | struct clk *wkupclk; | |
97 | struct clk *sys_clk; | |
1562864f | 98 | struct clk *refclk; |
99bbd48c | 99 | struct clk *div_clk; |
61f54674 | 100 | struct pipe3_dpll_map *dpll_map; |
c396a1c7 | 101 | struct regmap *phy_power_syscon; /* ctrl. reg. acces */ |
3f2362c5 | 102 | struct regmap *pcs_syscon; /* ctrl. reg. acces */ |
c934b361 RQ |
103 | struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */ |
104 | unsigned int dpll_reset_reg; /* reg. index within syscon */ | |
c396a1c7 | 105 | unsigned int power_reg; /* power reg. index within syscon */ |
3f2362c5 | 106 | unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */ |
c934b361 | 107 | bool sata_refclk_enabled; |
a70143bb KVA |
108 | }; |
109 | ||
61f54674 | 110 | static struct pipe3_dpll_map dpll_map_usb[] = { |
519c6013 RQ |
111 | {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ |
112 | {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ | |
113 | {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ | |
114 | {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ | |
115 | {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ | |
116 | {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ | |
61f54674 RQ |
117 | { }, /* Terminator */ |
118 | }; | |
119 | ||
120 | static struct pipe3_dpll_map dpll_map_sata[] = { | |
121 | {12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */ | |
122 | {16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */ | |
123 | {19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */ | |
124 | {20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */ | |
125 | {26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */ | |
126 | {38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */ | |
127 | { }, /* Terminator */ | |
57f6ce07 KVA |
128 | }; |
129 | ||
a70143bb KVA |
130 | static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset) |
131 | { | |
132 | return __raw_readl(addr + offset); | |
133 | } | |
134 | ||
135 | static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset, | |
136 | u32 data) | |
137 | { | |
138 | __raw_writel(data, addr + offset); | |
139 | } | |
140 | ||
61f54674 | 141 | static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy) |
519c6013 | 142 | { |
61f54674 RQ |
143 | unsigned long rate; |
144 | struct pipe3_dpll_map *dpll_map = phy->dpll_map; | |
519c6013 | 145 | |
61f54674 RQ |
146 | rate = clk_get_rate(phy->sys_clk); |
147 | ||
148 | for (; dpll_map->rate; dpll_map++) { | |
149 | if (rate == dpll_map->rate) | |
150 | return &dpll_map->params; | |
519c6013 RQ |
151 | } |
152 | ||
61f54674 RQ |
153 | dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate); |
154 | ||
1b97be8c | 155 | return NULL; |
519c6013 RQ |
156 | } |
157 | ||
0a0830fe RQ |
158 | static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy); |
159 | static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy); | |
160 | ||
a70143bb KVA |
161 | static int ti_pipe3_power_off(struct phy *x) |
162 | { | |
c396a1c7 KVA |
163 | u32 val; |
164 | int ret; | |
a70143bb | 165 | struct ti_pipe3 *phy = phy_get_drvdata(x); |
a70143bb | 166 | |
c396a1c7 KVA |
167 | if (!phy->phy_power_syscon) { |
168 | omap_control_phy_power(phy->control_dev, 0); | |
169 | return 0; | |
170 | } | |
a70143bb | 171 | |
c396a1c7 KVA |
172 | val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; |
173 | ||
174 | ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, | |
175 | PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val); | |
176 | return ret; | |
a70143bb KVA |
177 | } |
178 | ||
179 | static int ti_pipe3_power_on(struct phy *x) | |
57f6ce07 | 180 | { |
c396a1c7 KVA |
181 | u32 val; |
182 | u32 mask; | |
183 | int ret; | |
184 | unsigned long rate; | |
a70143bb | 185 | struct ti_pipe3 *phy = phy_get_drvdata(x); |
57f6ce07 | 186 | |
c396a1c7 KVA |
187 | if (!phy->phy_power_syscon) { |
188 | omap_control_phy_power(phy->control_dev, 1); | |
189 | return 0; | |
190 | } | |
57f6ce07 | 191 | |
c396a1c7 KVA |
192 | rate = clk_get_rate(phy->sys_clk); |
193 | if (!rate) { | |
194 | dev_err(phy->dev, "Invalid clock rate\n"); | |
195 | return -EINVAL; | |
196 | } | |
197 | rate = rate / 1000000; | |
198 | mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | | |
199 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; | |
200 | val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; | |
201 | val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; | |
202 | ||
203 | ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, | |
204 | mask, val); | |
205 | return ret; | |
57f6ce07 KVA |
206 | } |
207 | ||
629138db | 208 | static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy) |
57f6ce07 KVA |
209 | { |
210 | u32 val; | |
211 | unsigned long timeout; | |
212 | ||
629138db | 213 | timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME); |
57f6ce07 | 214 | do { |
629138db | 215 | cpu_relax(); |
a70143bb | 216 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); |
57f6ce07 | 217 | if (val & PLL_LOCK) |
a5e5d3c0 | 218 | return 0; |
629138db RQ |
219 | } while (!time_after(jiffies, timeout)); |
220 | ||
a5e5d3c0 AL |
221 | dev_err(phy->dev, "DPLL failed to lock\n"); |
222 | return -EBUSY; | |
57f6ce07 KVA |
223 | } |
224 | ||
629138db | 225 | static int ti_pipe3_dpll_program(struct ti_pipe3 *phy) |
57f6ce07 KVA |
226 | { |
227 | u32 val; | |
a70143bb | 228 | struct pipe3_dpll_params *dpll_params; |
57f6ce07 | 229 | |
61f54674 RQ |
230 | dpll_params = ti_pipe3_get_dpll_params(phy); |
231 | if (!dpll_params) | |
57f6ce07 | 232 | return -EINVAL; |
57f6ce07 | 233 | |
a70143bb | 234 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); |
57f6ce07 | 235 | val &= ~PLL_REGN_MASK; |
519c6013 | 236 | val |= dpll_params->n << PLL_REGN_SHIFT; |
a70143bb | 237 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); |
57f6ce07 | 238 | |
a70143bb | 239 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); |
57f6ce07 | 240 | val &= ~PLL_SELFREQDCO_MASK; |
519c6013 | 241 | val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; |
a70143bb | 242 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); |
57f6ce07 | 243 | |
a70143bb | 244 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); |
57f6ce07 | 245 | val &= ~PLL_REGM_MASK; |
519c6013 | 246 | val |= dpll_params->m << PLL_REGM_SHIFT; |
a70143bb | 247 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); |
57f6ce07 | 248 | |
a70143bb | 249 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); |
57f6ce07 | 250 | val &= ~PLL_REGM_F_MASK; |
519c6013 | 251 | val |= dpll_params->mf << PLL_REGM_F_SHIFT; |
a70143bb | 252 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); |
57f6ce07 | 253 | |
a70143bb | 254 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); |
57f6ce07 | 255 | val &= ~PLL_SD_MASK; |
519c6013 | 256 | val |= dpll_params->sd << PLL_SD_SHIFT; |
a70143bb | 257 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); |
57f6ce07 | 258 | |
629138db | 259 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); |
57f6ce07 | 260 | |
629138db | 261 | return ti_pipe3_dpll_wait_lock(phy); |
57f6ce07 KVA |
262 | } |
263 | ||
a70143bb | 264 | static int ti_pipe3_init(struct phy *x) |
57f6ce07 | 265 | { |
a70143bb | 266 | struct ti_pipe3 *phy = phy_get_drvdata(x); |
629138db RQ |
267 | u32 val; |
268 | int ret = 0; | |
519c6013 | 269 | |
0a0830fe | 270 | ti_pipe3_enable_clocks(phy); |
0bc09f9c V |
271 | /* |
272 | * Set pcie_pcs register to 0x96 for proper functioning of phy | |
273 | * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table | |
274 | * 18-1804. | |
275 | */ | |
f0e2cf7b | 276 | if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { |
3f2362c5 KVA |
277 | if (!phy->pcs_syscon) { |
278 | omap_control_pcie_pcs(phy->control_dev, 0x96); | |
279 | return 0; | |
280 | } | |
281 | ||
282 | val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT; | |
283 | ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg, | |
284 | PCIE_PCS_MASK, val); | |
285 | return ret; | |
f0e2cf7b | 286 | } |
99bbd48c | 287 | |
629138db RQ |
288 | /* Bring it out of IDLE if it is IDLE */ |
289 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); | |
290 | if (val & PLL_IDLE) { | |
291 | val &= ~PLL_IDLE; | |
292 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); | |
293 | ret = ti_pipe3_dpll_wait_lock(phy); | |
294 | } | |
57f6ce07 | 295 | |
31b2a32f | 296 | /* SATA has issues if re-programmed when locked */ |
629138db | 297 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); |
31b2a32f RQ |
298 | if ((val & PLL_LOCK) && of_device_is_compatible(phy->dev->of_node, |
299 | "ti,phy-pipe3-sata")) | |
300 | return ret; | |
301 | ||
302 | /* Program the DPLL */ | |
303 | ret = ti_pipe3_dpll_program(phy); | |
304 | if (ret) { | |
305 | ti_pipe3_disable_clocks(phy); | |
306 | return -EINVAL; | |
307 | } | |
57f6ce07 | 308 | |
629138db | 309 | return ret; |
57f6ce07 KVA |
310 | } |
311 | ||
629138db RQ |
312 | static int ti_pipe3_exit(struct phy *x) |
313 | { | |
314 | struct ti_pipe3 *phy = phy_get_drvdata(x); | |
315 | u32 val; | |
316 | unsigned long timeout; | |
317 | ||
c934b361 RQ |
318 | /* If dpll_reset_syscon is not present we wont power down SATA DPLL |
319 | * due to Errata i783 | |
320 | */ | |
321 | if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") && | |
322 | !phy->dpll_reset_syscon) | |
56042e4e RQ |
323 | return 0; |
324 | ||
0a0830fe RQ |
325 | /* PCIe doesn't have internal DPLL */ |
326 | if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { | |
327 | /* Put DPLL in IDLE mode */ | |
328 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); | |
329 | val |= PLL_IDLE; | |
330 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); | |
629138db | 331 | |
0a0830fe RQ |
332 | /* wait for LDO and Oscillator to power down */ |
333 | timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME); | |
334 | do { | |
335 | cpu_relax(); | |
336 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); | |
337 | if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) | |
338 | break; | |
339 | } while (!time_after(jiffies, timeout)); | |
340 | ||
341 | if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { | |
342 | dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n", | |
343 | val); | |
344 | return -EBUSY; | |
345 | } | |
629138db RQ |
346 | } |
347 | ||
c934b361 RQ |
348 | /* i783: SATA needs control bit toggle after PLL unlock */ |
349 | if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) { | |
350 | regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, | |
351 | SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET); | |
352 | regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, | |
353 | SATA_PLL_SOFT_RESET, 0); | |
354 | } | |
355 | ||
0a0830fe RQ |
356 | ti_pipe3_disable_clocks(phy); |
357 | ||
629138db RQ |
358 | return 0; |
359 | } | |
4a9e5ca1 | 360 | static const struct phy_ops ops = { |
a70143bb | 361 | .init = ti_pipe3_init, |
629138db | 362 | .exit = ti_pipe3_exit, |
a70143bb KVA |
363 | .power_on = ti_pipe3_power_on, |
364 | .power_off = ti_pipe3_power_off, | |
365 | .owner = THIS_MODULE, | |
366 | }; | |
367 | ||
61f54674 | 368 | static const struct of_device_id ti_pipe3_id_table[]; |
61f54674 | 369 | |
234738ea | 370 | static int ti_pipe3_get_clk(struct ti_pipe3 *phy) |
57f6ce07 | 371 | { |
99bbd48c | 372 | struct clk *clk; |
234738ea KVA |
373 | struct device *dev = phy->dev; |
374 | struct device_node *node = dev->of_node; | |
9c7f0443 | 375 | |
d65ff52e | 376 | phy->refclk = devm_clk_get(dev, "refclk"); |
7f33912d | 377 | if (IS_ERR(phy->refclk)) { |
d65ff52e | 378 | dev_err(dev, "unable to get refclk\n"); |
7f33912d RQ |
379 | /* older DTBs have missing refclk in SATA PHY |
380 | * so don't bail out in case of SATA PHY. | |
381 | */ | |
382 | if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) | |
383 | return PTR_ERR(phy->refclk); | |
384 | } | |
385 | ||
99bbd48c | 386 | if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { |
d65ff52e | 387 | phy->wkupclk = devm_clk_get(dev, "wkupclk"); |
9c7f0443 | 388 | if (IS_ERR(phy->wkupclk)) { |
d65ff52e | 389 | dev_err(dev, "unable to get wkupclk\n"); |
9c7f0443 RQ |
390 | return PTR_ERR(phy->wkupclk); |
391 | } | |
9c7f0443 RQ |
392 | } else { |
393 | phy->wkupclk = ERR_PTR(-ENODEV); | |
234738ea KVA |
394 | } |
395 | ||
c396a1c7 KVA |
396 | if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") || |
397 | phy->phy_power_syscon) { | |
234738ea KVA |
398 | phy->sys_clk = devm_clk_get(dev, "sysclk"); |
399 | if (IS_ERR(phy->sys_clk)) { | |
400 | dev_err(dev, "unable to get sysclk\n"); | |
401 | return -EINVAL; | |
c934b361 | 402 | } |
57f6ce07 | 403 | } |
57f6ce07 | 404 | |
99bbd48c | 405 | if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { |
d65ff52e | 406 | clk = devm_clk_get(dev, "dpll_ref"); |
99bbd48c | 407 | if (IS_ERR(clk)) { |
d65ff52e | 408 | dev_err(dev, "unable to get dpll ref clk\n"); |
99bbd48c KVA |
409 | return PTR_ERR(clk); |
410 | } | |
411 | clk_set_rate(clk, 1500000000); | |
412 | ||
d65ff52e | 413 | clk = devm_clk_get(dev, "dpll_ref_m2"); |
99bbd48c | 414 | if (IS_ERR(clk)) { |
d65ff52e | 415 | dev_err(dev, "unable to get dpll ref m2 clk\n"); |
99bbd48c KVA |
416 | return PTR_ERR(clk); |
417 | } | |
418 | clk_set_rate(clk, 100000000); | |
419 | ||
d65ff52e | 420 | clk = devm_clk_get(dev, "phy-div"); |
99bbd48c | 421 | if (IS_ERR(clk)) { |
d65ff52e | 422 | dev_err(dev, "unable to get phy-div clk\n"); |
99bbd48c KVA |
423 | return PTR_ERR(clk); |
424 | } | |
425 | clk_set_rate(clk, 100000000); | |
426 | ||
d65ff52e | 427 | phy->div_clk = devm_clk_get(dev, "div-clk"); |
99bbd48c | 428 | if (IS_ERR(phy->div_clk)) { |
d65ff52e | 429 | dev_err(dev, "unable to get div-clk\n"); |
99bbd48c KVA |
430 | return PTR_ERR(phy->div_clk); |
431 | } | |
432 | } else { | |
433 | phy->div_clk = ERR_PTR(-ENODEV); | |
57f6ce07 KVA |
434 | } |
435 | ||
234738ea KVA |
436 | return 0; |
437 | } | |
438 | ||
73bbc78e KVA |
439 | static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) |
440 | { | |
441 | struct device *dev = phy->dev; | |
442 | struct device_node *node = dev->of_node; | |
443 | struct device_node *control_node; | |
444 | struct platform_device *control_pdev; | |
445 | ||
c396a1c7 KVA |
446 | phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node, |
447 | "syscon-phy-power"); | |
448 | if (IS_ERR(phy->phy_power_syscon)) { | |
449 | dev_dbg(dev, | |
450 | "can't get syscon-phy-power, using control device\n"); | |
451 | phy->phy_power_syscon = NULL; | |
452 | } else { | |
453 | if (of_property_read_u32_index(node, | |
454 | "syscon-phy-power", 1, | |
455 | &phy->power_reg)) { | |
456 | dev_err(dev, "couldn't get power reg. offset\n"); | |
457 | return -EINVAL; | |
458 | } | |
73bbc78e KVA |
459 | } |
460 | ||
c396a1c7 KVA |
461 | if (!phy->phy_power_syscon) { |
462 | control_node = of_parse_phandle(node, "ctrl-module", 0); | |
463 | if (!control_node) { | |
464 | dev_err(dev, "Failed to get control device phandle\n"); | |
465 | return -EINVAL; | |
466 | } | |
73bbc78e | 467 | |
c396a1c7 KVA |
468 | control_pdev = of_find_device_by_node(control_node); |
469 | if (!control_pdev) { | |
470 | dev_err(dev, "Failed to get control device\n"); | |
471 | return -EINVAL; | |
472 | } | |
473 | ||
474 | phy->control_dev = &control_pdev->dev; | |
475 | } | |
73bbc78e | 476 | |
3f2362c5 KVA |
477 | if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { |
478 | phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node, | |
479 | "syscon-pcs"); | |
480 | if (IS_ERR(phy->pcs_syscon)) { | |
481 | dev_dbg(dev, | |
482 | "can't get syscon-pcs, using omap control\n"); | |
483 | phy->pcs_syscon = NULL; | |
484 | } else { | |
485 | if (of_property_read_u32_index(node, | |
486 | "syscon-pcs", 1, | |
487 | &phy->pcie_pcs_reg)) { | |
488 | dev_err(dev, | |
489 | "couldn't get pcie pcs reg. offset\n"); | |
490 | return -EINVAL; | |
491 | } | |
492 | } | |
493 | } | |
494 | ||
73bbc78e KVA |
495 | if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { |
496 | phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node, | |
497 | "syscon-pllreset"); | |
498 | if (IS_ERR(phy->dpll_reset_syscon)) { | |
499 | dev_info(dev, | |
500 | "can't get syscon-pllreset, sata dpll won't idle\n"); | |
501 | phy->dpll_reset_syscon = NULL; | |
502 | } else { | |
503 | if (of_property_read_u32_index(node, | |
504 | "syscon-pllreset", 1, | |
505 | &phy->dpll_reset_reg)) { | |
506 | dev_err(dev, | |
507 | "couldn't get pllreset reg. offset\n"); | |
508 | return -EINVAL; | |
509 | } | |
510 | } | |
511 | } | |
512 | ||
513 | return 0; | |
514 | } | |
515 | ||
1fe52122 KVA |
516 | static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) |
517 | { | |
518 | struct resource *res; | |
519 | const struct of_device_id *match; | |
520 | struct device *dev = phy->dev; | |
521 | struct device_node *node = dev->of_node; | |
522 | struct platform_device *pdev = to_platform_device(dev); | |
523 | ||
524 | if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) | |
525 | return 0; | |
526 | ||
527 | match = of_match_device(ti_pipe3_id_table, dev); | |
528 | if (!match) | |
529 | return -EINVAL; | |
530 | ||
531 | phy->dpll_map = (struct pipe3_dpll_map *)match->data; | |
532 | if (!phy->dpll_map) { | |
533 | dev_err(dev, "no DPLL data\n"); | |
534 | return -EINVAL; | |
535 | } | |
536 | ||
537 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
538 | "pll_ctrl"); | |
539 | phy->pll_ctrl_base = devm_ioremap_resource(dev, res); | |
540 | if (IS_ERR(phy->pll_ctrl_base)) | |
541 | return PTR_ERR(phy->pll_ctrl_base); | |
542 | ||
543 | return 0; | |
544 | } | |
545 | ||
234738ea KVA |
546 | static int ti_pipe3_probe(struct platform_device *pdev) |
547 | { | |
548 | struct ti_pipe3 *phy; | |
549 | struct phy *generic_phy; | |
550 | struct phy_provider *phy_provider; | |
234738ea | 551 | struct device_node *node = pdev->dev.of_node; |
234738ea KVA |
552 | struct device *dev = &pdev->dev; |
553 | int ret; | |
554 | ||
555 | phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); | |
556 | if (!phy) | |
557 | return -ENOMEM; | |
558 | ||
559 | phy->dev = dev; | |
560 | ||
1fe52122 KVA |
561 | ret = ti_pipe3_get_pll_base(phy); |
562 | if (ret) | |
563 | return ret; | |
234738ea | 564 | |
73bbc78e KVA |
565 | ret = ti_pipe3_get_sysctrl(phy); |
566 | if (ret) | |
567 | return ret; | |
234738ea KVA |
568 | |
569 | ret = ti_pipe3_get_clk(phy); | |
570 | if (ret) | |
571 | return ret; | |
572 | ||
57f6ce07 | 573 | platform_set_drvdata(pdev, phy); |
d65ff52e | 574 | pm_runtime_enable(dev); |
c934b361 RQ |
575 | |
576 | /* | |
577 | * Prevent auto-disable of refclk for SATA PHY due to Errata i783 | |
578 | */ | |
579 | if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { | |
580 | if (!IS_ERR(phy->refclk)) { | |
0a0830fe | 581 | clk_prepare_enable(phy->refclk); |
c934b361 RQ |
582 | phy->sata_refclk_enabled = true; |
583 | } | |
584 | } | |
a70143bb | 585 | |
d65ff52e | 586 | generic_phy = devm_phy_create(dev, NULL, &ops); |
a70143bb KVA |
587 | if (IS_ERR(generic_phy)) |
588 | return PTR_ERR(generic_phy); | |
589 | ||
590 | phy_set_drvdata(generic_phy, phy); | |
cc34ace7 KVA |
591 | |
592 | ti_pipe3_power_off(generic_phy); | |
593 | ||
d65ff52e | 594 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
a70143bb KVA |
595 | if (IS_ERR(phy_provider)) |
596 | return PTR_ERR(phy_provider); | |
597 | ||
57f6ce07 KVA |
598 | return 0; |
599 | } | |
600 | ||
a70143bb | 601 | static int ti_pipe3_remove(struct platform_device *pdev) |
57f6ce07 | 602 | { |
57f6ce07 KVA |
603 | pm_runtime_disable(&pdev->dev); |
604 | ||
605 | return 0; | |
606 | } | |
607 | ||
0a0830fe | 608 | static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy) |
7f33912d | 609 | { |
0a0830fe | 610 | int ret = 0; |
7f33912d | 611 | |
0a0830fe | 612 | if (!IS_ERR(phy->refclk)) { |
7f33912d RQ |
613 | ret = clk_prepare_enable(phy->refclk); |
614 | if (ret) { | |
615 | dev_err(phy->dev, "Failed to enable refclk %d\n", ret); | |
616 | return ret; | |
617 | } | |
7f33912d RQ |
618 | } |
619 | ||
1562864f RQ |
620 | if (!IS_ERR(phy->wkupclk)) { |
621 | ret = clk_prepare_enable(phy->wkupclk); | |
622 | if (ret) { | |
623 | dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); | |
0a0830fe | 624 | goto disable_refclk; |
1562864f | 625 | } |
57f6ce07 KVA |
626 | } |
627 | ||
99bbd48c KVA |
628 | if (!IS_ERR(phy->div_clk)) { |
629 | ret = clk_prepare_enable(phy->div_clk); | |
630 | if (ret) { | |
631 | dev_err(phy->dev, "Failed to enable div_clk %d\n", ret); | |
0a0830fe | 632 | goto disable_wkupclk; |
99bbd48c KVA |
633 | } |
634 | } | |
6e738432 | 635 | |
57f6ce07 KVA |
636 | return 0; |
637 | ||
0a0830fe | 638 | disable_wkupclk: |
99bbd48c KVA |
639 | if (!IS_ERR(phy->wkupclk)) |
640 | clk_disable_unprepare(phy->wkupclk); | |
641 | ||
0a0830fe | 642 | disable_refclk: |
1562864f RQ |
643 | if (!IS_ERR(phy->refclk)) |
644 | clk_disable_unprepare(phy->refclk); | |
57f6ce07 | 645 | |
6e738432 RQ |
646 | return ret; |
647 | } | |
648 | ||
649 | static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) | |
650 | { | |
6e738432 RQ |
651 | if (!IS_ERR(phy->wkupclk)) |
652 | clk_disable_unprepare(phy->wkupclk); | |
c934b361 | 653 | if (!IS_ERR(phy->refclk)) { |
0a0830fe | 654 | clk_disable_unprepare(phy->refclk); |
c934b361 RQ |
655 | /* |
656 | * SATA refclk needs an additional disable as we left it | |
657 | * on in probe to avoid Errata i783 | |
658 | */ | |
659 | if (phy->sata_refclk_enabled) { | |
660 | clk_disable_unprepare(phy->refclk); | |
661 | phy->sata_refclk_enabled = false; | |
662 | } | |
663 | } | |
664 | ||
6e738432 RQ |
665 | if (!IS_ERR(phy->div_clk)) |
666 | clk_disable_unprepare(phy->div_clk); | |
6e738432 RQ |
667 | } |
668 | ||
a70143bb | 669 | static const struct of_device_id ti_pipe3_id_table[] = { |
61f54674 RQ |
670 | { |
671 | .compatible = "ti,phy-usb3", | |
672 | .data = dpll_map_usb, | |
673 | }, | |
674 | { | |
675 | .compatible = "ti,omap-usb3", | |
676 | .data = dpll_map_usb, | |
677 | }, | |
678 | { | |
679 | .compatible = "ti,phy-pipe3-sata", | |
680 | .data = dpll_map_sata, | |
681 | }, | |
99bbd48c KVA |
682 | { |
683 | .compatible = "ti,phy-pipe3-pcie", | |
684 | }, | |
57f6ce07 KVA |
685 | {} |
686 | }; | |
a70143bb | 687 | MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); |
57f6ce07 | 688 | |
a70143bb KVA |
689 | static struct platform_driver ti_pipe3_driver = { |
690 | .probe = ti_pipe3_probe, | |
691 | .remove = ti_pipe3_remove, | |
57f6ce07 | 692 | .driver = { |
a70143bb | 693 | .name = "ti-pipe3", |
298fe56e | 694 | .of_match_table = ti_pipe3_id_table, |
57f6ce07 KVA |
695 | }, |
696 | }; | |
697 | ||
a70143bb | 698 | module_platform_driver(ti_pipe3_driver); |
57f6ce07 | 699 | |
dd64ad38 | 700 | MODULE_ALIAS("platform:ti_pipe3"); |
57f6ce07 | 701 | MODULE_AUTHOR("Texas Instruments Inc."); |
a70143bb | 702 | MODULE_DESCRIPTION("TI PIPE3 phy driver"); |
57f6ce07 | 703 | MODULE_LICENSE("GPL v2"); |