]>
Commit | Line | Data |
---|---|---|
9c4b64fb RQ |
1 | /* |
2 | * TI PIPE3 PHY | |
3 | * | |
4 | * (C) Copyright 2013 | |
5 | * Texas Instruments, <www.ti.com> | |
6 | * | |
7 | * SPDX-License-Identifier: GPL-2.0+ | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
11 | #include <sata.h> | |
12 | #include <asm/arch/clock.h> | |
13 | #include <asm/arch/sys_proto.h> | |
14 | #include <asm/io.h> | |
1221ce45 | 15 | #include <linux/errno.h> |
9c4b64fb RQ |
16 | #include "pipe3-phy.h" |
17 | ||
18 | /* PLLCTRL Registers */ | |
19 | #define PLL_STATUS 0x00000004 | |
20 | #define PLL_GO 0x00000008 | |
21 | #define PLL_CONFIGURATION1 0x0000000C | |
22 | #define PLL_CONFIGURATION2 0x00000010 | |
23 | #define PLL_CONFIGURATION3 0x00000014 | |
24 | #define PLL_CONFIGURATION4 0x00000020 | |
25 | ||
26 | #define PLL_REGM_MASK 0x001FFE00 | |
27 | #define PLL_REGM_SHIFT 9 | |
28 | #define PLL_REGM_F_MASK 0x0003FFFF | |
29 | #define PLL_REGM_F_SHIFT 0 | |
30 | #define PLL_REGN_MASK 0x000001FE | |
31 | #define PLL_REGN_SHIFT 1 | |
32 | #define PLL_SELFREQDCO_MASK 0x0000000E | |
33 | #define PLL_SELFREQDCO_SHIFT 1 | |
34 | #define PLL_SD_MASK 0x0003FC00 | |
35 | #define PLL_SD_SHIFT 10 | |
36 | #define SET_PLL_GO 0x1 | |
37 | #define PLL_TICOPWDN BIT(16) | |
38 | #define PLL_LDOPWDN BIT(15) | |
39 | #define PLL_LOCK 0x2 | |
40 | #define PLL_IDLE 0x1 | |
41 | ||
42 | /* PHY POWER CONTROL Register */ | |
43 | #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 | |
44 | #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 0xE | |
45 | ||
46 | #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 | |
47 | #define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 0x16 | |
48 | ||
49 | #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3 | |
50 | #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0 | |
51 | ||
52 | ||
53 | #define PLL_IDLE_TIME 100 /* in milliseconds */ | |
54 | #define PLL_LOCK_TIME 100 /* in milliseconds */ | |
55 | ||
56 | static inline u32 omap_pipe3_readl(void __iomem *addr, unsigned offset) | |
57 | { | |
58 | return __raw_readl(addr + offset); | |
59 | } | |
60 | ||
61 | static inline void omap_pipe3_writel(void __iomem *addr, unsigned offset, | |
62 | u32 data) | |
63 | { | |
64 | __raw_writel(data, addr + offset); | |
65 | } | |
66 | ||
67 | static struct pipe3_dpll_params *omap_pipe3_get_dpll_params(struct omap_pipe3 | |
68 | *pipe3) | |
69 | { | |
70 | u32 rate; | |
71 | struct pipe3_dpll_map *dpll_map = pipe3->dpll_map; | |
72 | ||
73 | rate = get_sys_clk_freq(); | |
74 | ||
75 | for (; dpll_map->rate; dpll_map++) { | |
76 | if (rate == dpll_map->rate) | |
77 | return &dpll_map->params; | |
78 | } | |
79 | ||
80 | printf("%s: No DPLL configuration for %u Hz SYS CLK\n", | |
81 | __func__, rate); | |
82 | return NULL; | |
83 | } | |
84 | ||
85 | ||
86 | static int omap_pipe3_wait_lock(struct omap_pipe3 *phy) | |
87 | { | |
88 | u32 val; | |
89 | int timeout = PLL_LOCK_TIME; | |
90 | ||
91 | do { | |
92 | mdelay(1); | |
93 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); | |
94 | if (val & PLL_LOCK) | |
95 | break; | |
96 | } while (--timeout); | |
97 | ||
98 | if (!(val & PLL_LOCK)) { | |
99 | printf("%s: DPLL failed to lock\n", __func__); | |
100 | return -EBUSY; | |
101 | } | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | static int omap_pipe3_dpll_program(struct omap_pipe3 *phy) | |
107 | { | |
108 | u32 val; | |
109 | struct pipe3_dpll_params *dpll_params; | |
110 | ||
111 | dpll_params = omap_pipe3_get_dpll_params(phy); | |
112 | if (!dpll_params) { | |
113 | printf("%s: Invalid DPLL parameters\n", __func__); | |
114 | return -EINVAL; | |
115 | } | |
116 | ||
117 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); | |
118 | val &= ~PLL_REGN_MASK; | |
119 | val |= dpll_params->n << PLL_REGN_SHIFT; | |
120 | omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); | |
121 | ||
122 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); | |
123 | val &= ~PLL_SELFREQDCO_MASK; | |
124 | val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; | |
125 | omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); | |
126 | ||
127 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); | |
128 | val &= ~PLL_REGM_MASK; | |
129 | val |= dpll_params->m << PLL_REGM_SHIFT; | |
130 | omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); | |
131 | ||
132 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); | |
133 | val &= ~PLL_REGM_F_MASK; | |
134 | val |= dpll_params->mf << PLL_REGM_F_SHIFT; | |
135 | omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); | |
136 | ||
137 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); | |
138 | val &= ~PLL_SD_MASK; | |
139 | val |= dpll_params->sd << PLL_SD_SHIFT; | |
140 | omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); | |
141 | ||
142 | omap_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); | |
143 | ||
144 | return omap_pipe3_wait_lock(phy); | |
145 | } | |
146 | ||
147 | static void omap_control_phy_power(struct omap_pipe3 *phy, int on) | |
148 | { | |
149 | u32 val, rate; | |
150 | ||
151 | val = readl(phy->power_reg); | |
152 | ||
153 | rate = get_sys_clk_freq(); | |
154 | rate = rate/1000000; | |
155 | ||
156 | if (on) { | |
157 | val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | | |
158 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); | |
159 | val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << | |
160 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; | |
161 | val |= rate << | |
162 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; | |
163 | } else { | |
164 | val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; | |
165 | val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << | |
166 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; | |
167 | } | |
168 | ||
169 | writel(val, phy->power_reg); | |
170 | } | |
171 | ||
172 | int phy_pipe3_power_on(struct omap_pipe3 *phy) | |
173 | { | |
174 | int ret; | |
175 | u32 val; | |
176 | ||
177 | /* Program the DPLL only if not locked */ | |
178 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); | |
179 | if (!(val & PLL_LOCK)) { | |
180 | ret = omap_pipe3_dpll_program(phy); | |
181 | if (ret) | |
182 | return ret; | |
183 | } else { | |
184 | /* else just bring it out of IDLE mode */ | |
185 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); | |
186 | if (val & PLL_IDLE) { | |
187 | val &= ~PLL_IDLE; | |
188 | omap_pipe3_writel(phy->pll_ctrl_base, | |
189 | PLL_CONFIGURATION2, val); | |
190 | ret = omap_pipe3_wait_lock(phy); | |
191 | if (ret) | |
192 | return ret; | |
193 | } | |
194 | } | |
195 | ||
196 | /* Power up the PHY */ | |
197 | omap_control_phy_power(phy, 1); | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | int phy_pipe3_power_off(struct omap_pipe3 *phy) | |
203 | { | |
204 | u32 val; | |
205 | int timeout = PLL_IDLE_TIME; | |
206 | ||
207 | /* Power down the PHY */ | |
208 | omap_control_phy_power(phy, 0); | |
209 | ||
210 | /* Put DPLL in IDLE mode */ | |
211 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); | |
212 | val |= PLL_IDLE; | |
213 | omap_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); | |
214 | ||
215 | /* wait for LDO and Oscillator to power down */ | |
216 | do { | |
217 | mdelay(1); | |
218 | val = omap_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); | |
219 | if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) | |
220 | break; | |
221 | } while (--timeout); | |
222 | ||
223 | if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { | |
224 | printf("%s: Failed to power down DPLL: PLL_STATUS 0x%x\n", | |
225 | __func__, val); | |
226 | return -EBUSY; | |
227 | } | |
228 | ||
229 | return 0; | |
230 | } | |
231 |