Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 1993-2003 NVIDIA, Corporation
0003  * Copyright 2007-2009 Stuart Bennett
0004  *
0005  * Permission is hereby granted, free of charge, to any person obtaining a
0006  * copy of this software and associated documentation files (the "Software"),
0007  * to deal in the Software without restriction, including without limitation
0008  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0009  * and/or sell copies of the Software, and to permit persons to whom the
0010  * Software is furnished to do so, subject to the following conditions:
0011  *
0012  * The above copyright notice and this permission notice shall be included in
0013  * all copies or substantial portions of the Software.
0014  *
0015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0018  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
0019  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
0020  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
0021  * SOFTWARE.
0022  */
0023 #include "pll.h"
0024 
0025 #include <subdev/bios.h>
0026 #include <subdev/bios/pll.h>
0027 
0028 static int
0029 getMNP_single(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk,
0030           int *pN, int *pM, int *pP)
0031 {
0032     /* Find M, N and P for a single stage PLL
0033      *
0034      * Note that some bioses (NV3x) have lookup tables of precomputed MNP
0035      * values, but we're too lazy to use those atm
0036      *
0037      * "clk" parameter in kHz
0038      * returns calculated clock
0039      */
0040     struct nvkm_bios *bios = subdev->device->bios;
0041     int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq;
0042     int minM = info->vco1.min_m, maxM = info->vco1.max_m;
0043     int minN = info->vco1.min_n, maxN = info->vco1.max_n;
0044     int minU = info->vco1.min_inputfreq;
0045     int maxU = info->vco1.max_inputfreq;
0046     int minP = info->min_p;
0047     int maxP = info->max_p_usable;
0048     int crystal = info->refclk;
0049     int M, N, thisP, P;
0050     int clkP, calcclk;
0051     int delta, bestdelta = INT_MAX;
0052     int bestclk = 0;
0053 
0054     /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
0055     /* possibly correlated with introduction of 27MHz crystal */
0056     if (bios->version.major < 0x60) {
0057         int cv = bios->version.chip;
0058         if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
0059             if (clk > 250000)
0060                 maxM = 6;
0061             if (clk > 340000)
0062                 maxM = 2;
0063         } else if (cv < 0x40) {
0064             if (clk > 150000)
0065                 maxM = 6;
0066             if (clk > 200000)
0067                 maxM = 4;
0068             if (clk > 340000)
0069                 maxM = 2;
0070         }
0071     }
0072 
0073     P = 1 << maxP;
0074     if ((clk * P) < minvco) {
0075         minvco = clk * maxP;
0076         maxvco = minvco * 2;
0077     }
0078 
0079     if (clk + clk/200 > maxvco) /* +0.5% */
0080         maxvco = clk + clk/200;
0081 
0082     /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
0083     for (thisP = minP; thisP <= maxP; thisP++) {
0084         P = 1 << thisP;
0085         clkP = clk * P;
0086 
0087         if (clkP < minvco)
0088             continue;
0089         if (clkP > maxvco)
0090             return bestclk;
0091 
0092         for (M = minM; M <= maxM; M++) {
0093             if (crystal/M < minU)
0094                 return bestclk;
0095             if (crystal/M > maxU)
0096                 continue;
0097 
0098             /* add crystal/2 to round better */
0099             N = (clkP * M + crystal/2) / crystal;
0100 
0101             if (N < minN)
0102                 continue;
0103             if (N > maxN)
0104                 break;
0105 
0106             /* more rounding additions */
0107             calcclk = ((N * crystal + P/2) / P + M/2) / M;
0108             delta = abs(calcclk - clk);
0109             /* we do an exhaustive search rather than terminating
0110              * on an optimality condition...
0111              */
0112             if (delta < bestdelta) {
0113                 bestdelta = delta;
0114                 bestclk = calcclk;
0115                 *pN = N;
0116                 *pM = M;
0117                 *pP = thisP;
0118                 if (delta == 0) /* except this one */
0119                     return bestclk;
0120             }
0121         }
0122     }
0123 
0124     return bestclk;
0125 }
0126 
0127 static int
0128 getMNP_double(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk,
0129           int *pN1, int *pM1, int *pN2, int *pM2, int *pP)
0130 {
0131     /* Find M, N and P for a two stage PLL
0132      *
0133      * Note that some bioses (NV30+) have lookup tables of precomputed MNP
0134      * values, but we're too lazy to use those atm
0135      *
0136      * "clk" parameter in kHz
0137      * returns calculated clock
0138      */
0139     int chip_version = subdev->device->bios->version.chip;
0140     int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq;
0141     int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq;
0142     int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq;
0143     int maxU1 = info->vco1.max_inputfreq, maxU2 = info->vco2.max_inputfreq;
0144     int minM1 = info->vco1.min_m, maxM1 = info->vco1.max_m;
0145     int minN1 = info->vco1.min_n, maxN1 = info->vco1.max_n;
0146     int minM2 = info->vco2.min_m, maxM2 = info->vco2.max_m;
0147     int minN2 = info->vco2.min_n, maxN2 = info->vco2.max_n;
0148     int maxlog2P = info->max_p_usable;
0149     int crystal = info->refclk;
0150     bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
0151     int M1, N1, M2, N2, log2P;
0152     int clkP, calcclk1, calcclk2, calcclkout;
0153     int delta, bestdelta = INT_MAX;
0154     int bestclk = 0;
0155 
0156     int vco2 = (maxvco2 - maxvco2/200) / 2;
0157     for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
0158         ;
0159     clkP = clk << log2P;
0160 
0161     if (maxvco2 < clk + clk/200)    /* +0.5% */
0162         maxvco2 = clk + clk/200;
0163 
0164     for (M1 = minM1; M1 <= maxM1; M1++) {
0165         if (crystal/M1 < minU1)
0166             return bestclk;
0167         if (crystal/M1 > maxU1)
0168             continue;
0169 
0170         for (N1 = minN1; N1 <= maxN1; N1++) {
0171             calcclk1 = crystal * N1 / M1;
0172             if (calcclk1 < minvco1)
0173                 continue;
0174             if (calcclk1 > maxvco1)
0175                 break;
0176 
0177             for (M2 = minM2; M2 <= maxM2; M2++) {
0178                 if (calcclk1/M2 < minU2)
0179                     break;
0180                 if (calcclk1/M2 > maxU2)
0181                     continue;
0182 
0183                 /* add calcclk1/2 to round better */
0184                 N2 = (clkP * M2 + calcclk1/2) / calcclk1;
0185                 if (N2 < minN2)
0186                     continue;
0187                 if (N2 > maxN2)
0188                     break;
0189 
0190                 if (!fixedgain2) {
0191                     if (chip_version < 0x60)
0192                         if (N2/M2 < 4 || N2/M2 > 10)
0193                             continue;
0194 
0195                     calcclk2 = calcclk1 * N2 / M2;
0196                     if (calcclk2 < minvco2)
0197                         break;
0198                     if (calcclk2 > maxvco2)
0199                         continue;
0200                 } else
0201                     calcclk2 = calcclk1;
0202 
0203                 calcclkout = calcclk2 >> log2P;
0204                 delta = abs(calcclkout - clk);
0205                 /* we do an exhaustive search rather than terminating
0206                  * on an optimality condition...
0207                  */
0208                 if (delta < bestdelta) {
0209                     bestdelta = delta;
0210                     bestclk = calcclkout;
0211                     *pN1 = N1;
0212                     *pM1 = M1;
0213                     *pN2 = N2;
0214                     *pM2 = M2;
0215                     *pP = log2P;
0216                     if (delta == 0) /* except this one */
0217                         return bestclk;
0218                 }
0219             }
0220         }
0221     }
0222 
0223     return bestclk;
0224 }
0225 
0226 int
0227 nv04_pll_calc(struct nvkm_subdev *subdev, struct nvbios_pll *info, u32 freq,
0228           int *N1, int *M1, int *N2, int *M2, int *P)
0229 {
0230     int ret;
0231 
0232     if (!info->vco2.max_freq || !N2) {
0233         ret = getMNP_single(subdev, info, freq, N1, M1, P);
0234         if (N2) {
0235             *N2 = 1;
0236             *M2 = 1;
0237         }
0238     } else {
0239         ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P);
0240     }
0241 
0242     if (!ret)
0243         nvkm_error(subdev, "unable to compute acceptable pll values\n");
0244     return ret;
0245 }