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