1/*
2 * Copyright 1993-2003 NVIDIA, Corporation
3 * Copyright 2007-2009 Stuart Bennett
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
20 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include <subdev/bios.h>
25#include <subdev/bios/pll.h>
26
27#include "pll.h"
28
29static int
30getMNP_single(struct nouveau_subdev *subdev, struct nvbios_pll *info, int clk,
31	      int *pN, int *pM, int *pP)
32{
33	/* Find M, N and P for a single stage PLL
34	 *
35	 * Note that some bioses (NV3x) have lookup tables of precomputed MNP
36	 * values, but we're too lazy to use those atm
37	 *
38	 * "clk" parameter in kHz
39	 * returns calculated clock
40	 */
41	struct nouveau_bios *bios = nouveau_bios(subdev);
42	int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq;
43	int minM = info->vco1.min_m, maxM = info->vco1.max_m;
44	int minN = info->vco1.min_n, maxN = info->vco1.max_n;
45	int minU = info->vco1.min_inputfreq;
46	int maxU = info->vco1.max_inputfreq;
47	int minP = info->min_p;
48	int maxP = info->max_p_usable;
49	int crystal = info->refclk;
50	int M, N, thisP, P;
51	int clkP, calcclk;
52	int delta, bestdelta = INT_MAX;
53	int bestclk = 0;
54
55	/* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
56	/* possibly correlated with introduction of 27MHz crystal */
57	if (bios->version.major < 0x60) {
58		int cv = bios->version.chip;
59		if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
60			if (clk > 250000)
61				maxM = 6;
62			if (clk > 340000)
63				maxM = 2;
64		} else if (cv < 0x40) {
65			if (clk > 150000)
66				maxM = 6;
67			if (clk > 200000)
68				maxM = 4;
69			if (clk > 340000)
70				maxM = 2;
71		}
72	}
73
74	P = 1 << maxP;
75	if ((clk * P) < minvco) {
76		minvco = clk * maxP;
77		maxvco = minvco * 2;
78	}
79
80	if (clk + clk/200 > maxvco)	/* +0.5% */
81		maxvco = clk + clk/200;
82
83	/* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
84	for (thisP = minP; thisP <= maxP; thisP++) {
85		P = 1 << thisP;
86		clkP = clk * P;
87
88		if (clkP < minvco)
89			continue;
90		if (clkP > maxvco)
91			return bestclk;
92
93		for (M = minM; M <= maxM; M++) {
94			if (crystal/M < minU)
95				return bestclk;
96			if (crystal/M > maxU)
97				continue;
98
99			/* add crystal/2 to round better */
100			N = (clkP * M + crystal/2) / crystal;
101
102			if (N < minN)
103				continue;
104			if (N > maxN)
105				break;
106
107			/* more rounding additions */
108			calcclk = ((N * crystal + P/2) / P + M/2) / M;
109			delta = abs(calcclk - clk);
110			/* we do an exhaustive search rather than terminating
111			 * on an optimality condition...
112			 */
113			if (delta < bestdelta) {
114				bestdelta = delta;
115				bestclk = calcclk;
116				*pN = N;
117				*pM = M;
118				*pP = thisP;
119				if (delta == 0)	/* except this one */
120					return bestclk;
121			}
122		}
123	}
124
125	return bestclk;
126}
127
128static int
129getMNP_double(struct nouveau_subdev *subdev, struct nvbios_pll *info, int clk,
130	      int *pN1, int *pM1, int *pN2, int *pM2, int *pP)
131{
132	/* Find M, N and P for a two stage PLL
133	 *
134	 * Note that some bioses (NV30+) have lookup tables of precomputed MNP
135	 * values, but we're too lazy to use those atm
136	 *
137	 * "clk" parameter in kHz
138	 * returns calculated clock
139	 */
140	int chip_version = nouveau_bios(subdev)->version.chip;
141	int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq;
142	int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq;
143	int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq;
144	int maxU1 = info->vco1.max_inputfreq, maxU2 = info->vco2.max_inputfreq;
145	int minM1 = info->vco1.min_m, maxM1 = info->vco1.max_m;
146	int minN1 = info->vco1.min_n, maxN1 = info->vco1.max_n;
147	int minM2 = info->vco2.min_m, maxM2 = info->vco2.max_m;
148	int minN2 = info->vco2.min_n, maxN2 = info->vco2.max_n;
149	int maxlog2P = info->max_p_usable;
150	int crystal = info->refclk;
151	bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
152	int M1, N1, M2, N2, log2P;
153	int clkP, calcclk1, calcclk2, calcclkout;
154	int delta, bestdelta = INT_MAX;
155	int bestclk = 0;
156
157	int vco2 = (maxvco2 - maxvco2/200) / 2;
158	for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
159		;
160	clkP = clk << log2P;
161
162	if (maxvco2 < clk + clk/200)	/* +0.5% */
163		maxvco2 = clk + clk/200;
164
165	for (M1 = minM1; M1 <= maxM1; M1++) {
166		if (crystal/M1 < minU1)
167			return bestclk;
168		if (crystal/M1 > maxU1)
169			continue;
170
171		for (N1 = minN1; N1 <= maxN1; N1++) {
172			calcclk1 = crystal * N1 / M1;
173			if (calcclk1 < minvco1)
174				continue;
175			if (calcclk1 > maxvco1)
176				break;
177
178			for (M2 = minM2; M2 <= maxM2; M2++) {
179				if (calcclk1/M2 < minU2)
180					break;
181				if (calcclk1/M2 > maxU2)
182					continue;
183
184				/* add calcclk1/2 to round better */
185				N2 = (clkP * M2 + calcclk1/2) / calcclk1;
186				if (N2 < minN2)
187					continue;
188				if (N2 > maxN2)
189					break;
190
191				if (!fixedgain2) {
192					if (chip_version < 0x60)
193						if (N2/M2 < 4 || N2/M2 > 10)
194							continue;
195
196					calcclk2 = calcclk1 * N2 / M2;
197					if (calcclk2 < minvco2)
198						break;
199					if (calcclk2 > maxvco2)
200						continue;
201				} else
202					calcclk2 = calcclk1;
203
204				calcclkout = calcclk2 >> log2P;
205				delta = abs(calcclkout - clk);
206				/* we do an exhaustive search rather than terminating
207				 * on an optimality condition...
208				 */
209				if (delta < bestdelta) {
210					bestdelta = delta;
211					bestclk = calcclkout;
212					*pN1 = N1;
213					*pM1 = M1;
214					*pN2 = N2;
215					*pM2 = M2;
216					*pP = log2P;
217					if (delta == 0)	/* except this one */
218						return bestclk;
219				}
220			}
221		}
222	}
223
224	return bestclk;
225}
226
227int
228nv04_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, u32 freq,
229	      int *N1, int *M1, int *N2, int *M2, int *P)
230{
231	int ret;
232
233	if (!info->vco2.max_freq || !N2) {
234		ret = getMNP_single(subdev, info, freq, N1, M1, P);
235		if (N2) {
236			*N2 = 1;
237			*M2 = 1;
238		}
239	} else {
240		ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P);
241	}
242
243	if (!ret)
244		nv_error(subdev, "unable to compute acceptable pll values\n");
245	return ret;
246}
247