mirror of
https://github.com/gryf/wmtemp.git
synced 2025-12-18 12:00:26 +01:00
Attempt to use nvclock code for reading GPU temperature, since running
nvidia-settings couple of times for a second is highly inefficient.
This commit is contained in:
426
nvclock/nv30.c
Normal file
426
nvclock/nv30.c
Normal file
@@ -0,0 +1,426 @@
|
||||
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
|
||||
*
|
||||
* site: http://nvclock.sourceforge.net
|
||||
*
|
||||
* Copyright(C) 2001-2007 Roderick Colenbrander
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "backend.h"
|
||||
|
||||
static float nv30_get_fanspeed()
|
||||
{
|
||||
/* Bit 30-16 of register 0x10f0 are used to store the fanspeed and bit 14-0 contain the pwm divider */
|
||||
int pwm_divider = nv_card->PMC[0x10f0/4] & 0x7fff;
|
||||
return ((nv_card->PMC[0x10f0/4] >> 16) & 0x7fff) * 100.0/pwm_divider;
|
||||
}
|
||||
|
||||
static void nv30_set_fanspeed(float speed)
|
||||
{
|
||||
int value;
|
||||
int pwm_divider = nv_card->PMC[0x10f0/4] & 0x7fff;
|
||||
|
||||
/* Don't set the fan lower than 10% for safety reasons */
|
||||
if(speed < 10 || speed > 100)
|
||||
return;
|
||||
|
||||
value = 0x80000000 + (((int)(speed * pwm_divider/100) & 0x7fff) << 16) + (nv_card->PMC[0x10f0/4] & 0xffff);
|
||||
nv_card->PMC[0x10f0/4] = value;
|
||||
}
|
||||
|
||||
static int CalcSpeed(int base_freq, int m1, int m2, int n1, int n2, int p)
|
||||
{
|
||||
return (int)((float)(n1*n2)/(m1*m2) * base_freq) >> p;
|
||||
}
|
||||
|
||||
float GetClock_nv30(int base_freq, unsigned int pll, unsigned int pll2)
|
||||
{
|
||||
int m1, m2, n1, n2, p;
|
||||
|
||||
/* Geforce FX5600 cards and the FX5700?? (0x340) use an additional PLL register */
|
||||
if(nv_card->arch & NV31)
|
||||
{
|
||||
m1 = pll & 0xff;
|
||||
n1 = (pll >> 8) & 0xff;
|
||||
p = (pll >> 16) & 0x0f;
|
||||
|
||||
/* For some reasons a speed consists of two plls */
|
||||
if(pll2 & 0x80000000)
|
||||
{
|
||||
m2 = pll2 & 0xff;
|
||||
n2 = (pll2 >> 8) & 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
m2 = 1;
|
||||
n2 = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* All GeforceFX cards except the FX5200/FX5600/FX5700 use this algorithm */
|
||||
/* If bit 7 is set use two VCOs else use the old algorithm; do cards fall back to a single PLL? */
|
||||
if(pll & 0x80)
|
||||
{
|
||||
m1 = NV30_PLL_M1(pll);
|
||||
m2 = NV30_PLL_M2(pll);
|
||||
n1 = NV30_PLL_N1(pll);
|
||||
n2 = NV30_PLL_N2(pll); /* Perhaps the 0x1f for n2 is wrong .. ? */
|
||||
p = NV30_PLL_P(pll);
|
||||
}
|
||||
else
|
||||
{
|
||||
m1 = NV30_PLL_M1(pll);
|
||||
m2 = 1;
|
||||
n1 = NV30_PLL_N1(pll);
|
||||
n2 = 1;
|
||||
p = NV30_PLL_P(pll);
|
||||
}
|
||||
}
|
||||
|
||||
if(nv_card->debug)
|
||||
printf("m1=%d m2=%d n1=%d n2=%d p=%d\n", m1, m2, n1, n2, p);
|
||||
|
||||
return (float)CalcSpeed(base_freq, m1, m2, n1, n2, p)/1000;
|
||||
}
|
||||
|
||||
static void ClockSelect_nv30(int clockIn, int p_current, unsigned int *bestM1, unsigned int *bestM2, unsigned int *bestN1, unsigned int *bestN2, unsigned int *bestP)
|
||||
{
|
||||
unsigned diff, diffOld;
|
||||
unsigned VClk, Freq;
|
||||
unsigned m, m2, n, n2, p = 0;
|
||||
int base_freq = 27000;
|
||||
|
||||
diffOld = 0xFFFFFFFF;
|
||||
|
||||
if(clockIn < 125)
|
||||
p = 3;
|
||||
else if(clockIn < 250)
|
||||
p = 2;
|
||||
else if(clockIn < 500)
|
||||
p = 1;
|
||||
else
|
||||
p = 0;
|
||||
|
||||
VClk = (unsigned)clockIn;
|
||||
|
||||
Freq = VClk;
|
||||
if ((Freq >= 75000) && (Freq <= 1100000))
|
||||
{
|
||||
for(m = 1; m <= 4; m++)
|
||||
{
|
||||
for (m2 = 1; m2 <= 4; m2++)
|
||||
{
|
||||
for(n = 1; n <= 31; n++)
|
||||
{
|
||||
n2 = (int)((float)((VClk << p) * m * m2) / (float)(base_freq * n)+.5);
|
||||
|
||||
if((n2 < 24) && (n >= n2) && (m >= m2))
|
||||
{
|
||||
Freq = ((base_freq * n * n2) / (m * m2)) >> p;
|
||||
if (Freq > VClk)
|
||||
diff = Freq - VClk;
|
||||
else
|
||||
diff = VClk - Freq;
|
||||
|
||||
/* When the difference is 0 or less than .5% accept the speed */
|
||||
if( (float)diff/(float)clockIn <= 0.005)
|
||||
{
|
||||
*bestM1 = m;
|
||||
*bestM2 = m2;
|
||||
*bestN1 = n;
|
||||
*bestN2 = n2;
|
||||
*bestP = p;
|
||||
return;
|
||||
}
|
||||
if(diff < diffOld)
|
||||
{
|
||||
*bestM1 = m;
|
||||
*bestM2 = m2;
|
||||
*bestN1 = n;
|
||||
*bestN2 = n2;
|
||||
*bestP = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float nv30_get_gpu_speed()
|
||||
{
|
||||
int pll = nv_card->PRAMDAC[0x500/4];
|
||||
int pll2 = nv_card->PRAMDAC[0x570/4];
|
||||
if(nv_card->debug == 1)
|
||||
{
|
||||
printf("NVPLL_COEFF=%08x\n", pll);
|
||||
printf("NVPLL_COEFF2=%08x\n", pll2);
|
||||
}
|
||||
|
||||
return (float)GetClock_nv30(nv_card->base_freq, pll, pll2);
|
||||
}
|
||||
|
||||
static void nv30_set_gpu_speed(unsigned int nvclk)
|
||||
{
|
||||
unsigned int PLL=0;
|
||||
int m1, m2, n1, n2, p;
|
||||
nvclk *= 1000;
|
||||
|
||||
p = NV30_PLL_P(nv_card->PRAMDAC[0x500/4]);
|
||||
ClockSelect_nv30(nvclk, p, &m1, &m2, &n1, &n2, &p);
|
||||
PLL = m1 + (m2<<4) + (n1<<8) + ((n2 & 0x7) << 19) + ((n2 & 0x18)<<21) + (p<<16) + (1<<7);
|
||||
|
||||
if(nv_card->debug)
|
||||
{
|
||||
printf("NVPLL_COEFF: %08x\n", PLL);
|
||||
}
|
||||
|
||||
/* Unlock the programmable NVPLL/MPLL */
|
||||
nv_card->PRAMDAC[0x50c/4] |= 0x500;
|
||||
|
||||
if(PLL)
|
||||
nv_card->PRAMDAC[0x500/4] = PLL;
|
||||
}
|
||||
|
||||
static void nv31_set_gpu_speed(unsigned int nvclk)
|
||||
{
|
||||
unsigned int PLL, PLL2;
|
||||
int m1, m2, n1, n2, p;
|
||||
nvclk *= 1000;
|
||||
|
||||
p = NV30_PLL_P(nv_card->PRAMDAC[0x500/4]);
|
||||
ClockSelect_nv30(nvclk, p, &m1, &m2, &n1, &n2, &p);
|
||||
PLL = (p << 16) | (n1 << 8) | m1;
|
||||
PLL2 = (1<<31) | (n2 << 8) | m2;
|
||||
|
||||
/* Unlock the programmable NVPLL/MPLL */
|
||||
nv_card->PRAMDAC[0x50c/4] |= 0x500;
|
||||
|
||||
/* When no speed is found, don't change the PLL */
|
||||
/* The algorithm doesn't allow too low speeds */
|
||||
if(PLL)
|
||||
{
|
||||
if(nv_card->debug)
|
||||
{
|
||||
printf("NVPLL_COEFF: %08x\n", PLL);
|
||||
printf("NVPLL2_COEFF: %08x\n", PLL2);
|
||||
}
|
||||
nv_card->PRAMDAC[0x500/4] = PLL;
|
||||
nv_card->PRAMDAC[0x570/4] = PLL2;
|
||||
}
|
||||
}
|
||||
|
||||
static float nv30_get_memory_speed()
|
||||
{
|
||||
int pll = nv_card->PRAMDAC[0x504/4];
|
||||
int pll2 = nv_card->PRAMDAC[0x574/4];
|
||||
if(nv_card->debug == 1)
|
||||
{
|
||||
printf("MPLL_COEFF=%08x\n", pll);
|
||||
printf("MPLL_COEFF2=%08x\n", pll2);
|
||||
}
|
||||
|
||||
return (float)GetClock_nv30(nv_card->base_freq, pll, pll2);
|
||||
}
|
||||
|
||||
static void nv30_set_memory_speed(unsigned int memclk)
|
||||
{
|
||||
unsigned int PLL=0;
|
||||
int m1, m2, n1, n2, p;
|
||||
memclk *= 1000;
|
||||
|
||||
p = NV30_PLL_P(nv_card->PRAMDAC[0x500/4]);
|
||||
ClockSelect_nv30(memclk, p, &m1, &m2, &n1, &n2, &p);
|
||||
PLL = m1 + (m2<<4) + (n1<<8) + ((n2 & 0x7) << 19) + ((n2 & 0x18)<<21) + (p<<16) + (1<<7);
|
||||
|
||||
if(nv_card->debug)
|
||||
printf("MPLL_COEFF: %08x\n", PLL);
|
||||
|
||||
/* Unlock the programmable NVPLL/MPLL */
|
||||
nv_card->PRAMDAC[0x50c/4] |= 0x500;
|
||||
if(PLL)
|
||||
nv_card->PRAMDAC[0x504/4] = PLL;
|
||||
}
|
||||
|
||||
static void nv31_set_memory_speed(unsigned int memclk)
|
||||
{
|
||||
unsigned int PLL, PLL2;
|
||||
int m1, m2, n1, n2, p;
|
||||
memclk *= 1000;
|
||||
|
||||
p = NV30_PLL_P(nv_card->PRAMDAC[0x500/4]);
|
||||
ClockSelect_nv30(memclk, p, &m1, &m2, &n1, &n2, &p);
|
||||
PLL = (p << 16) | (n1 << 8) | m1;
|
||||
PLL2 = (1<<31) | (n2 << 8) | m2;
|
||||
|
||||
/* Unlock the programmable NVPLL/MPLL */
|
||||
nv_card->PRAMDAC[0x50c/4] |= 0x500;
|
||||
|
||||
/* When no speed is found, don't change the PLL */
|
||||
/* The algorithm doesn't allow too low speeds */
|
||||
if(PLL)
|
||||
{
|
||||
if(nv_card->debug)
|
||||
{
|
||||
printf("MPLL_COEFF: %08x\n", PLL);
|
||||
printf("MPLL2_COEFF: %08x\n", PLL2);
|
||||
}
|
||||
nv_card->PRAMDAC[0x504/4] = PLL;
|
||||
nv_card->PRAMDAC[0x574/4] = PLL2;
|
||||
}
|
||||
}
|
||||
|
||||
static void nv30_reset_gpu_speed()
|
||||
{
|
||||
/* FIXME: we need to use our bios info */
|
||||
/* Unlock the programmable NVPLL/MPLL */
|
||||
nv_card->PRAMDAC[0x50c/4] |= 0x500;
|
||||
|
||||
/* Set the gpu speed */
|
||||
nv_card->PRAMDAC[0x500/4] = nv_card->nvpll;
|
||||
}
|
||||
|
||||
static void nv31_reset_gpu_speed()
|
||||
{
|
||||
/* FIXME: we need to use our bios info */
|
||||
/* Unlock the programmable NVPLL/MPLL */
|
||||
nv_card->PRAMDAC[0x50c/4] |= 0x500;
|
||||
|
||||
/* Set the gpu speed */
|
||||
nv_card->PRAMDAC[0x500/4] = nv_card->nvpll;
|
||||
nv_card->PRAMDAC[0x570/4] = nv_card->nvpll2;
|
||||
}
|
||||
|
||||
static void nv30_reset_memory_speed()
|
||||
{
|
||||
/* FIXME: we need to use our bios info */
|
||||
/* Unlock the programmable NVPLL/MPLL */
|
||||
nv_card->PRAMDAC[0x50c/4] |= 0x500;
|
||||
|
||||
/* Set the memory speed */
|
||||
nv_card->PRAMDAC[0x504/4] = nv_card->mpll;
|
||||
}
|
||||
|
||||
static void nv31_reset_memory_speed()
|
||||
{
|
||||
/* FIXME: we need to use our bios info */
|
||||
/* Unlock the programmable NVPLL/MPLL */
|
||||
nv_card->PRAMDAC[0x50c/4] |= 0x500;
|
||||
|
||||
/* Set the memory speed */
|
||||
nv_card->PRAMDAC[0x504/4] = nv_card->mpll;
|
||||
nv_card->PRAMDAC[0x574/4] = nv_card->mpll2;
|
||||
}
|
||||
|
||||
static void nv30_set_state(int state)
|
||||
{
|
||||
#ifdef HAVE_NVCONTROL
|
||||
if(state & (STATE_2D | STATE_3D))
|
||||
{
|
||||
nv_card->state = state;
|
||||
nv_card->get_gpu_speed = nvcontrol_get_gpu_speed;
|
||||
nv_card->get_memory_speed = nvcontrol_get_memory_speed;
|
||||
nv_card->set_gpu_speed = nvcontrol_set_gpu_speed;
|
||||
nv_card->set_memory_speed = nvcontrol_set_memory_speed;
|
||||
nv_card->reset_gpu_speed = nvcontrol_reset_gpu_speed;
|
||||
nv_card->reset_memory_speed = nvcontrol_reset_memory_speed;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
nv_card->state = STATE_LOWLEVEL;
|
||||
if(nv_card->arch & NV31)
|
||||
{
|
||||
nv_card->get_gpu_speed = nv30_get_gpu_speed;
|
||||
nv_card->get_memory_speed = nv30_get_memory_speed;
|
||||
nv_card->set_memory_speed = nv31_set_memory_speed;
|
||||
nv_card->set_gpu_speed = nv31_set_gpu_speed;
|
||||
nv_card->reset_gpu_speed = nv31_reset_gpu_speed;
|
||||
nv_card->reset_memory_speed = nv31_reset_memory_speed;
|
||||
}
|
||||
else /* FX5800/5900 */
|
||||
{
|
||||
nv_card->get_gpu_speed = nv30_get_gpu_speed;
|
||||
nv_card->get_memory_speed = nv30_get_memory_speed;
|
||||
nv_card->set_memory_speed = nv30_set_memory_speed;
|
||||
nv_card->set_gpu_speed = nv30_set_gpu_speed;
|
||||
nv_card->reset_gpu_speed = nv30_reset_gpu_speed;
|
||||
nv_card->reset_memory_speed = nv30_reset_memory_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nv30_init(void)
|
||||
{
|
||||
nv_card->base_freq = 27000;
|
||||
|
||||
nv_card->set_state = nv30_set_state;
|
||||
nv_card->set_state(STATE_LOWLEVEL); /* Set the clock function pointers */
|
||||
|
||||
/* Register I2C busses for hardware monitoring purposes */
|
||||
if(nv_card->busses[0] == NULL)
|
||||
{
|
||||
nv_card->num_busses = 3;
|
||||
nv_card->busses[0] = NV_I2CCreateBusPtr("BUS0", 0x3e); /* available on riva128 and higher */
|
||||
nv_card->busses[1] = NV_I2CCreateBusPtr("BUS1", 0x36); /* available on rivatnt hardware and higher */
|
||||
nv_card->busses[2] = NV_I2CCreateBusPtr("BUS2", 0x50); /* available on geforce4mx/4ti/fx/6/7 */
|
||||
|
||||
i2c_sensor_init();
|
||||
}
|
||||
|
||||
/* HW monitoring; bit 31 is an indication if fanspeed monitoring is available
|
||||
/ Note this bit isn't very reliable as it is set on cards with advanced sensors too.
|
||||
/
|
||||
/ Only support this on NV30/NV35/NV38 hardware for now as it works differently on other NV3x boards
|
||||
*/
|
||||
if((nv_card->PMC[0x10f0/4] & 0x80000000) && (nv_card->arch & (NV30 | NV35)) && !(nv_card->caps & I2C_FANSPEED_MONITORING))
|
||||
{
|
||||
nv_card->caps |= GPU_FANSPEED_MONITORING;
|
||||
nv_card->get_fanspeed = nv30_get_fanspeed;
|
||||
nv_card->set_fanspeed = nv30_set_fanspeed;
|
||||
}
|
||||
|
||||
/* Mobile GPU check; we don't want to overclock those unless the user wants it */
|
||||
if(nv_card->gpu == MOBILE)
|
||||
{
|
||||
nv_card->caps = ~(~nv_card->caps | GPU_OVERCLOCKING | MEM_OVERCLOCKING);
|
||||
}
|
||||
else
|
||||
nv_card->caps |= (GPU_OVERCLOCKING | MEM_OVERCLOCKING);
|
||||
|
||||
/* Set the speed range */
|
||||
if(nv_card->bios)
|
||||
{
|
||||
/* GeforceFX models have different clocks in 2d and 3d; above hack doesn't work for those */
|
||||
nv_card->memclk_min = (short)(nv_card->bios->perf_lst[0].memclk * .75);
|
||||
nv_card->memclk_max = (short)(nv_card->bios->perf_lst[2].memclk * 1.25);
|
||||
nv_card->nvclk_min = (short)(nv_card->bios->perf_lst[0].nvclk * .75);
|
||||
nv_card->nvclk_max = (short)(nv_card->bios->perf_lst[2].nvclk * 1.25);
|
||||
}
|
||||
else
|
||||
{
|
||||
float memclk = GetClock_nv30(nv_card->base_freq, nv_card->mpll, nv_card->mpll2);
|
||||
float nvclk = GetClock_nv30(nv_card->base_freq, nv_card->nvpll, nv_card->nvpll2);
|
||||
|
||||
/* Not great but better than nothing .. */
|
||||
nv_card->memclk_min = (short)(memclk * .75);
|
||||
nv_card->memclk_max = (short)(memclk * 1.5);
|
||||
nv_card->nvclk_min = (short)(nvclk * .75);
|
||||
nv_card->nvclk_max = (short)(nvclk * 1.5);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user