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