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