]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
982082d9 JJH |
2 | /* |
3 | * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ | |
4 | * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> | |
982082d9 JJH |
5 | */ |
6 | ||
7 | #include <common.h> | |
8 | #include <dm.h> | |
9 | #include <dm/device.h> | |
10 | #include <generic-phy.h> | |
11 | #include <asm/io.h> | |
12 | #include <asm/arch/sys_proto.h> | |
13 | #include <syscon.h> | |
14 | #include <regmap.h> | |
15 | ||
16 | /* PLLCTRL Registers */ | |
17 | #define PLL_STATUS 0x00000004 | |
18 | #define PLL_GO 0x00000008 | |
19 | #define PLL_CONFIGURATION1 0x0000000C | |
20 | #define PLL_CONFIGURATION2 0x00000010 | |
21 | #define PLL_CONFIGURATION3 0x00000014 | |
22 | #define PLL_CONFIGURATION4 0x00000020 | |
23 | ||
24 | #define PLL_REGM_MASK 0x001FFE00 | |
25 | #define PLL_REGM_SHIFT 9 | |
26 | #define PLL_REGM_F_MASK 0x0003FFFF | |
27 | #define PLL_REGM_F_SHIFT 0 | |
28 | #define PLL_REGN_MASK 0x000001FE | |
29 | #define PLL_REGN_SHIFT 1 | |
30 | #define PLL_SELFREQDCO_MASK 0x0000000E | |
31 | #define PLL_SELFREQDCO_SHIFT 1 | |
32 | #define PLL_SD_MASK 0x0003FC00 | |
33 | #define PLL_SD_SHIFT 10 | |
34 | #define SET_PLL_GO 0x1 | |
35 | #define PLL_TICOPWDN BIT(16) | |
36 | #define PLL_LDOPWDN BIT(15) | |
37 | #define PLL_LOCK 0x2 | |
38 | #define PLL_IDLE 0x1 | |
39 | ||
40 | /* Software rest for the SATA PLL (in CTRL_CORE_SMA_SW_0 register)*/ | |
41 | #define SATA_PLL_SOFT_RESET (1<<18) | |
42 | ||
43 | /* PHY POWER CONTROL Register */ | |
44 | #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 | |
45 | #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 0xE | |
46 | ||
47 | #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 | |
48 | #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 0x16 | |
49 | ||
50 | #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3 | |
51 | #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0 | |
52 | ||
53 | ||
54 | #define PLL_IDLE_TIME 100 /* in milliseconds */ | |
55 | #define PLL_LOCK_TIME 100 /* in milliseconds */ | |
56 | ||
57 | struct omap_pipe3 { | |
58 | void __iomem *pll_ctrl_base; | |
59 | void __iomem *power_reg; | |
60 | void __iomem *pll_reset_reg; | |
61 | struct pipe3_dpll_map *dpll_map; | |
62 | }; | |
63 | ||
64 | ||
65 | struct pipe3_dpll_params { | |
66 | u16 m; | |
67 | u8 n; | |
68 | u8 freq:3; | |
69 | u8 sd; | |
70 | u32 mf; | |
71 | }; | |
72 | ||
73 | struct pipe3_dpll_map { | |
74 | unsigned long rate; | |
75 | struct pipe3_dpll_params params; | |
76 | }; | |
77 | ||
78 | static inline u32 omap_pipe3_readl(void __iomem *addr, unsigned offset) | |
79 | { | |
80 | return readl(addr + offset); | |
81 | } | |
82 | ||
83 | static inline void omap_pipe3_writel(void __iomem *addr, unsigned offset, | |
84 | u32 data) | |
85 | { | |
86 | writel(data, addr + offset); | |
87 | } | |
88 | ||
89 | static struct pipe3_dpll_params *omap_pipe3_get_dpll_params(struct omap_pipe3 | |
90 | *pipe3) | |
91 | { | |
92 | u32 rate; | |
93 | struct pipe3_dpll_map *dpll_map = pipe3->dpll_map; | |
94 | ||
95 | rate = get_sys_clk_freq(); | |
96 | ||
97 | for (; dpll_map->rate; dpll_map++) { | |
98 | if (rate == dpll_map->rate) | |
99 | return &dpll_map->params; | |
100 | } | |
101 | ||
102 | printf("%s: No DPLL configuration for %u Hz SYS CLK\n", | |
103 | __func__, rate); | |
104 | return NULL; | |
105 | } | |
106 | ||
107 | static int omap_pipe3_wait_lock(struct omap_pipe3 *pipe3) | |
108 | { | |
109 | u32 val; | |
110 | int timeout = PLL_LOCK_TIME; | |
111 | ||
112 | do { | |
113 | mdelay(1); | |
114 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); | |
115 | if (val & PLL_LOCK) | |
116 | break; | |
117 | } while (--timeout); | |
118 | ||
119 | if (!(val & PLL_LOCK)) { | |
120 | printf("%s: DPLL failed to lock\n", __func__); | |
121 | return -EBUSY; | |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | static int omap_pipe3_dpll_program(struct omap_pipe3 *pipe3) | |
128 | { | |
129 | u32 val; | |
130 | struct pipe3_dpll_params *dpll_params; | |
131 | ||
132 | dpll_params = omap_pipe3_get_dpll_params(pipe3); | |
133 | if (!dpll_params) { | |
134 | printf("%s: Invalid DPLL parameters\n", __func__); | |
135 | return -EINVAL; | |
136 | } | |
137 | ||
138 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1); | |
139 | val &= ~PLL_REGN_MASK; | |
140 | val |= dpll_params->n << PLL_REGN_SHIFT; | |
141 | omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val); | |
142 | ||
143 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2); | |
144 | val &= ~PLL_SELFREQDCO_MASK; | |
145 | val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; | |
146 | omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val); | |
147 | ||
148 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1); | |
149 | val &= ~PLL_REGM_MASK; | |
150 | val |= dpll_params->m << PLL_REGM_SHIFT; | |
151 | omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val); | |
152 | ||
153 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION4); | |
154 | val &= ~PLL_REGM_F_MASK; | |
155 | val |= dpll_params->mf << PLL_REGM_F_SHIFT; | |
156 | omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION4, val); | |
157 | ||
158 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION3); | |
159 | val &= ~PLL_SD_MASK; | |
160 | val |= dpll_params->sd << PLL_SD_SHIFT; | |
161 | omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION3, val); | |
162 | ||
163 | omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_GO, SET_PLL_GO); | |
164 | ||
165 | return omap_pipe3_wait_lock(pipe3); | |
166 | } | |
167 | ||
168 | static void omap_control_pipe3_power(struct omap_pipe3 *pipe3, int on) | |
169 | { | |
170 | u32 val, rate; | |
171 | ||
172 | val = readl(pipe3->power_reg); | |
173 | ||
174 | rate = get_sys_clk_freq(); | |
175 | rate = rate/1000000; | |
176 | ||
177 | if (on) { | |
178 | val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | | |
179 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); | |
180 | val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << | |
181 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; | |
182 | val |= rate << | |
183 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; | |
184 | } else { | |
185 | val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; | |
186 | val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << | |
187 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; | |
188 | } | |
189 | ||
190 | writel(val, pipe3->power_reg); | |
191 | } | |
192 | ||
193 | static int pipe3_init(struct phy *phy) | |
194 | { | |
195 | int ret; | |
196 | u32 val; | |
197 | struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); | |
198 | ||
199 | /* Program the DPLL only if not locked */ | |
200 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); | |
201 | if (!(val & PLL_LOCK)) { | |
202 | ret = omap_pipe3_dpll_program(pipe3); | |
203 | if (ret) | |
204 | return ret; | |
205 | } else { | |
206 | /* else just bring it out of IDLE mode */ | |
207 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, | |
208 | PLL_CONFIGURATION2); | |
209 | if (val & PLL_IDLE) { | |
210 | val &= ~PLL_IDLE; | |
211 | omap_pipe3_writel(pipe3->pll_ctrl_base, | |
212 | PLL_CONFIGURATION2, val); | |
213 | ret = omap_pipe3_wait_lock(pipe3); | |
214 | if (ret) | |
215 | return ret; | |
216 | } | |
217 | } | |
218 | return 0; | |
219 | } | |
220 | ||
221 | static int pipe3_power_on(struct phy *phy) | |
222 | { | |
223 | struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); | |
224 | ||
225 | /* Power up the PHY */ | |
226 | omap_control_pipe3_power(pipe3, 1); | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | static int pipe3_power_off(struct phy *phy) | |
232 | { | |
233 | struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); | |
234 | ||
235 | /* Power down the PHY */ | |
236 | omap_control_pipe3_power(pipe3, 0); | |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
241 | static int pipe3_exit(struct phy *phy) | |
242 | { | |
243 | u32 val; | |
244 | int timeout = PLL_IDLE_TIME; | |
245 | struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); | |
246 | ||
247 | pipe3_power_off(phy); | |
248 | ||
249 | /* Put DPLL in IDLE mode */ | |
250 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2); | |
251 | val |= PLL_IDLE; | |
252 | omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val); | |
253 | ||
254 | /* wait for LDO and Oscillator to power down */ | |
255 | do { | |
256 | mdelay(1); | |
257 | val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); | |
258 | if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) | |
259 | break; | |
260 | } while (--timeout); | |
261 | ||
262 | if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { | |
9b643e31 | 263 | pr_err("%s: Failed to power down DPLL: PLL_STATUS 0x%x\n", |
982082d9 JJH |
264 | __func__, val); |
265 | return -EBUSY; | |
266 | } | |
267 | ||
268 | val = readl(pipe3->pll_reset_reg); | |
269 | writel(val | SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg); | |
270 | mdelay(1); | |
271 | writel(val & ~SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg); | |
272 | return 0; | |
273 | } | |
274 | ||
275 | static void *get_reg(struct udevice *dev, const char *name) | |
276 | { | |
277 | struct udevice *syscon; | |
278 | struct regmap *regmap; | |
279 | const fdt32_t *cell; | |
280 | int len, err; | |
281 | void *base; | |
282 | ||
283 | err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, | |
284 | name, &syscon); | |
285 | if (err) { | |
9b643e31 | 286 | pr_err("unable to find syscon device for %s (%d)\n", |
982082d9 JJH |
287 | name, err); |
288 | return NULL; | |
289 | } | |
290 | ||
291 | regmap = syscon_get_regmap(syscon); | |
292 | if (IS_ERR(regmap)) { | |
9b643e31 | 293 | pr_err("unable to find regmap for %s (%ld)\n", |
982082d9 JJH |
294 | name, PTR_ERR(regmap)); |
295 | return NULL; | |
296 | } | |
297 | ||
da409ccc | 298 | cell = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), name, |
982082d9 JJH |
299 | &len); |
300 | if (len < 2*sizeof(fdt32_t)) { | |
9b643e31 | 301 | pr_err("offset not available for %s\n", name); |
982082d9 JJH |
302 | return NULL; |
303 | } | |
304 | ||
305 | base = regmap_get_range(regmap, 0); | |
306 | if (!base) | |
307 | return NULL; | |
308 | ||
309 | return fdtdec_get_number(cell + 1, 1) + base; | |
310 | } | |
311 | ||
312 | static int pipe3_phy_probe(struct udevice *dev) | |
313 | { | |
314 | fdt_addr_t addr; | |
315 | fdt_size_t sz; | |
316 | struct omap_pipe3 *pipe3 = dev_get_priv(dev); | |
317 | ||
a821c4af | 318 | addr = devfdt_get_addr_size_index(dev, 2, &sz); |
982082d9 | 319 | if (addr == FDT_ADDR_T_NONE) { |
9b643e31 | 320 | pr_err("missing pll ctrl address\n"); |
982082d9 JJH |
321 | return -EINVAL; |
322 | } | |
323 | ||
324 | pipe3->pll_ctrl_base = map_physmem(addr, sz, MAP_NOCACHE); | |
325 | if (!pipe3->pll_ctrl_base) { | |
9b643e31 | 326 | pr_err("unable to remap pll ctrl\n"); |
982082d9 JJH |
327 | return -EINVAL; |
328 | } | |
329 | ||
330 | pipe3->power_reg = get_reg(dev, "syscon-phy-power"); | |
331 | if (!pipe3->power_reg) | |
332 | return -EINVAL; | |
333 | ||
334 | pipe3->pll_reset_reg = get_reg(dev, "syscon-pllreset"); | |
335 | if (!pipe3->pll_reset_reg) | |
336 | return -EINVAL; | |
337 | ||
338 | pipe3->dpll_map = (struct pipe3_dpll_map *)dev_get_driver_data(dev); | |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
343 | static struct pipe3_dpll_map dpll_map_sata[] = { | |
344 | {12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */ | |
345 | {16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */ | |
346 | {19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */ | |
347 | {20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */ | |
348 | {26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */ | |
349 | {38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */ | |
350 | { }, /* Terminator */ | |
351 | }; | |
352 | ||
353 | static const struct udevice_id pipe3_phy_ids[] = { | |
354 | { .compatible = "ti,phy-pipe3-sata", .data = (ulong)&dpll_map_sata }, | |
355 | { } | |
356 | }; | |
357 | ||
358 | static struct phy_ops pipe3_phy_ops = { | |
359 | .init = pipe3_init, | |
360 | .power_on = pipe3_power_on, | |
361 | .power_off = pipe3_power_off, | |
362 | .exit = pipe3_exit, | |
363 | }; | |
364 | ||
365 | U_BOOT_DRIVER(pipe3_phy) = { | |
366 | .name = "pipe3_phy", | |
367 | .id = UCLASS_PHY, | |
368 | .of_match = pipe3_phy_ids, | |
369 | .ops = &pipe3_phy_ops, | |
370 | .probe = pipe3_phy_probe, | |
371 | .priv_auto_alloc_size = sizeof(struct omap_pipe3), | |
372 | }; |