1*58bfdd0cSPulkoMandy /*
2*58bfdd0cSPulkoMandy * Copyright 2024, Haiku, Inc. All Rights Reserved.
3*58bfdd0cSPulkoMandy * Distributed under the terms of the MIT License.
4*58bfdd0cSPulkoMandy */
5*58bfdd0cSPulkoMandy
6*58bfdd0cSPulkoMandy #include "TigerLakePLL.h"
7*58bfdd0cSPulkoMandy
8*58bfdd0cSPulkoMandy #include "accelerant.h"
9*58bfdd0cSPulkoMandy
10*58bfdd0cSPulkoMandy #include <math.h>
11*58bfdd0cSPulkoMandy
12*58bfdd0cSPulkoMandy
13*58bfdd0cSPulkoMandy #undef TRACE
14*58bfdd0cSPulkoMandy #define TRACE_PLL
15*58bfdd0cSPulkoMandy #ifdef TRACE_PLL
16*58bfdd0cSPulkoMandy # define TRACE(x...) _sPrintf("intel_extreme: " x)
17*58bfdd0cSPulkoMandy #else
18*58bfdd0cSPulkoMandy # define TRACE(x...)
19*58bfdd0cSPulkoMandy #endif
20*58bfdd0cSPulkoMandy
21*58bfdd0cSPulkoMandy #define ERROR(x...) _sPrintf("intel_extreme: " x)
22*58bfdd0cSPulkoMandy #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
23*58bfdd0cSPulkoMandy
24*58bfdd0cSPulkoMandy
25*58bfdd0cSPulkoMandy /**
26*58bfdd0cSPulkoMandy * Compute the best PLL parameters for a given symbol clock frequency for a DVI or HDMI port.
27*58bfdd0cSPulkoMandy *
28*58bfdd0cSPulkoMandy * This is the algorithm documented in Intel Documentation: IHD-OS-TGL-Vol 12-12.21, page 182
29*58bfdd0cSPulkoMandy *
30*58bfdd0cSPulkoMandy * The clock generation on Tiger Lake is in two steps: first, a DCO generates a fractional
31*58bfdd0cSPulkoMandy * multiplication of the reference clock (in the GHz range). Then, 3 dividers bring this back into
32*58bfdd0cSPulkoMandy * the symbol clock frequency range.
33*58bfdd0cSPulkoMandy *
34*58bfdd0cSPulkoMandy * Reference clock (24 or 19.2MHz, as defined in DSSM Reference Frequency register)
35*58bfdd0cSPulkoMandy * ||
36*58bfdd0cSPulkoMandy * vv
37*58bfdd0cSPulkoMandy * DCO (multiply by non-integer value defined in DPLL_CFGCR0 register)
38*58bfdd0cSPulkoMandy * ||
39*58bfdd0cSPulkoMandy * vv
40*58bfdd0cSPulkoMandy * "DCO frequency" in the range 7998 - 10000 MHz
41*58bfdd0cSPulkoMandy * ||
42*58bfdd0cSPulkoMandy * vv
43*58bfdd0cSPulkoMandy * Divide by P, Q, and K
44*58bfdd0cSPulkoMandy * ||
45*58bfdd0cSPulkoMandy * vv
46*58bfdd0cSPulkoMandy * AFE clock (PLL output)
47*58bfdd0cSPulkoMandy * ||
48*58bfdd0cSPulkoMandy * vv
49*58bfdd0cSPulkoMandy * Divide by 5 (fixed)
50*58bfdd0cSPulkoMandy * ||
51*58bfdd0cSPulkoMandy * vv
52*58bfdd0cSPulkoMandy * Symbol clock (same as Pixel clock for 24-bit RGB)
53*58bfdd0cSPulkoMandy *
54*58bfdd0cSPulkoMandy * The algorithm to configure this is:
55*58bfdd0cSPulkoMandy * - Iterate over all allowed values for the divider obtained by P, Q and K
56*58bfdd0cSPulkoMandy * - Determine the one that results in the DCO frequency being as close as possible to 8999MHz
57*58bfdd0cSPulkoMandy * - Compute the corresponding values for P, Q and K and the DCO multiplier
58*58bfdd0cSPulkoMandy *
59*58bfdd0cSPulkoMandy * Since the DCO is a fractional multiplier (it can multiply by non-integer values), it will always
60*58bfdd0cSPulkoMandy * be possible to set the DCO to a "close enough" value in its available range. The only constraint
61*58bfdd0cSPulkoMandy * is getting it as close as possible to the midpoint (8999MHz), and at least have it in the
62*58bfdd0cSPulkoMandy * allowed range (7998 to 10000MHz). If this is not possible (too low or too high pixel clock), a
63*58bfdd0cSPulkoMandy * different video mode or setup will be needed (for example, enable dual link DVI to divide the
64*58bfdd0cSPulkoMandy * clock by two).
65*58bfdd0cSPulkoMandy *
66*58bfdd0cSPulkoMandy * This also means that this algorithm is independant of the initial reference frequency: there
67*58bfdd0cSPulkoMandy * will always be a way to setup the DCO so that it outputs the frequency computed here, no matter
68*58bfdd0cSPulkoMandy * what the input clock is.
69*58bfdd0cSPulkoMandy *
70*58bfdd0cSPulkoMandy * Unlinke in previous hardware generations, there is no need to satisfy multiple constraints at
71*58bfdd0cSPulkoMandy * the same time because of several stages of dividers and multipliers each with their own
72*58bfdd0cSPulkoMandy * frequency range.
73*58bfdd0cSPulkoMandy *
74*58bfdd0cSPulkoMandy * DCO multiplier = DCO integer + DCO fraction / 2^15
75*58bfdd0cSPulkoMandy * Symbol clock frequency = DCO multiplier * RefFreq in MHz / (5 * Pdiv * Qdiv * Kdiv)
76*58bfdd0cSPulkoMandy *
77*58bfdd0cSPulkoMandy * The symbol clock is the clock of the DVI/HDMI port. It defines how much time is needed to send
78*58bfdd0cSPulkoMandy * one "symbol", which corresponds to 8 bits of useful data for each channel (Red, Green and Blue).
79*58bfdd0cSPulkoMandy *
80*58bfdd0cSPulkoMandy * In our case (8 bit RGB videomode), the symbol clock is equal to the pixel rate. It would need
81*58bfdd0cSPulkoMandy * to be adjusted for 10 and 12-bit modes (more bits per pixel) as well as for YUV420 modes (the U
82*58bfdd0cSPulkoMandy * and V parts are sent only for some pixels, reducing the total bandwidth).
83*58bfdd0cSPulkoMandy *
84*58bfdd0cSPulkoMandy * @param[in] freq Desired symbol clock frequency in kHz
85*58bfdd0cSPulkoMandy * @param[out] Pdiv, Qdiv, Kdiv: dividers for the PLL
86*58bfdd0cSPulkoMandy * @param[out] bestdco Required DCO frequency, in the range 7998 to 10000, in MHz
87*58bfdd0cSPulkoMandy */
88*58bfdd0cSPulkoMandy bool
ComputeHdmiDpll(int freq,int * Pdiv,int * Qdiv,int * Kdiv,float * bestdco)89*58bfdd0cSPulkoMandy ComputeHdmiDpll(int freq, int* Pdiv, int* Qdiv, int* Kdiv, float* bestdco)
90*58bfdd0cSPulkoMandy {
91*58bfdd0cSPulkoMandy int bestdiv = 0;
92*58bfdd0cSPulkoMandy float dco = 0, dcocentrality = 0;
93*58bfdd0cSPulkoMandy float bestdcocentrality = 999999;
94*58bfdd0cSPulkoMandy
95*58bfdd0cSPulkoMandy // The allowed values for the divider depending on the allowed values for P, Q, and K:
96*58bfdd0cSPulkoMandy // - P can be 2, 3, 5 or 7
97*58bfdd0cSPulkoMandy // - K can be 1, 2, or 3
98*58bfdd0cSPulkoMandy // - Q can be 1 to 255 if K = 2. Otherwise, Q must be 1.
99*58bfdd0cSPulkoMandy // Not all possible combinations are listed here, more can be added if needed to reach lower
100*58bfdd0cSPulkoMandy // resolutions and refresh rates (probably not so interesting, this already allows to reach
101*58bfdd0cSPulkoMandy // frequencies low enough for all practical uses in a standard setup).
102*58bfdd0cSPulkoMandy const int dividerlist[] = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 30, 32, 36,
103*58bfdd0cSPulkoMandy 40, 42, 44, 48, 50, 52, 54, 56, 60, 64, 66, 68, 70, 72, 76, 78, 80, 84, 88, 90, 92,
104*58bfdd0cSPulkoMandy 96, 98, 100, 102, 3, 5, 7, 9, 15, 21 };
105*58bfdd0cSPulkoMandy const float dcomin = 7998;
106*58bfdd0cSPulkoMandy const float dcomax = 10000;
107*58bfdd0cSPulkoMandy const float dcomid = (dcomin + dcomax) / 2;
108*58bfdd0cSPulkoMandy
109*58bfdd0cSPulkoMandy float afeclk = (5 * freq) / 1000;
110*58bfdd0cSPulkoMandy
111*58bfdd0cSPulkoMandy for (size_t i = 0; i < B_COUNT_OF(dividerlist); i++) {
112*58bfdd0cSPulkoMandy int div = dividerlist[i];
113*58bfdd0cSPulkoMandy dco = afeclk * div;
114*58bfdd0cSPulkoMandy if (dco <= dcomax && dco >= dcomin) {
115*58bfdd0cSPulkoMandy dcocentrality = fabs(dco - dcomid);
116*58bfdd0cSPulkoMandy if (dcocentrality < bestdcocentrality) {
117*58bfdd0cSPulkoMandy bestdcocentrality = dcocentrality;
118*58bfdd0cSPulkoMandy bestdiv = div;
119*58bfdd0cSPulkoMandy *bestdco = dco;
120*58bfdd0cSPulkoMandy }
121*58bfdd0cSPulkoMandy }
122*58bfdd0cSPulkoMandy }
123*58bfdd0cSPulkoMandy
124*58bfdd0cSPulkoMandy if (bestdiv != 0) {
125*58bfdd0cSPulkoMandy // Good divider found
126*58bfdd0cSPulkoMandy if (bestdiv % 2 == 0) {
127*58bfdd0cSPulkoMandy // Divider is even
128*58bfdd0cSPulkoMandy if (bestdiv == 2) {
129*58bfdd0cSPulkoMandy *Pdiv = 2;
130*58bfdd0cSPulkoMandy *Qdiv = 1;
131*58bfdd0cSPulkoMandy *Kdiv = 1;
132*58bfdd0cSPulkoMandy } else if (bestdiv % 4 == 0) {
133*58bfdd0cSPulkoMandy *Pdiv = 2;
134*58bfdd0cSPulkoMandy *Qdiv = bestdiv / 4;
135*58bfdd0cSPulkoMandy *Kdiv = 2;
136*58bfdd0cSPulkoMandy } else if (bestdiv % 6 == 0) {
137*58bfdd0cSPulkoMandy *Pdiv = 3;
138*58bfdd0cSPulkoMandy *Qdiv = bestdiv / 6;
139*58bfdd0cSPulkoMandy *Kdiv = 2;
140*58bfdd0cSPulkoMandy } else if (bestdiv % 5 == 0) {
141*58bfdd0cSPulkoMandy *Pdiv = 5;
142*58bfdd0cSPulkoMandy *Qdiv = bestdiv / 10;
143*58bfdd0cSPulkoMandy *Kdiv = 2;
144*58bfdd0cSPulkoMandy } else if (bestdiv % 14 == 0) {
145*58bfdd0cSPulkoMandy *Pdiv = 7;
146*58bfdd0cSPulkoMandy *Qdiv = bestdiv / 14;
147*58bfdd0cSPulkoMandy *Kdiv = 2;
148*58bfdd0cSPulkoMandy }
149*58bfdd0cSPulkoMandy } else {
150*58bfdd0cSPulkoMandy // Divider is odd
151*58bfdd0cSPulkoMandy if (bestdiv == 3 || bestdiv == 5 || bestdiv == 7) {
152*58bfdd0cSPulkoMandy *Pdiv = bestdiv;
153*58bfdd0cSPulkoMandy *Qdiv = 1;
154*58bfdd0cSPulkoMandy *Kdiv = 1;
155*58bfdd0cSPulkoMandy } else {
156*58bfdd0cSPulkoMandy // Divider is 9, 15, or 21
157*58bfdd0cSPulkoMandy *Pdiv = bestdiv / 3;
158*58bfdd0cSPulkoMandy *Qdiv = 1;
159*58bfdd0cSPulkoMandy *Kdiv = 3;
160*58bfdd0cSPulkoMandy }
161*58bfdd0cSPulkoMandy }
162*58bfdd0cSPulkoMandy
163*58bfdd0cSPulkoMandy // SUCCESS
164*58bfdd0cSPulkoMandy return true;
165*58bfdd0cSPulkoMandy } else {
166*58bfdd0cSPulkoMandy // No good divider found
167*58bfdd0cSPulkoMandy // FAIL, try a different frequency (different video mode)
168*58bfdd0cSPulkoMandy return false;
169*58bfdd0cSPulkoMandy }
170*58bfdd0cSPulkoMandy }
171*58bfdd0cSPulkoMandy
172*58bfdd0cSPulkoMandy
173*58bfdd0cSPulkoMandy /*! In the case of DisplayPort, the interface between the computer and the display is not just a
174*58bfdd0cSPulkoMandy * stream of pixels, but instead a packetized link. This means the interface does not need to be
175*58bfdd0cSPulkoMandy * running in sync with the pixel clock. Instead, a selection of well-defined frequencies are used.
176*58bfdd0cSPulkoMandy *
177*58bfdd0cSPulkoMandy * This also would allow to use a "spread spectrum" clock, reducing interferences without degrading
178*58bfdd0cSPulkoMandy * picture quality.
179*58bfdd0cSPulkoMandy *
180*58bfdd0cSPulkoMandy * Here we just set it to the isecond lowest predefined frequency of 2.7GHz, which will be enough
181*58bfdd0cSPulkoMandy * for displays up to full HD, and a little more.
182*58bfdd0cSPulkoMandy *
183*58bfdd0cSPulkoMandy * TODO decide when we have to use one of the higher frequencies. See "DisplayPort Mode PLL values"
184*58bfdd0cSPulkoMandy * in IHD-OS-TGL-Vol 12-12.21, page 178. However, my machine uses a slightly different value than
185*58bfdd0cSPulkoMandy * what's in Intel datasheets (with Intel values, bestdco should be 8100 and not 8090). I'm not
186*58bfdd0cSPulkoMandy * sure why that is so, possibly they shift the fractional value in the CFGR0 register by 9 bits
187*58bfdd0cSPulkoMandy * instead of 10? But replicating what my BIOS does here allows me to skip the PLL
188*58bfdd0cSPulkoMandy * programming, a good idea, because it seems we don't yet know how to properly disable and
189*58bfdd0cSPulkoMandy * re-train displayport once it is switched off.
190*58bfdd0cSPulkoMandy */
191*58bfdd0cSPulkoMandy bool
ComputeDisplayPortDpll(int freq,int * Pdiv,int * Qdiv,int * Kdiv,float * bestdco)192*58bfdd0cSPulkoMandy ComputeDisplayPortDpll(int freq, int* Pdiv, int* Qdiv, int* Kdiv, float* bestdco)
193*58bfdd0cSPulkoMandy {
194*58bfdd0cSPulkoMandy *Pdiv = 3;
195*58bfdd0cSPulkoMandy *Qdiv = 1;
196*58bfdd0cSPulkoMandy *Kdiv = 2;
197*58bfdd0cSPulkoMandy *bestdco = 8090;
198*58bfdd0cSPulkoMandy
199*58bfdd0cSPulkoMandy return true;
200*58bfdd0cSPulkoMandy }
201*58bfdd0cSPulkoMandy
202*58bfdd0cSPulkoMandy
203*58bfdd0cSPulkoMandy /*! Actually program the computed values (from the functions above) into the PLL, and start it.
204*58bfdd0cSPulkoMandy *
205*58bfdd0cSPulkoMandy * TODO: detect if the PLL is already running at the right frequency, and in that case, skip
206*58bfdd0cSPulkoMandy * reprogramming it altogether. In the case of DisplayPort, most often there is no need to change
207*58bfdd0cSPulkoMandy * anything once the clock has been initially set.
208*58bfdd0cSPulkoMandy */
209*58bfdd0cSPulkoMandy status_t
ProgramPLL(int which,int Pdiv,int Qdiv,int Kdiv,float dco)210*58bfdd0cSPulkoMandy ProgramPLL(int which, int Pdiv, int Qdiv, int Kdiv, float dco)
211*58bfdd0cSPulkoMandy {
212*58bfdd0cSPulkoMandy // Set up the registers for PLL access for the requested PLL
213*58bfdd0cSPulkoMandy uint32 DPLL_CFGCR0;
214*58bfdd0cSPulkoMandy uint32 DPLL_CFGCR1;
215*58bfdd0cSPulkoMandy uint32 DPLL_ENABLE;
216*58bfdd0cSPulkoMandy uint32 DPLL_SPREAD_SPECTRUM;
217*58bfdd0cSPulkoMandy
218*58bfdd0cSPulkoMandy switch (which) {
219*58bfdd0cSPulkoMandy case 0:
220*58bfdd0cSPulkoMandy DPLL_ENABLE = TGL_DPLL0_ENABLE;
221*58bfdd0cSPulkoMandy DPLL_SPREAD_SPECTRUM = TGL_DPLL0_SPREAD_SPECTRUM;
222*58bfdd0cSPulkoMandy DPLL_CFGCR0 = TGL_DPLL0_CFGCR0;
223*58bfdd0cSPulkoMandy DPLL_CFGCR1 = TGL_DPLL0_CFGCR1;
224*58bfdd0cSPulkoMandy break;
225*58bfdd0cSPulkoMandy case 1:
226*58bfdd0cSPulkoMandy DPLL_ENABLE = TGL_DPLL1_ENABLE;
227*58bfdd0cSPulkoMandy DPLL_SPREAD_SPECTRUM = TGL_DPLL1_SPREAD_SPECTRUM;
228*58bfdd0cSPulkoMandy DPLL_CFGCR0 = TGL_DPLL1_CFGCR0;
229*58bfdd0cSPulkoMandy DPLL_CFGCR1 = TGL_DPLL1_CFGCR1;
230*58bfdd0cSPulkoMandy break;
231*58bfdd0cSPulkoMandy case 4:
232*58bfdd0cSPulkoMandy DPLL_ENABLE = TGL_DPLL4_ENABLE;
233*58bfdd0cSPulkoMandy DPLL_SPREAD_SPECTRUM = TGL_DPLL4_SPREAD_SPECTRUM;
234*58bfdd0cSPulkoMandy DPLL_CFGCR0 = TGL_DPLL4_CFGCR0;
235*58bfdd0cSPulkoMandy DPLL_CFGCR1 = TGL_DPLL4_CFGCR1;
236*58bfdd0cSPulkoMandy break;
237*58bfdd0cSPulkoMandy default:
238*58bfdd0cSPulkoMandy return B_BAD_VALUE;
239*58bfdd0cSPulkoMandy }
240*58bfdd0cSPulkoMandy
241*58bfdd0cSPulkoMandy // Find the reference frequency (24 or 19.2MHz)
242*58bfdd0cSPulkoMandy int ref_khz = gInfo->shared_info->pll_info.reference_frequency;
243*58bfdd0cSPulkoMandy
244*58bfdd0cSPulkoMandy // There is an automatic divide-by-two in this case
245*58bfdd0cSPulkoMandy if (ref_khz == 38400)
246*58bfdd0cSPulkoMandy ref_khz = 19200;
247*58bfdd0cSPulkoMandy
248*58bfdd0cSPulkoMandy float ref = ref_khz / 1000.0f;
249*58bfdd0cSPulkoMandy
250*58bfdd0cSPulkoMandy // Compute the DCO divider integer and fractional parts
251*58bfdd0cSPulkoMandy uint32 dco_int = (uint32)floorf(dco / ref);
252*58bfdd0cSPulkoMandy uint32 dco_frac = (uint32)ceilf((dco / ref - dco_int) * (1 << 15));
253*58bfdd0cSPulkoMandy
254*58bfdd0cSPulkoMandy int32 dco_reg = dco_int | (dco_frac << TGL_DPLL_DCO_FRACTION_SHIFT);
255*58bfdd0cSPulkoMandy
256*58bfdd0cSPulkoMandy int32 dividers = 0;
257*58bfdd0cSPulkoMandy switch (Pdiv) {
258*58bfdd0cSPulkoMandy case 2:
259*58bfdd0cSPulkoMandy dividers |= TGL_DPLL_PDIV_2;
260*58bfdd0cSPulkoMandy break;
261*58bfdd0cSPulkoMandy case 3:
262*58bfdd0cSPulkoMandy dividers |= TGL_DPLL_PDIV_3;
263*58bfdd0cSPulkoMandy break;
264*58bfdd0cSPulkoMandy case 5:
265*58bfdd0cSPulkoMandy dividers |= TGL_DPLL_PDIV_5;
266*58bfdd0cSPulkoMandy break;
267*58bfdd0cSPulkoMandy case 7:
268*58bfdd0cSPulkoMandy dividers |= TGL_DPLL_PDIV_7;
269*58bfdd0cSPulkoMandy break;
270*58bfdd0cSPulkoMandy default:
271*58bfdd0cSPulkoMandy return B_BAD_VALUE;
272*58bfdd0cSPulkoMandy }
273*58bfdd0cSPulkoMandy switch (Kdiv) {
274*58bfdd0cSPulkoMandy case 1:
275*58bfdd0cSPulkoMandy dividers |= TGL_DPLL_KDIV_1;
276*58bfdd0cSPulkoMandy break;
277*58bfdd0cSPulkoMandy case 2:
278*58bfdd0cSPulkoMandy dividers |= TGL_DPLL_KDIV_2;
279*58bfdd0cSPulkoMandy break;
280*58bfdd0cSPulkoMandy case 3:
281*58bfdd0cSPulkoMandy dividers |= TGL_DPLL_KDIV_3;
282*58bfdd0cSPulkoMandy break;
283*58bfdd0cSPulkoMandy default:
284*58bfdd0cSPulkoMandy return B_BAD_VALUE;
285*58bfdd0cSPulkoMandy }
286*58bfdd0cSPulkoMandy if (Qdiv != 1)
287*58bfdd0cSPulkoMandy dividers |= (Qdiv << TGL_DPLL_QDIV_RATIO_SHIFT) | TGL_DPLL_QDIV_ENABLE;
288*58bfdd0cSPulkoMandy
289*58bfdd0cSPulkoMandy int32 initialState = read32(DPLL_ENABLE);
290*58bfdd0cSPulkoMandy TRACE("DPLL_ENABLE(%" B_PRIx32 ") initial value = %" B_PRIx32 "\n", DPLL_ENABLE, initialState);
291*58bfdd0cSPulkoMandy
292*58bfdd0cSPulkoMandy if (initialState & TGL_DPLL_LOCK) {
293*58bfdd0cSPulkoMandy int32 oldDCO = read32(DPLL_CFGCR0);
294*58bfdd0cSPulkoMandy int32 oldDividers = read32(DPLL_CFGCR1);
295*58bfdd0cSPulkoMandy TRACE("DPLL already locked, checking current settings: DCO %" B_PRIx32 " -> %" B_PRIx32
296*58bfdd0cSPulkoMandy ", dividers %" B_PRIx32 " -> %" B_PRIx32 "\n",
297*58bfdd0cSPulkoMandy oldDCO, dco_reg, oldDividers, dividers);
298*58bfdd0cSPulkoMandy
299*58bfdd0cSPulkoMandy if ((oldDCO == dco_reg) && (oldDividers == dividers)) {
300*58bfdd0cSPulkoMandy TRACE("DPLL already configured at the right frequency, no changes needed\n");
301*58bfdd0cSPulkoMandy return B_OK;
302*58bfdd0cSPulkoMandy }
303*58bfdd0cSPulkoMandy }
304*58bfdd0cSPulkoMandy
305*58bfdd0cSPulkoMandy // Before we start, disable the PLL
306*58bfdd0cSPulkoMandy write32(DPLL_ENABLE, read32(DPLL_ENABLE) & ~TGL_DPLL_ENABLE);
307*58bfdd0cSPulkoMandy while ((read32(DPLL_ENABLE) & TGL_DPLL_LOCK) != 0);
308*58bfdd0cSPulkoMandy TRACE("PLL is unlocked\n");
309*58bfdd0cSPulkoMandy
310*58bfdd0cSPulkoMandy // Enable PLL power
311*58bfdd0cSPulkoMandy write32(DPLL_ENABLE, read32(DPLL_ENABLE) | TGL_DPLL_POWER_ENABLE);
312*58bfdd0cSPulkoMandy
313*58bfdd0cSPulkoMandy // Wait for PLL to be powered up
314*58bfdd0cSPulkoMandy while ((read32(DPLL_ENABLE) & TGL_DPLL_POWER_STATE) == 0);
315*58bfdd0cSPulkoMandy TRACE("PLL is powered on\n");
316*58bfdd0cSPulkoMandy
317*58bfdd0cSPulkoMandy // Deactivate spread spectrum
318*58bfdd0cSPulkoMandy write32(DPLL_SPREAD_SPECTRUM, read32(DPLL_SPREAD_SPECTRUM) & ~TGL_DPLL_SSC_ENABLE);
319*58bfdd0cSPulkoMandy
320*58bfdd0cSPulkoMandy // Configure DCO
321*58bfdd0cSPulkoMandy write32(DPLL_CFGCR0, dco_reg);
322*58bfdd0cSPulkoMandy
323*58bfdd0cSPulkoMandy // Configure dividers
324*58bfdd0cSPulkoMandy write32(DPLL_CFGCR1, dividers);
325*58bfdd0cSPulkoMandy TRACE("DFGCR0(%" B_PRIx32 ") = %" B_PRIx32 ", CFGCR1(%" B_PRIx32 ") = %" B_PRIx32
326*58bfdd0cSPulkoMandy " (int = %" B_PRId32 ", frac = %" B_PRId32 ")\n", DPLL_CFGCR0, dco_reg,
327*58bfdd0cSPulkoMandy DPLL_CFGCR1, dividers, dco_int, dco_frac);
328*58bfdd0cSPulkoMandy
329*58bfdd0cSPulkoMandy // Read to make sure all writes are flushed to the hardware
330*58bfdd0cSPulkoMandy read32(DPLL_CFGCR1);
331*58bfdd0cSPulkoMandy
332*58bfdd0cSPulkoMandy // TODO Display voltage frequency switching?
333*58bfdd0cSPulkoMandy
334*58bfdd0cSPulkoMandy // Enable PLL
335*58bfdd0cSPulkoMandy write32(DPLL_ENABLE, read32(DPLL_ENABLE) | TGL_DPLL_ENABLE);
336*58bfdd0cSPulkoMandy TRACE("DPLL_ENABLE(%" B_PRIx32 ") = %" B_PRIx32 "\n", DPLL_ENABLE, read32(DPLL_ENABLE));
337*58bfdd0cSPulkoMandy
338*58bfdd0cSPulkoMandy // Wait for PLL to be enabled
339*58bfdd0cSPulkoMandy while ((read32(DPLL_ENABLE) & TGL_DPLL_LOCK) == 0);
340*58bfdd0cSPulkoMandy TRACE("PLL is locked\n");
341*58bfdd0cSPulkoMandy
342*58bfdd0cSPulkoMandy // TODO Display voltage frequency switching?
343*58bfdd0cSPulkoMandy
344*58bfdd0cSPulkoMandy return B_OK;
345*58bfdd0cSPulkoMandy }
346