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#include "pll.h"
24
25#include <subdev/bios.h>
26#include <subdev/bios/pll.h>
27
28static int
29getMNP_single(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk,
30	      int *pN, int *pM, int *pP)
31{
32	/* Find M, N and P for a single stage PLL
33	 *
34	 * Note that some bioses (NV3x) have lookup tables of precomputed MNP
35	 * values, but we're too lazy to use those atm
36	 *
37	 * "clk" parameter in kHz
38	 * returns calculated clock
39	 */
40	struct nvkm_bios *bios = subdev->device->bios;
41	int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq;
42	int minM = info->vco1.min_m, maxM = info->vco1.max_m;
43	int minN = info->vco1.min_n, maxN = info->vco1.max_n;
44	int minU = info->vco1.min_inputfreq;
45	int maxU = info->vco1.max_inputfreq;
46	int minP = info->min_p;
47	int maxP = info->max_p_usable;
48	int crystal = info->refclk;
49	int M, N, thisP, P;
50	int clkP, calcclk;
51	int delta, bestdelta = INT_MAX;
52	int bestclk = 0;
53
54	/* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
55	/* possibly correlated with introduction of 27MHz crystal */
56	if (bios->version.major < 0x60) {
57		int cv = bios->version.chip;
58		if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
59			if (clk > 250000)
60				maxM = 6;
61			if (clk > 340000)
62				maxM = 2;
63		} else if (cv < 0x40) {
64			if (clk > 150000)
65				maxM = 6;
66			if (clk > 200000)
67				maxM = 4;
68			if (clk > 340000)
69				maxM = 2;
70		}
71	}
72
73	P = 1 << maxP;
74	if ((clk * P) < minvco) {
75		minvco = clk * maxP;
76		maxvco = minvco * 2;
77	}
78
79	if (clk + clk/200 > maxvco)	/* +0.5% */
80		maxvco = clk + clk/200;
81
82	/* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
83	for (thisP = minP; thisP <= maxP; thisP++) {
84		P = 1 << thisP;
85		clkP = clk * P;
86
87		if (clkP < minvco)
88			continue;
89		if (clkP > maxvco)
90			return bestclk;
91
92		for (M = minM; M <= maxM; M++) {
93			if (crystal/M < minU)
94				return bestclk;
95			if (crystal/M > maxU)
96				continue;
97
98			/* add crystal/2 to round better */
99			N = (clkP * M + crystal/2) / crystal;
100
101			if (N < minN)
102				continue;
103			if (N > maxN)
104				break;
105
106			/* more rounding additions */
107			calcclk = ((N * crystal + P/2) / P + M/2) / M;
108			delta = abs(calcclk - clk);
109			/* we do an exhaustive search rather than terminating
110			 * on an optimality condition...
111			 */
112			if (delta < bestdelta) {
113				bestdelta = delta;
114				bestclk = calcclk;
115				*pN = N;
116				*pM = M;
117				*pP = thisP;
118				if (delta == 0)	/* except this one */
119					return bestclk;
120			}
121		}
122	}
123
124	return bestclk;
125}
126
127static int
128getMNP_double(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk,
129	      int *pN1, int *pM1, int *pN2, int *pM2, int *pP)
130{
131	/* Find M, N and P for a two stage PLL
132	 *
133	 * Note that some bioses (NV30+) have lookup tables of precomputed MNP
134	 * values, but we're too lazy to use those atm
135	 *
136	 * "clk" parameter in kHz
137	 * returns calculated clock
138	 */
139	int chip_version = subdev->device->bios->version.chip;
140	int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq;
141	int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq;
142	int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq;
143	int maxU1 = info->vco1.max_inputfreq, maxU2 = info->vco2.max_inputfreq;
144	int minM1 = info->vco1.min_m, maxM1 = info->vco1.max_m;
145	int minN1 = info->vco1.min_n, maxN1 = info->vco1.max_n;
146	int minM2 = info->vco2.min_m, maxM2 = info->vco2.max_m;
147	int minN2 = info->vco2.min_n, maxN2 = info->vco2.max_n;
148	int maxlog2P = info->max_p_usable;
149	int crystal = info->refclk;
150	bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
151	int M1, N1, M2, N2, log2P;
152	int clkP, calcclk1, calcclk2, calcclkout;
153	int delta, bestdelta = INT_MAX;
154	int bestclk = 0;
155
156	int vco2 = (maxvco2 - maxvco2/200) / 2;
157	for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
158		;
159	clkP = clk << log2P;
160
161	if (maxvco2 < clk + clk/200)	/* +0.5% */
162		maxvco2 = clk + clk/200;
163
164	for (M1 = minM1; M1 <= maxM1; M1++) {
165		if (crystal/M1 < minU1)
166			return bestclk;
167		if (crystal/M1 > maxU1)
168			continue;
169
170		for (N1 = minN1; N1 <= maxN1; N1++) {
171			calcclk1 = crystal * N1 / M1;
172			if (calcclk1 < minvco1)
173				continue;
174			if (calcclk1 > maxvco1)
175				break;
176
177			for (M2 = minM2; M2 <= maxM2; M2++) {
178				if (calcclk1/M2 < minU2)
179					break;
180				if (calcclk1/M2 > maxU2)
181					continue;
182
183				/* add calcclk1/2 to round better */
184				N2 = (clkP * M2 + calcclk1/2) / calcclk1;
185				if (N2 < minN2)
186					continue;
187				if (N2 > maxN2)
188					break;
189
190				if (!fixedgain2) {
191					if (chip_version < 0x60)
192						if (N2/M2 < 4 || N2/M2 > 10)
193							continue;
194
195					calcclk2 = calcclk1 * N2 / M2;
196					if (calcclk2 < minvco2)
197						break;
198					if (calcclk2 > maxvco2)
199						continue;
200				} else
201					calcclk2 = calcclk1;
202
203				calcclkout = calcclk2 >> log2P;
204				delta = abs(calcclkout - clk);
205				/* we do an exhaustive search rather than terminating
206				 * on an optimality condition...
207				 */
208				if (delta < bestdelta) {
209					bestdelta = delta;
210					bestclk = calcclkout;
211					*pN1 = N1;
212					*pM1 = M1;
213					*pN2 = N2;
214					*pM2 = M2;
215					*pP = log2P;
216					if (delta == 0)	/* except this one */
217						return bestclk;
218				}
219			}
220		}
221	}
222
223	return bestclk;
224}
225
226int
227nv04_pll_calc(struct nvkm_subdev *subdev, struct nvbios_pll *info, u32 freq,
228	      int *N1, int *M1, int *N2, int *M2, int *P)
229{
230	int ret;
231
232	if (!info->vco2.max_freq || !N2) {
233		ret = getMNP_single(subdev, info, freq, N1, M1, P);
234		if (N2) {
235			*N2 = 1;
236			*M2 = 1;
237		}
238	} else {
239		ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P);
240	}
241
242	if (!ret)
243		nvkm_error(subdev, "unable to compute acceptable pll values\n");
244	return ret;
245}
246