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 | |
629138db RQ |
296 | /* Program the DPLL only if not locked */ |
297 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); | |
298 | if (!(val & PLL_LOCK)) | |
299 | if (ti_pipe3_dpll_program(phy)) | |
300 | return -EINVAL; | |
57f6ce07 | 301 | |
629138db | 302 | return ret; |
57f6ce07 KVA |
303 | } |
304 | ||
629138db RQ |
305 | static int ti_pipe3_exit(struct phy *x) |
306 | { | |
307 | struct ti_pipe3 *phy = phy_get_drvdata(x); | |
308 | u32 val; | |
309 | unsigned long timeout; | |
310 | ||
c934b361 RQ |
311 | /* If dpll_reset_syscon is not present we wont power down SATA DPLL |
312 | * due to Errata i783 | |
313 | */ | |
314 | if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") && | |
315 | !phy->dpll_reset_syscon) | |
56042e4e RQ |
316 | return 0; |
317 | ||
0a0830fe RQ |
318 | /* PCIe doesn't have internal DPLL */ |
319 | if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { | |
320 | /* Put DPLL in IDLE mode */ | |
321 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); | |
322 | val |= PLL_IDLE; | |
323 | ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); | |
629138db | 324 | |
0a0830fe RQ |
325 | /* wait for LDO and Oscillator to power down */ |
326 | timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME); | |
327 | do { | |
328 | cpu_relax(); | |
329 | val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); | |
330 | if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) | |
331 | break; | |
332 | } while (!time_after(jiffies, timeout)); | |
333 | ||
334 | if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { | |
335 | dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n", | |
336 | val); | |
337 | return -EBUSY; | |
338 | } | |
629138db RQ |
339 | } |
340 | ||
c934b361 RQ |
341 | /* i783: SATA needs control bit toggle after PLL unlock */ |
342 | if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) { | |
343 | regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, | |
344 | SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET); | |
345 | regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, | |
346 | SATA_PLL_SOFT_RESET, 0); | |
347 | } | |
348 | ||
0a0830fe RQ |
349 | ti_pipe3_disable_clocks(phy); |
350 | ||
629138db RQ |
351 | return 0; |
352 | } | |
4a9e5ca1 | 353 | static const struct phy_ops ops = { |
a70143bb | 354 | .init = ti_pipe3_init, |
629138db | 355 | .exit = ti_pipe3_exit, |
a70143bb KVA |
356 | .power_on = ti_pipe3_power_on, |
357 | .power_off = ti_pipe3_power_off, | |
358 | .owner = THIS_MODULE, | |
359 | }; | |
360 | ||
61f54674 | 361 | static const struct of_device_id ti_pipe3_id_table[]; |
61f54674 | 362 | |
234738ea | 363 | static int ti_pipe3_get_clk(struct ti_pipe3 *phy) |
57f6ce07 | 364 | { |
99bbd48c | 365 | struct clk *clk; |
234738ea KVA |
366 | struct device *dev = phy->dev; |
367 | struct device_node *node = dev->of_node; | |
9c7f0443 | 368 | |
d65ff52e | 369 | phy->refclk = devm_clk_get(dev, "refclk"); |
7f33912d | 370 | if (IS_ERR(phy->refclk)) { |
d65ff52e | 371 | dev_err(dev, "unable to get refclk\n"); |
7f33912d RQ |
372 | /* older DTBs have missing refclk in SATA PHY |
373 | * so don't bail out in case of SATA PHY. | |
374 | */ | |
375 | if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) | |
376 | return PTR_ERR(phy->refclk); | |
377 | } | |
378 | ||
99bbd48c | 379 | if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { |
d65ff52e | 380 | phy->wkupclk = devm_clk_get(dev, "wkupclk"); |
9c7f0443 | 381 | if (IS_ERR(phy->wkupclk)) { |
d65ff52e | 382 | dev_err(dev, "unable to get wkupclk\n"); |
9c7f0443 RQ |
383 | return PTR_ERR(phy->wkupclk); |
384 | } | |
9c7f0443 RQ |
385 | } else { |
386 | phy->wkupclk = ERR_PTR(-ENODEV); | |
234738ea KVA |
387 | } |
388 | ||
c396a1c7 KVA |
389 | if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") || |
390 | phy->phy_power_syscon) { | |
234738ea KVA |
391 | phy->sys_clk = devm_clk_get(dev, "sysclk"); |
392 | if (IS_ERR(phy->sys_clk)) { | |
393 | dev_err(dev, "unable to get sysclk\n"); | |
394 | return -EINVAL; | |
c934b361 | 395 | } |
57f6ce07 | 396 | } |
57f6ce07 | 397 | |
99bbd48c | 398 | if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { |
d65ff52e | 399 | clk = devm_clk_get(dev, "dpll_ref"); |
99bbd48c | 400 | if (IS_ERR(clk)) { |
d65ff52e | 401 | dev_err(dev, "unable to get dpll ref clk\n"); |
99bbd48c KVA |
402 | return PTR_ERR(clk); |
403 | } | |
404 | clk_set_rate(clk, 1500000000); | |
405 | ||
d65ff52e | 406 | clk = devm_clk_get(dev, "dpll_ref_m2"); |
99bbd48c | 407 | if (IS_ERR(clk)) { |
d65ff52e | 408 | dev_err(dev, "unable to get dpll ref m2 clk\n"); |
99bbd48c KVA |
409 | return PTR_ERR(clk); |
410 | } | |
411 | clk_set_rate(clk, 100000000); | |
412 | ||
d65ff52e | 413 | clk = devm_clk_get(dev, "phy-div"); |
99bbd48c | 414 | if (IS_ERR(clk)) { |
d65ff52e | 415 | dev_err(dev, "unable to get phy-div clk\n"); |
99bbd48c KVA |
416 | return PTR_ERR(clk); |
417 | } | |
418 | clk_set_rate(clk, 100000000); | |
419 | ||
d65ff52e | 420 | phy->div_clk = devm_clk_get(dev, "div-clk"); |
99bbd48c | 421 | if (IS_ERR(phy->div_clk)) { |
d65ff52e | 422 | dev_err(dev, "unable to get div-clk\n"); |
99bbd48c KVA |
423 | return PTR_ERR(phy->div_clk); |
424 | } | |
425 | } else { | |
426 | phy->div_clk = ERR_PTR(-ENODEV); | |
57f6ce07 KVA |
427 | } |
428 | ||
234738ea KVA |
429 | return 0; |
430 | } | |
431 | ||
73bbc78e KVA |
432 | static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) |
433 | { | |
434 | struct device *dev = phy->dev; | |
435 | struct device_node *node = dev->of_node; | |
436 | struct device_node *control_node; | |
437 | struct platform_device *control_pdev; | |
438 | ||
c396a1c7 KVA |
439 | phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node, |
440 | "syscon-phy-power"); | |
441 | if (IS_ERR(phy->phy_power_syscon)) { | |
442 | dev_dbg(dev, | |
443 | "can't get syscon-phy-power, using control device\n"); | |
444 | phy->phy_power_syscon = NULL; | |
445 | } else { | |
446 | if (of_property_read_u32_index(node, | |
447 | "syscon-phy-power", 1, | |
448 | &phy->power_reg)) { | |
449 | dev_err(dev, "couldn't get power reg. offset\n"); | |
450 | return -EINVAL; | |
451 | } | |
73bbc78e KVA |
452 | } |
453 | ||
c396a1c7 KVA |
454 | if (!phy->phy_power_syscon) { |
455 | control_node = of_parse_phandle(node, "ctrl-module", 0); | |
456 | if (!control_node) { | |
457 | dev_err(dev, "Failed to get control device phandle\n"); | |
458 | return -EINVAL; | |
459 | } | |
73bbc78e | 460 | |
c396a1c7 KVA |
461 | control_pdev = of_find_device_by_node(control_node); |
462 | if (!control_pdev) { | |
463 | dev_err(dev, "Failed to get control device\n"); | |
464 | return -EINVAL; | |
465 | } | |
466 | ||
467 | phy->control_dev = &control_pdev->dev; | |
468 | } | |
73bbc78e | 469 | |
3f2362c5 KVA |
470 | if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { |
471 | phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node, | |
472 | "syscon-pcs"); | |
473 | if (IS_ERR(phy->pcs_syscon)) { | |
474 | dev_dbg(dev, | |
475 | "can't get syscon-pcs, using omap control\n"); | |
476 | phy->pcs_syscon = NULL; | |
477 | } else { | |
478 | if (of_property_read_u32_index(node, | |
479 | "syscon-pcs", 1, | |
480 | &phy->pcie_pcs_reg)) { | |
481 | dev_err(dev, | |
482 | "couldn't get pcie pcs reg. offset\n"); | |
483 | return -EINVAL; | |
484 | } | |
485 | } | |
486 | } | |
487 | ||
73bbc78e KVA |
488 | if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { |
489 | phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node, | |
490 | "syscon-pllreset"); | |
491 | if (IS_ERR(phy->dpll_reset_syscon)) { | |
492 | dev_info(dev, | |
493 | "can't get syscon-pllreset, sata dpll won't idle\n"); | |
494 | phy->dpll_reset_syscon = NULL; | |
495 | } else { | |
496 | if (of_property_read_u32_index(node, | |
497 | "syscon-pllreset", 1, | |
498 | &phy->dpll_reset_reg)) { | |
499 | dev_err(dev, | |
500 | "couldn't get pllreset reg. offset\n"); | |
501 | return -EINVAL; | |
502 | } | |
503 | } | |
504 | } | |
505 | ||
506 | return 0; | |
507 | } | |
508 | ||
1fe52122 KVA |
509 | static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) |
510 | { | |
511 | struct resource *res; | |
512 | const struct of_device_id *match; | |
513 | struct device *dev = phy->dev; | |
514 | struct device_node *node = dev->of_node; | |
515 | struct platform_device *pdev = to_platform_device(dev); | |
516 | ||
517 | if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) | |
518 | return 0; | |
519 | ||
520 | match = of_match_device(ti_pipe3_id_table, dev); | |
521 | if (!match) | |
522 | return -EINVAL; | |
523 | ||
524 | phy->dpll_map = (struct pipe3_dpll_map *)match->data; | |
525 | if (!phy->dpll_map) { | |
526 | dev_err(dev, "no DPLL data\n"); | |
527 | return -EINVAL; | |
528 | } | |
529 | ||
530 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
531 | "pll_ctrl"); | |
532 | phy->pll_ctrl_base = devm_ioremap_resource(dev, res); | |
533 | if (IS_ERR(phy->pll_ctrl_base)) | |
534 | return PTR_ERR(phy->pll_ctrl_base); | |
535 | ||
536 | return 0; | |
537 | } | |
538 | ||
234738ea KVA |
539 | static int ti_pipe3_probe(struct platform_device *pdev) |
540 | { | |
541 | struct ti_pipe3 *phy; | |
542 | struct phy *generic_phy; | |
543 | struct phy_provider *phy_provider; | |
234738ea | 544 | struct device_node *node = pdev->dev.of_node; |
234738ea KVA |
545 | struct device *dev = &pdev->dev; |
546 | int ret; | |
547 | ||
548 | phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); | |
549 | if (!phy) | |
550 | return -ENOMEM; | |
551 | ||
552 | phy->dev = dev; | |
553 | ||
1fe52122 KVA |
554 | ret = ti_pipe3_get_pll_base(phy); |
555 | if (ret) | |
556 | return ret; | |
234738ea | 557 | |
73bbc78e KVA |
558 | ret = ti_pipe3_get_sysctrl(phy); |
559 | if (ret) | |
560 | return ret; | |
234738ea KVA |
561 | |
562 | ret = ti_pipe3_get_clk(phy); | |
563 | if (ret) | |
564 | return ret; | |
565 | ||
57f6ce07 | 566 | platform_set_drvdata(pdev, phy); |
d65ff52e | 567 | pm_runtime_enable(dev); |
c934b361 RQ |
568 | |
569 | /* | |
570 | * Prevent auto-disable of refclk for SATA PHY due to Errata i783 | |
571 | */ | |
572 | if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { | |
573 | if (!IS_ERR(phy->refclk)) { | |
0a0830fe | 574 | clk_prepare_enable(phy->refclk); |
c934b361 RQ |
575 | phy->sata_refclk_enabled = true; |
576 | } | |
577 | } | |
a70143bb | 578 | |
d65ff52e | 579 | generic_phy = devm_phy_create(dev, NULL, &ops); |
a70143bb KVA |
580 | if (IS_ERR(generic_phy)) |
581 | return PTR_ERR(generic_phy); | |
582 | ||
583 | phy_set_drvdata(generic_phy, phy); | |
cc34ace7 KVA |
584 | |
585 | ti_pipe3_power_off(generic_phy); | |
586 | ||
d65ff52e | 587 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
a70143bb KVA |
588 | if (IS_ERR(phy_provider)) |
589 | return PTR_ERR(phy_provider); | |
590 | ||
57f6ce07 KVA |
591 | return 0; |
592 | } | |
593 | ||
a70143bb | 594 | static int ti_pipe3_remove(struct platform_device *pdev) |
57f6ce07 | 595 | { |
57f6ce07 KVA |
596 | pm_runtime_disable(&pdev->dev); |
597 | ||
598 | return 0; | |
599 | } | |
600 | ||
0a0830fe | 601 | static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy) |
7f33912d | 602 | { |
0a0830fe | 603 | int ret = 0; |
7f33912d | 604 | |
0a0830fe | 605 | if (!IS_ERR(phy->refclk)) { |
7f33912d RQ |
606 | ret = clk_prepare_enable(phy->refclk); |
607 | if (ret) { | |
608 | dev_err(phy->dev, "Failed to enable refclk %d\n", ret); | |
609 | return ret; | |
610 | } | |
7f33912d RQ |
611 | } |
612 | ||
1562864f RQ |
613 | if (!IS_ERR(phy->wkupclk)) { |
614 | ret = clk_prepare_enable(phy->wkupclk); | |
615 | if (ret) { | |
616 | dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); | |
0a0830fe | 617 | goto disable_refclk; |
1562864f | 618 | } |
57f6ce07 KVA |
619 | } |
620 | ||
99bbd48c KVA |
621 | if (!IS_ERR(phy->div_clk)) { |
622 | ret = clk_prepare_enable(phy->div_clk); | |
623 | if (ret) { | |
624 | dev_err(phy->dev, "Failed to enable div_clk %d\n", ret); | |
0a0830fe | 625 | goto disable_wkupclk; |
99bbd48c KVA |
626 | } |
627 | } | |
6e738432 | 628 | |
57f6ce07 KVA |
629 | return 0; |
630 | ||
0a0830fe | 631 | disable_wkupclk: |
99bbd48c KVA |
632 | if (!IS_ERR(phy->wkupclk)) |
633 | clk_disable_unprepare(phy->wkupclk); | |
634 | ||
0a0830fe | 635 | disable_refclk: |
1562864f RQ |
636 | if (!IS_ERR(phy->refclk)) |
637 | clk_disable_unprepare(phy->refclk); | |
57f6ce07 | 638 | |
6e738432 RQ |
639 | return ret; |
640 | } | |
641 | ||
642 | static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) | |
643 | { | |
6e738432 RQ |
644 | if (!IS_ERR(phy->wkupclk)) |
645 | clk_disable_unprepare(phy->wkupclk); | |
c934b361 | 646 | if (!IS_ERR(phy->refclk)) { |
0a0830fe | 647 | clk_disable_unprepare(phy->refclk); |
c934b361 RQ |
648 | /* |
649 | * SATA refclk needs an additional disable as we left it | |
650 | * on in probe to avoid Errata i783 | |
651 | */ | |
652 | if (phy->sata_refclk_enabled) { | |
653 | clk_disable_unprepare(phy->refclk); | |
654 | phy->sata_refclk_enabled = false; | |
655 | } | |
656 | } | |
657 | ||
6e738432 RQ |
658 | if (!IS_ERR(phy->div_clk)) |
659 | clk_disable_unprepare(phy->div_clk); | |
6e738432 RQ |
660 | } |
661 | ||
a70143bb | 662 | static const struct of_device_id ti_pipe3_id_table[] = { |
61f54674 RQ |
663 | { |
664 | .compatible = "ti,phy-usb3", | |
665 | .data = dpll_map_usb, | |
666 | }, | |
667 | { | |
668 | .compatible = "ti,omap-usb3", | |
669 | .data = dpll_map_usb, | |
670 | }, | |
671 | { | |
672 | .compatible = "ti,phy-pipe3-sata", | |
673 | .data = dpll_map_sata, | |
674 | }, | |
99bbd48c KVA |
675 | { |
676 | .compatible = "ti,phy-pipe3-pcie", | |
677 | }, | |
57f6ce07 KVA |
678 | {} |
679 | }; | |
a70143bb | 680 | MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); |
57f6ce07 | 681 | |
a70143bb KVA |
682 | static struct platform_driver ti_pipe3_driver = { |
683 | .probe = ti_pipe3_probe, | |
684 | .remove = ti_pipe3_remove, | |
57f6ce07 | 685 | .driver = { |
a70143bb | 686 | .name = "ti-pipe3", |
298fe56e | 687 | .of_match_table = ti_pipe3_id_table, |
57f6ce07 KVA |
688 | }, |
689 | }; | |
690 | ||
a70143bb | 691 | module_platform_driver(ti_pipe3_driver); |
57f6ce07 | 692 | |
dd64ad38 | 693 | MODULE_ALIAS("platform:ti_pipe3"); |
57f6ce07 | 694 | MODULE_AUTHOR("Texas Instruments Inc."); |
a70143bb | 695 | MODULE_DESCRIPTION("TI PIPE3 phy driver"); |
57f6ce07 | 696 | MODULE_LICENSE("GPL v2"); |