1
0
mirror of https://github.com/gryf/wmtemp.git synced 2025-12-17 19:40:27 +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:
2012-05-30 11:01:21 +02:00
parent d256a01f46
commit 28d2a7fd2e
54 changed files with 9003 additions and 5 deletions

39
nvclock/Makefile Normal file
View File

@@ -0,0 +1,39 @@
CC=gcc -O0
AR=ar
RANLIB=ranlib
system=Linux
HAVE_NVCONTROL=no
OBJECTS=backend.o bios.o config.o error.o nv30.o nv40.o nv50.o info.o overclock.o utils.o i2c.o xf86i2c.o adt7473.o f75375.o lm99.o w83781d.o w83l785r.o libc_wrapper.o
CFLAGS= -I../.. -I../nvcontrol
ifeq ($(system), FreeBSD)
OBJECTS+=back_bsd.o
else
ifeq ($(system), Win32)
OBJECTS+=back_win32.o
else
OBJECTS+=back_linux.o
endif
endif
ifeq ($(HAVE_NVCONTROL), yes)
OBJECTS+=nvcontrol.o
endif
.PHONY: clean distclean
all: libbackend.a
libbackend.a: $(OBJECTS)
$(AR) cru libbackend.a $(OBJECTS)
$(RANLIB) libbackend.a
clean:
rm -f *.o *.a
distclean: clean
rm -f Makefile
install:
uninstall:

173
nvclock/adt7473.c Normal file
View File

@@ -0,0 +1,173 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2005 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
*
* ADT7473 hardware monitoring
*/
#include <string.h>
#include "i2c.h"
#include "nvclock.h"
/* various defines for register offsets and such are needed */
#define ADT7473_REG_LOCAL_TEMP 0x26
#define ADT7473_REG_LOCAL_TEMP_OFFSET 0x70
#define ADT7473_REG_REMOTE_TEMP 0x25
#define ADT7473_REG_REMOTE_TEMP_OFFSET 0x71
#define ADT7473_REG_PWM1_CFG 0x5c
#define ADT7473_REG_PWM1_DUTYCYCLE 0x30
#define ADT7473_REG_PWM1_MAX_DUTYCYCLE 0x38
#define ADT7473_REG_PWM1_MIN_DUTYCYCLE 0x64
#define ADT7473_REG_TACH1_LB 0x28
#define ADT7473_REG_TACH1_HB 0x29
#define ADT7473_REG_CFG5 0x7c
#define ADT7473_REG_MAN_ID 0x3e
#define AD_MAN_ID 0x41
#define ADT7473_REG_CHIP_ID 0x3d
#define ADT7473_CHIP_ID 0x73
/* This function should return the chip type .. */
int adt7473_detect(I2CDevPtr dev)
{
I2CByte man_id, chip_id;
xf86I2CReadByte(dev, ADT7473_REG_MAN_ID, &man_id);
xf86I2CReadByte(dev, ADT7473_REG_CHIP_ID, &chip_id);
if((man_id == AD_MAN_ID) && (chip_id == ADT7473_CHIP_ID))
{
dev->chip_id = ADT7473;
dev->chip_name = (char*)strdup("Analog Devices ADT7473");
return 1;
}
return 0;
}
int adt7473_get_board_temp(I2CDevPtr dev)
{
I2CByte temp;
I2CByte cfg;
xf86I2CReadByte(dev, ADT7473_REG_LOCAL_TEMP, &temp);
/* Check if the sensor uses 2-complement or offset-64 mode */
xf86I2CReadByte(dev, ADT7473_REG_CFG5, &cfg);
if(cfg & 0x1)
return (int)((char)temp);
else
return temp - 64;
}
int adt7473_get_gpu_temp(I2CDevPtr dev)
{
I2CByte temp;
I2CByte cfg;
int offset = 0;
/* We add a 10C offset to the temperature though this isn't conform
/ the ADT7473 datasheet. The reason we add this is to show a temperature
/ similar to the internal gpu sensor. Right now the board and gpu
/ temperature as reported by the sensor are about the same (there's
/ a difference between the two or 3-4C). Most likely the internal gpu
/ temperature is a bit higher and assuming the temperature as reported
/ by the internal sensor is correct adding a 10C offset is a good solution.
/ (The offset seems to be the same independant of the temperature which)
/ Don't do it on NV50, it doesn't have an internal sensor?
*/
if(dev->arch & NV4X)
offset = 10;
xf86I2CReadByte(dev, ADT7473_REG_REMOTE_TEMP, &temp);
/* Check if the sensor uses 2-complement or offset-64 mode */
xf86I2CReadByte(dev, ADT7473_REG_CFG5, &cfg);
if(cfg & 0x1)
return (int)((char)temp + offset);
else
return temp - 64 + offset;
}
int adt7473_get_fanspeed_rpm(I2CDevPtr dev)
{
I2CByte count_lb, count_hb;
int count;
xf86I2CReadByte(dev, ADT7473_REG_TACH1_LB, &count_lb);
xf86I2CReadByte(dev, ADT7473_REG_TACH1_HB, &count_hb);
count = (count_hb << 8) | count_lb;
/* RPM = 60*90k pulses / (number of counts that fit in a pulse) */
return 90000*60/count;
}
float adt7473_get_fanspeed_pwm(I2CDevPtr dev)
{
I2CByte value;
xf86I2CReadByte(dev, ADT7473_REG_PWM1_DUTYCYCLE, &value);
return (float)value*100/255;
}
int adt7473_set_fanspeed_pwm(I2CDevPtr dev, float speed)
{
I2CByte value = (int)speed * 255/100;
I2CByte cfg, max_dutycycle;
xf86I2CReadByte(dev, ADT7473_REG_PWM1_CFG, &cfg);
cfg |= 0xe0; /* Put PWM1 in manual mode; this disables automatic control */
xf86I2CWriteByte(dev, ADT7473_REG_PWM1_CFG, cfg);
/* If the MAX dutycycle is lower than 0xff (100%), set it to 0xff */
xf86I2CReadByte(dev, ADT7473_REG_PWM1_MAX_DUTYCYCLE, &max_dutycycle);
if(max_dutycycle < 0xff)
xf86I2CWriteByte(dev, ADT7473_REG_PWM1_MAX_DUTYCYCLE, 0xff);
xf86I2CWriteByte(dev, ADT7473_REG_PWM1_DUTYCYCLE, value);
return 1;
}
int adt7473_get_fanspeed_mode(I2CDevPtr dev) {
I2CByte cfg;
xf86I2CReadByte(dev, ADT7473_REG_PWM1_CFG, &cfg);
if(cfg & (0x6 << 5)) return 0; /* auto */
if(cfg & (0x7 << 5)) return 1; /* manual */
return -1; /* something went wrong */
}
void adt7473_set_fanspeed_mode(I2CDevPtr dev, int mode) {
I2CByte cfg;
xf86I2CReadByte(dev, ADT7473_REG_PWM1_CFG, &cfg);
/* Clear the pwm1 config bits */
cfg&=~(0xF << 5);
if(mode==1)
cfg|=0x7 << 5; /* manual */
else
cfg|=0x6 << 5; /* auto */
xf86I2CWriteByte(dev, ADT7473_REG_PWM1_CFG, cfg);
}

BIN
nvclock/adt7473.o Normal file

Binary file not shown.

250
nvclock/back_bsd.c Normal file
View File

@@ -0,0 +1,250 @@
/* NVClock 0.8 - FreeBSD overclocker for NVIDIA cards
*
* Site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2005 Roderick Colenbrander
* Portions Copyright(C) 2003 Samy Al Bahra <samy@kerneled.com>
*
* 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 <errno.h>
#include <fcntl.h>
#include <osreldate.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/mman.h>
#include "backend.h"
#if __FreeBSD_version < 430000
# include <pci/pci_ioctl.h>
#else
# include <sys/pciio.h>
#endif
#define NV_VENDOR 0x10de
#define VGA 0x03
#define SIZE 255
NVClock nvclock;
NVCard *nv_card = NULL;
static void *map_dev_mem (int fd, unsigned long Base, unsigned long Size);
static void unmap_dev_mem (unsigned long Base, unsigned long Size);
int init_nvclock()
{
/* Check if the user is root */
if(getuid() != 0 && geteuid() != 0)
{
set_error(NV_ERR_NOT_ENOUGH_PERMISSIONS);
return 0;
}
/* Detect all nvidia cards; this needs to be done before creating directory and config file as that code needs card access */
if(!probe_devices())
{
/* probe_devices takes care of the error as it isn't certain it failed because of there are no nvidia cards */
return 0;
}
if(!open_config())
return 0;
return 1;
}
static int probe_devices()
{
struct pci_conf_io pcidev;
struct pci_match_conf patterns;
struct pci_conf matches[SIZE];
struct pci_io pi;
int pcid, counter;
if((pcid=open("/dev/pci", O_RDWR))==-1)
{
set_error_str("Could not open /dev/pci");
return 0;
}
memset(&pcidev,0,sizeof(pcidev));
pcidev.pat_buf_len=sizeof(patterns);
patterns.pc_vendor=NV_VENDOR;
patterns.pc_class=VGA;
patterns.flags=PCI_GETCONF_MATCH_VENDOR|PCI_GETCONF_MATCH_CLASS;
pcidev.patterns=&patterns;
pcidev.num_patterns=1;
pcidev.pat_buf_len=sizeof(patterns);
pcidev.match_buf_len=sizeof(matches);
pcidev.matches=matches;
if(ioctl(pcid, PCIOCGETCONF, &pcidev)==-1)
{
set_error_str("Could not get configuration of /dev/pci");
close(pcid);
return 0;
}
if(pcidev.status==PCI_GETCONF_LIST_CHANGED)
{
set_error_str("PCI device list has changed\n");
close(pcid);
return 0;
}
else if(pcidev.status==PCI_GETCONF_ERROR)
{
set_error_str("General error encountered while trying to get PCI information\n");
close(pcid);
return 0;
}
if(!pcidev.num_matches)
{
set_error(NV_ERR_NO_DEVICES_FOUND);
close(pcid);
return 0;
}
for(counter=0;counter<MAX_CARDS && counter < pcidev.num_matches;counter++)
{
nvclock.card[counter].device_id=(matches+counter)->pc_device;
pi.pi_sel.pc_bus=(matches+counter)->pc_sel.pc_bus;
pi.pi_sel.pc_dev=(matches+counter)->pc_sel.pc_dev;
pi.pi_sel.pc_func=(matches+counter)->pc_sel.pc_func;
pi.pi_reg=0x10;
pi.pi_width=sizeof(unsigned int);
if(ioctl(pcid, PCIOCREAD, &pi)==-1)
{
set_error_str("Could not read data from /dev/pci");
close(pcid);
return 0;
}
nvclock.card[counter].reg_address=pi.pi_data;
nvclock.card[counter].dev_name= "/dev/mem";
nvclock.card[counter].card_name = (char*)get_card_name(nvclock.card[counter].device_id, &nvclock.card[counter].gpu);
nvclock.card[counter].arch = get_gpu_arch(nvclock.card[counter].device_id);
nvclock.card[counter].number = counter;
nvclock.card[counter].devbusfn = PCI_GET_DEVBUSFN(pi.pi_sel.pc_dev, pi.pi_sel.pc_bus, pi.pi_sel.pc_func);
nvclock.card[counter].state = 0;
}
close(pcid);
nvclock.num_cards = counter;
return 1;
}
int32_t pciReadLong(unsigned short devbusfn, long offset)
{
int fd;
struct pci_io pi;
pi.pi_sel.pc_bus = PCI_GET_BUS(devbusfn);
pi.pi_sel.pc_dev = PCI_GET_DEVICE(devbusfn);
pi.pi_sel.pc_func = PCI_GET_FUNCTION(devbusfn);
pi.pi_reg = offset;
pi.pi_width = 4;
if((fd=open("/dev/pci", O_RDWR)) == -1)
{
set_error_str("Could not open /dev/pci");
return -1;
}
if(ioctl(fd, PCIOCREAD, &pi) == -1)
{
set_error_str("Could not read data from /dev/pci");
close(fd);
return -1;
}
close(fd);
return pi.pi_data;
}
int map_mem(const char *dev_name)
{
int fd;
if( (fd = open(dev_name, O_RDWR)) == -1 )
{
char err[80];
sprintf(err, "Can't open %s", dev_name);
set_error_str(err);
return 0;
}
/* Map the registers of the nVidia chip */
nv_card->PEXTDEV = map_dev_mem(fd, nv_card->reg_address + 0x101000, 0x1000);
nv_card->PFB = map_dev_mem(fd, nv_card->reg_address + 0x100000, 0x1000);
/* normally pmc is till 0x2000 but extended it for nv40 */
nv_card->PMC = map_dev_mem(fd, nv_card->reg_address + 0x000000, 0xffff);
nv_card->PCIO = map_dev_mem(fd, nv_card->reg_address + 0x601000, 0x2000);
nv_card->PRAMDAC = map_dev_mem(fd, nv_card->reg_address + 0x680000, 0x2000);
nv_card->PROM = map_dev_mem(fd, nv_card->reg_address + 0x300000, 0xffff);
/* On Geforce 8xxx cards it appears that the pci config header has been moved */
if(nv_card->arch & NV5X)
nv_card->PBUS = map_dev_mem(fd, nv_card->reg_address + 0x88000, 0x100);
else
nv_card->PBUS = nv_card->PMC + 0x1800/4;
nv_card->mem_mapped = 1;
close(fd);
return 1;
}
void unmap_mem()
{
unmap_dev_mem((unsigned long)nv_card->PEXTDEV, 0x1000);
unmap_dev_mem((unsigned long)nv_card->PFB, 0x1000);
unmap_dev_mem((unsigned long)nv_card->PMC, 0x2ffff);
unmap_dev_mem((unsigned long)nv_card->PCIO, 0x2000);
unmap_dev_mem((unsigned long)nv_card->PRAMDAC, 0x2000);
unmap_dev_mem((unsigned long)nv_card->PROM, 0xffff);
}
/* -------- mmap on devices -------- */
/* This piece of code is from nvtv a linux program for tvout */
/* The author of nvtv got this from xfree86's os-support/linux/lnx_video.c */
/* and he modified it a little */
static void *map_dev_mem (int fd, unsigned long Base, unsigned long Size)
{
void *base;
int mapflags = MAP_SHARED;
unsigned long realBase, alignOff;
realBase = Base & ~(getpagesize() - 1);
alignOff = Base - realBase;
base = mmap((caddr_t)0, Size + alignOff, PROT_READ|PROT_WRITE,
mapflags, fd, (off_t)realBase);
return (void *) ((char *)base + alignOff);
}
static void unmap_dev_mem (unsigned long Base, unsigned long Size)
{
unsigned long alignOff = Base - (Base & ~(getpagesize() - 1));
munmap((caddr_t)(Base - alignOff), (Size + alignOff));
nv_card->mem_mapped = 0;
}

296
nvclock/back_linux.c Normal file
View File

@@ -0,0 +1,296 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* Copyright(C) 2001-2007 Roderick Colenbrander
*
* site: http://nvclock.sourceforge.net
*
* 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 <netinet/in.h> /* needed for htonl */
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "backend.h"
NVClock nvclock;
NVCard *nv_card = NULL;
static int IsVideoCard(unsigned short devbusfn);
static int probe_devices();
static void *map_dev_mem (int fd, unsigned long Base, unsigned long Size);
static void unmap_dev_mem (unsigned long Base, unsigned long Size);
/* Check if we are using the closed source Nvidia drivers */
static int check_driver()
{
FILE *proc;
char buffer[80];
proc = fopen("/proc/modules", "r");
/* Don't crash when there's no /proc/modules */
if(proc == NULL)
return 0;
while(fgets(buffer, 80, proc) != NULL)
{
char name[80];
int size;
int used;
/* Check to see if NVdriver/nvidia is loaded and if it is used.
/ For various versions the driver isn't initialized when X hasn't
/ been started and it can crash then.
*/
if(sscanf(buffer,"%s %d %d",&name, &size, &used) != 3) continue;
{
if(strcmp(name, "NVdriver") == 0)
{
fclose(proc);
if(used)
return 1;
return 0;
}
if(strcmp(name, "nvidia") == 0)
{
fclose(proc);
if(used)
return 2;
return 0;
}
}
}
fclose(proc);
return 0;
}
int init_nvclock()
{
int nv_driver = check_driver();
/* Check if the nvidia drivers are available and if not check if the user is root */
if((!nv_driver) && (getuid() != 0 && geteuid() != 0))
{
set_error(NV_ERR_NO_DRIVERS_FOUND);
return 0;
}
/* Detect all nvidia cards; this needs to be done before creating directory and config file as that code needs card access */
if(!probe_devices())
{
/* probe_devices takes care of the error as it isn't certain it failed because of there are no nvidia cards */
return 0;
}
if(!open_config())
return 0;
return 1;
}
static int probe_devices()
{
int dev, irq, reg_addr, i=0;
unsigned short devbusfn;
char buf[256];
FILE *proc;
proc = fopen("/proc/bus/pci/devices", "r");
if(!proc)
{
set_error_str("Can't open /proc/bus/pci/devices to detect your videocard.");
return 0;
}
while(fgets(buf, sizeof(buf)-1, proc))
{
if(sscanf(buf,"%hx %x %x %x",&devbusfn, &dev, &irq, &reg_addr) != 4) continue;
/* Check if the card contains an Nvidia chipset */
if((dev>>16) == 0x10de)
{
/*
When we enter this block of code we know that the device contains some
chip designed by Nvidia. In the past Nvidia only produced videochips but
now they also make various other devices. Because of this we need to find
out if the device is a videocard or not. There are two ways to do this. We can
create a list of all Nvidia videochips or we can check the pci header of the device.
We will read the pci header from /proc/bus/pci/(bus)/(function).(device). When
the card is in our card database we report the name of the card and else we say
it is an unknown card.
*/
if(!IsVideoCard(devbusfn))
continue;
nvclock.card[i].device_id = (0x0000ffff & dev);
nvclock.card[i].arch = get_gpu_arch(nvclock.card[i].device_id);
nvclock.card[i].number = i;
nvclock.card[i].card_name = (char*)get_card_name(nvclock.card[i].device_id, &nvclock.card[i].gpu);
nvclock.card[i].devbusfn = devbusfn;
nvclock.card[i].irq = irq;
nvclock.card[i].state = 0;
/*
Thanks to all different driver version this is needed now.
When nv_driver > 1 the nvidia kernel module is loaded.
For driver versions < 1.0-40xx the register offset could be set to 0.
Thanks to a rewritten kernel module in 1.0-40xx the register offset needs
to be set again to the real offset.
*/
switch(check_driver())
{
case 0:
nvclock.card[i].dev_name = (char*)strdup("/dev/mem");
nvclock.card[i].reg_address = reg_addr;
break;
case 1:
nvclock.card[i].dev_name = calloc(13, sizeof(char));
sprintf(nvclock.card[i].dev_name, "/dev/nvidia%d", nvclock.card[i].number);
nvclock.card[i].reg_address = 0;
break;
case 2:
nvclock.card[i].dev_name = calloc(13, sizeof(char));
sprintf(nvclock.card[i].dev_name, "/dev/nvidia%d", nvclock.card[i].number);
nvclock.card[i].reg_address = reg_addr;
break;
}
i++;
}
}
fclose(proc);
if(i==0)
{
set_error(NV_ERR_NO_DEVICES_FOUND);
return 0;
}
nvclock.num_cards = i;
return 1;
}
/* Check if the device is a videocard */
static int IsVideoCard(unsigned short devbusfn)
{
int32_t pci_class = pciReadLong(devbusfn, 0x9);
/* When the id isn't 0x03 the card isn't a vga card return 0 */
if(((htonl(pci_class) >> 8) & 0xf) != 0x03)
return 0;
else
return 1;
}
int32_t pciReadLong(unsigned short devbusfn, long offset)
{
char file[25];
FILE *device;
short bus = PCI_GET_BUS(devbusfn);
short dev = PCI_GET_DEVICE(devbusfn);
short function = PCI_GET_FUNCTION(devbusfn);
snprintf(file, sizeof(file), "/proc/bus/pci/%02x/%02x.%x", bus, dev, function);
if((device = fopen(file, "r")) != NULL)
{
int32_t buffer;
fseek(device, offset, SEEK_SET);
fread(&buffer, sizeof(int32_t), 1, device);
fclose(device);
return buffer;
}
return -1;
}
int map_mem(const char *dev_name)
{
int fd;
if( (fd = open(dev_name, O_RDWR)) == -1 )
{
char err[80];
sprintf(err, "Can't open %s", dev_name);
set_error_str(err);
return 0;
}
/* Map the registers of the nVidia chip */
nv_card->PEXTDEV = map_dev_mem(fd, nv_card->reg_address + 0x101000, 0x1000);
nv_card->PFB = map_dev_mem(fd, nv_card->reg_address + 0x100000, 0x1000);
/* normally pmc is till 0x2000 but extended it for nv40 */
nv_card->PMC = map_dev_mem(fd, nv_card->reg_address + 0x000000, 0x2ffff);
nv_card->PCIO = map_dev_mem(fd, nv_card->reg_address + 0x601000, 0x2000);
nv_card->PRAMDAC = map_dev_mem(fd, nv_card->reg_address + 0x680000, 0x2000);
nv_card->PROM = map_dev_mem(fd, nv_card->reg_address + 0x300000, 0xffff);
/* On Geforce 8xxx cards it appears that the pci config header has been moved */
if(nv_card->arch & NV5X)
nv_card->PBUS = map_dev_mem(fd, nv_card->reg_address + 0x88000, 0x100);
else
nv_card->PBUS = nv_card->PMC + 0x1800/4;
nv_card->mem_mapped = 1;
close(fd);
return 1;
}
void unmap_mem()
{
unmap_dev_mem((unsigned long)nv_card->PEXTDEV, 0x1000);
unmap_dev_mem((unsigned long)nv_card->PFB, 0x1000);
unmap_dev_mem((unsigned long)nv_card->PMC, 0xffff);
unmap_dev_mem((unsigned long)nv_card->PCIO, 0x2000);
unmap_dev_mem((unsigned long)nv_card->PRAMDAC, 0x2000);
unmap_dev_mem((unsigned long)nv_card->PROM, 0xffff);
}
/* -------- mmap on devices -------- */
/* This piece of code is from nvtv a linux program for tvout */
/* The author of nvtv got this from xfree86's os-support/linux/lnx_video.c */
/* and he modified it a little */
static void *map_dev_mem (int fd, unsigned long Base, unsigned long Size)
{
void *base;
int mapflags = MAP_SHARED;
unsigned long realBase, alignOff;
realBase = Base & ~(getpagesize() - 1);
alignOff = Base - realBase;
base = mmap((caddr_t)0, Size + alignOff, PROT_READ|PROT_WRITE,
mapflags, fd, (off_t)realBase);
return (void *) ((char *)base + alignOff);
}
static void unmap_dev_mem (unsigned long Base, unsigned long Size)
{
unsigned long alignOff = Base - (Base & ~(getpagesize() - 1));
munmap((caddr_t)(Base - alignOff), (Size + alignOff));
nv_card->mem_mapped = 0;
}

BIN
nvclock/back_linux.o Normal file

Binary file not shown.

162
nvclock/back_win32.c Normal file
View File

@@ -0,0 +1,162 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* Copyright(C) 2001-2007 Roderick Colenbrander
*
* site: http://nvclock.sourceforge.net
*
* 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 <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "backend.h"
#include "MemAcc.h"
void *lib_winio = NULL;
int (__stdcall *pOpenLibrary)();
int (__stdcall *pGetLastState)(char *sStatus);
int (__stdcall *pGetDeviceBaseAddress)(unsigned short* VendorID, unsigned short*
DeviceID, LONG nIndex, BADDR baddrBuffer[6]);
int (__stdcall *pGetPCIDeviceInfo)(unsigned short* VendorID, unsigned short* DeviceID, long nIndex, unsigned long* Bus, unsigned
long* Device, unsigned long* Function, PCI_CONFIG_HEADER *pcfg);
void* (__stdcall *pMapPhysToLinear)(DWORD PhAddr, DWORD dwSize, HANDLE* hMem);
static int probe_devices();
NVClock nvclock;
NVCard *nv_card = NULL;
int init_nvclock()
{
void *lib_memacc = LoadLibrary("MemAcc.dll");
int nResult, nIndex;
int VendorID, DeviceID;
char* s;
int i;
char sError[255];
if(lib_memacc == NULL)
{
set_error_str("Can't open MemAcc.dll\n");
return 0;
}
pOpenLibrary = (void*)GetProcAddress(lib_memacc, "maOpenLibrary");
pGetLastState = (void*)GetProcAddress(lib_memacc, "maGetLastState");
pGetDeviceBaseAddress = (void*)GetProcAddress(lib_memacc, "maGetDeviceBaseAddress");
pGetPCIDeviceInfo = (void*)GetProcAddress(lib_memacc, "maGetPCIDeviceInfo");
pMapPhysToLinear = (void*)GetProcAddress(lib_memacc, "maMapPhysToLinear");
if(!pOpenLibrary())
{
char buf[80];
pGetLastState(buf);
printf("Loading of MemAcc.dll failed: %s \n", buf);
}
/* Detect all nvidia cards; this needs to be done before creating directory and config file as that code needs card access */
if(!probe_devices())
{
/* probe_devices takes care of the error as it isn't certain it failed because of there are no nvidia cards */
return 0;
}
if(!open_config())
return 0;
return 1;
}
static int probe_devices()
{
unsigned short vendor_id=0xffff, device_id=0xffff;
int index=0;
int i=0;
unsigned long bus, dev, function;
PCI_CONFIG_HEADER pCfg;
BADDR baddrAddress[6];
while(pGetPCIDeviceInfo(&vendor_id, &device_id, index, &bus, &dev, &function, &pCfg) == 1)
{
/* Check whether the vendor is nvidia and the BaseClass == VGA */
if(vendor_id == 0x10de && pCfg.BaseClass == 0x3)
{
printf("Found VendorID: 0x%x DeviceID: 0x%x\r\n", vendor_id, device_id);
nvclock.card[i].device_id = device_id;
nvclock.card[i].arch = get_gpu_arch(nvclock.card[i].device_id);
nvclock.card[i].number = i;
nvclock.card[i].card_name = (char*)get_card_name(nvclock.card[i].device_id, &nvclock.card[i].gpu);
nvclock.card[i].reg_address = pCfg.BaseAddresses[0];
// nvclock.card[i].devbusfn = devbusfn;
nvclock.card[i].irq = pCfg.InterruptLine;
nvclock.card[i].state = 0;
i++;
}
index++;
vendor_id = 0xffff;
device_id = 0xffff;
}
nvclock.num_cards = i;
return 1;
}
int32_t pciReadLong(unsigned short devbusfn, long offset)
{
return -1;
}
int map_mem(const char *dev_name)
{
void *hmem; // do nothing with this for now
/* Map the registers of the nVidia chip */
nv_card->PEXTDEV = pMapPhysToLinear(nv_card->reg_address + 0x101000, 0x1000, &hmem);
nv_card->PFB = pMapPhysToLinear(nv_card->reg_address + 0x100000, 0x1000, &hmem);
/* normally pmc is till 0x2000 but extended it for nv40 */
nv_card->PMC = pMapPhysToLinear(nv_card->reg_address + 0x000000, 0x2ffff, &hmem);
nv_card->PCIO = pMapPhysToLinear(nv_card->reg_address + 0x601000, 0x2000, &hmem);
nv_card->PRAMDAC = pMapPhysToLinear(nv_card->reg_address + 0x680000, 0x2000, &hmem);
nv_card->PROM = pMapPhysToLinear(nv_card->reg_address + 0x300000, 0xffff, &hmem);
/* On Geforce 8xxx cards it appears that the pci config header has been moved */
if(nv_card->arch & NV5X)
nv_card->PBUS = pMapPhysToLinear(nv_card->reg_address + 0x88000, 0x100, &hmem);
else
nv_card->PBUS = nv_card->PMC + 0x1800/4;
nv_card->mem_mapped = 1;
return 1;
}
void unmap_mem()
{
#if 0
pUnMapIO(winio, (void*)nv_card->PEXTDEV);
pUnMapIO(winio, (void*)nv_card->PFB);
pUnMapIO(winio, (void*)nv_card->PMC);
pUnMapIO(winio, (void*)nv_card->PCIO);
pUnMapIO(winio, (void*)nv_card->PRAMDAC);
pUnMapIO(winio, (void*)nv_card->PROM);
nv_card->mem_mapped = 0;
#endif
}

127
nvclock/backend.c Normal file
View File

@@ -0,0 +1,127 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* Copyright(C) 2001-2007 Roderick Colenbrander
*
* site: http://nvclock.sourceforge.net
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "config.h"
#ifdef HAVE_NVCONTROL
#include "nvcontrol.h"
#endif
#include "backend.h"
/* Read a byte from the pci bus */
unsigned char nv_read_pbus8(int offset)
{
int shift = (offset % 4)*8;
return (nv_card->PBUS[offset/4] >> shift) & 0xff;
}
/* Read an unsigned short from the pci bus */
unsigned short nv_read_pbus16(int offset)
{
int shift = (offset / 2)*16;
return (nv_card->PBUS[offset/4] >> shift) & 0xffff;
}
/* Read an unsigned int from the pci bus */
unsigned int nv_read_pbus(int offset)
{
return nv_card->PBUS[offset/4];
}
/* Read an unsigned int from the PMC registers */
unsigned int nv_read_pmc(int offset)
{
return nv_card->PMC[offset/4];
}
/* This function is actually a basic version of set_card.
/ It mainly copies the entries of the card list and maps
/ the video registers. We need this function because we need
/ access to the videocard from the config file creation code.
/ At that stage we can't use the normal set_card because that
/ function also sets function pointers and uses bios/config
/ file info which we don't have yet.
*/
int set_card_info(int number)
{
nv_card = &nvclock.card[number];
if(!nv_card->mem_mapped)
if(!map_mem(nv_card->dev_name))
return 0; /* map_mem already took care of the error */
return 1;
}
/* Set the card object to the requested card */
int set_card(int number)
{
int have_coolbits, irq;
if(!set_card_info(number))
return 0;
/* nvcontrol detection */
#ifdef HAVE_NVCONTROL
/* We need an X display to check if NV-CONTROL support exists */
if(nvclock.dpy)
{
/* Check if we have NV-CONTROL support */
if(init_nvcontrol(nvclock.dpy))
{
int tmp;
have_coolbits = NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_OVERCLOCKING_STATE, &tmp);
/* Also retrieve the irq which is used to sync nvclock and NV-CONTROL */
NVGetAttribute(nvclock.dpy, 0, 0, NV_IRQ, &irq);
if(have_coolbits && (nv_card->irq == irq))
{
nv_card->caps |= COOLBITS_OVERCLOCKING;
/* By default use Coolbits on NV3X / NV4X cards */
if(!nv_card->state && nv_card->arch & (NV3X|NV4X))
nv_card->state = STATE_3D;
}
}
}
#endif /* HAVE_NVCONTROL */
info_init();
if(nv_card->arch & NV3X)
nv30_init();
else if(nv_card->arch & NV4X)
nv40_init();
else if(nv_card->arch & NV5X)
nv50_init();
else
nv_init();
return 1;
}
void unset_card()
{
unmap_mem();
}

87
nvclock/backend.h Normal file
View File

@@ -0,0 +1,87 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* Copyright(C) 2001-2005 Roderick Colenbrander
*
* site: http://nvclock.sourceforge.net
*
* 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 "config.h"
#include "i2c.h"
#include "nvclock.h"
#include "nvreg.h"
#include <stdint.h>
/* Thanks to Alexey Nicolaychuk (Unwinder), the author of Rivatuner, for providing
these macros to get nv30 clock detection working.
*/
#define NV30_PLL_M1(PLL) ( ( PLL ) & 0x0f )
#define NV30_PLL_M2(PLL) ( ( ( PLL ) >> 4 ) & 0x07 )
#define NV30_PLL_N1(PLL) ( ( ( PLL ) >> 8 ) & 0xff )
#define NV30_PLL_N2(PLL) ( ( ( ( PLL ) >> 19 ) & 0x07 ) | ( ( ( PLL ) >> 21 ) & 0x18 ) )
#define NV30_PLL_P(PLL) ( ( ( PLL ) >> 16 ) & 0x07 )
#define PCI_GET_BUS(devbusfn) ((devbusfn >> 8) & 0xff)
#define PCI_GET_DEVICE(devbusfn) ((devbusfn & 0xff) >> 3)
#define PCI_GET_FUNCTION(devbusfn) (devbusfn & 0x7)
#define PCI_GET_DEVBUSFN(dev, bus, fn) ((bus << 8) | (dev << 3) | (fn & 0x7))
/* Set the card object to the requested card */
int set_card(int number);
/* Some internally needed functions */
const char* get_card_name(int device_id, gpu_type *gpu);
int get_gpu_arch(int device_id);
int set_card_info(int number); /* Basic version of set_card */
int map_mem(const char* dev_name);
void unmap_mem();
int32_t pciReadLong(unsigned short devbusfn, long offset);
/* Bios related stuff */
void dump_bios(char *filename);
struct nvbios* read_bios(char *filename);
/* NV-CONTROL overclocking functions */
float nvcontrol_get_gpu_speed();
float nvcontrol_get_memory_speed(); /* NV-CONTROL wrapper */
void nvcontrol_set_gpu_speed(unsigned int nvclk);
void nvcontrol_set_memory_speed(unsigned int memclk);
void nvcontrol_reset_gpu_speed();
void nvcontrol_reset_memory_speed();
/* PLL to clock conversion */
float GetClock(int base_freq, unsigned int pll);
float GetClock_nv30(int base_freq, unsigned int pll, unsigned int pll2);
float GetClock_nv40(int base_freq, unsigned int pll, unsigned int pll2);
float GetClock_nv50(int base_freq, unsigned int pll, unsigned int pll2);
void info_init(void);
void i2c_sensor_init(void);
void nv_init(void);
void nv30_init(void);
void nv31_init(void);
void nv40_init(void);
void nv50_init(void);
/* PCI bus reading */
unsigned char nv_read_pbus8(int offset);
unsigned short nv_read_pbus16(int offset);
unsigned int nv_read_pbus(int offset);
/* PMC reading */
unsigned int nv_read_pmc(int offset);

BIN
nvclock/backend.o Normal file

Binary file not shown.

1068
nvclock/bios.c Normal file

File diff suppressed because it is too large Load Diff

BIN
nvclock/bios.o Normal file

Binary file not shown.

606
nvclock/config.c Normal file
View File

@@ -0,0 +1,606 @@
/* 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 <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "backend.h"
#include "nvclock.h"
static void insert_entry(cfg_entry **cfg, cfg_entry *entry);
/* Free the config file structure */
void destroy(cfg_entry **cfg)
{
cfg_entry *pCfg, *pPrevious;
pCfg = *cfg;
while(pCfg)
{
pPrevious = pCfg;
free(pPrevious->name);
free(pPrevious->section);
pCfg = pCfg->next;
free(pPrevious);
}
}
/* Add a new config file entry section.name=value */
void add_entry(cfg_entry **cfg, char *section, char *name, int value)
{
cfg_entry *entry;
entry = (cfg_entry*)calloc(1, sizeof(cfg_entry));
entry->name = (char*)strdup(name);
entry->section = (char*)strdup(section);
entry->value = value;
entry->next = NULL;
insert_entry(cfg, entry);
}
/* Change the value of an entry or add an entry when it doesn't exist */
void change_entry(cfg_entry **cfg, char *section, char *name, int value)
{
cfg_entry *entry = lookup_entry(cfg, section, name);
/* When an entry is found update it */
if(entry)
{
entry->value=value;
}
/* Create a new entry */
else
{
add_entry(cfg, section, name, value);
}
}
/* Insert an entry into the config file structure */
static void insert_entry(cfg_entry **cfg, cfg_entry *entry)
{
cfg_entry *pCfg = NULL;
cfg_entry *pPrev = NULL;
cfg_entry *pNext = NULL;
/* When the cfg list is still empty, add the first entry */
if(!*cfg)
{
*cfg = entry;
return;
}
/* Sort the list by section and add the entry */
for(pCfg = *cfg; pCfg != NULL; pPrev=pCfg, pCfg = pCfg->next, pNext = pCfg ? pCfg->next : NULL )
{
/* Check if the entry belongs to a section below the current section */
if(strcmp(entry->section, pCfg->section) < 0)
{
if(!pPrev)
{
*cfg = entry;
entry->next = pCfg;
}
else
{
pPrev->next = entry;
entry->next = pCfg;
}
return;
}
if(strcmp(entry->section, pCfg->section) == 0)
{
/* The entry needs to be placed before the current entry */
if(strcmp(entry->name, pCfg->name) < 0)
{
if(!pPrev)
{
*cfg = entry;
entry->next = pCfg;
}
else
{
pPrev->next = entry;
entry->next = pCfg;
}
return;
}
/* When there's a next entry, check if it belongs to the same section or
/ else add the option to the current section.
*/
if(pNext)
{
/* The sections don't match, so add the option to the current one */
if(strcmp(entry->section, pNext->section) != 0)
{
pCfg->next = entry;
entry->next = pNext;
return;
}
}
}
/* Entry should become the last one */
if(!pCfg->next)
{
pCfg->next = entry;
return;
}
continue;
}
}
/* Look up section.name */
cfg_entry* lookup_entry(cfg_entry **cfg, char *section, char *name)
{
cfg_entry *entry = *cfg;
while(entry)
{
/* If everything matches, the option is found */
if(!strcmp(entry->section, section) && !strcmp(entry->name, name))
{
return entry;
}
entry = (cfg_entry*)entry->next;
}
return NULL;
}
/* Helper function that splits lines of the form section.name=value */
static int split_line(const char *line, char **section, char **name, char **value)
{
char *a, *b;
if(!(a = strchr(line, '.')))
return 0;
/* overwrite '.' with '\0' */
a[0] = '\0';
*section = (char*)strdup(line);
a++;
if(!(b = strchr(a, '=')))
return 0;
/* overwrite '=' with '\0' */
b[0] = '\0';
*name = (char*)strdup(a);
b++;
/* overwrite '\n' with '\0' if '\n' present */
if((a = strchr(b, '\n')))
{
a[0] = '\0';
}
*value = (char*)strdup(b);
return 1;
}
/* Reads the config file from disc and stores it in cfg */
int read_config(cfg_entry **cfg, char *file)
{
char line[80];
FILE *fp;
fp = fopen(file, "r");
if(!fp)
{
return 0;
}
while(fgets(line, 80, fp) != NULL)
{
char *name, *section, *value;
cfg_entry *entry = NULL;
if((line[0] == '#'))
continue; /* Skip comments */
if((line[0] == '<'))
continue; /* There's no section on this line */
if((line[0] == '\n'))
continue; /* There's no section on this line */
if(strchr(line, '=') == NULL)
continue;
if(!split_line(line, &section, &name, &value))
{
free(name);
free(section);
free(value);
continue;
}
/* Add the entry when it doesn't exist; we don't want double options*/
if(!(entry = lookup_entry(cfg, section, name)))
{
/* Use strtoul instead of atoi as on some nv40 cards we get issues regarding signed/unsigned */
add_entry(cfg, section, name, strtoul(value, (char**)NULL, 10));
}
free(name);
free(section);
free(value);
}
fclose(fp);
return 1;
}
/* Looks if a config file exists and then makes sure it will be read and parsed.
/ When no config file exists create it.
*/
int open_config()
{
char *file = NULL;
char *home = NULL;
struct stat stat_buf;
if(!(home = getenv("HOME")))
{
/* error */
}
nvclock.path = malloc(strlen(home) + strlen("/.nvclock") +1);
sprintf(nvclock.path, "%s/.nvclock", home);
file = malloc(strlen(nvclock.path) + strlen("/config") +1);
sprintf(file, "%s/config", nvclock.path);
/* Check if our ~/.nvclock directory exists if not create it */
if(stat(nvclock.path, &stat_buf))
{
if(mkdir(nvclock.path, 0755))
{
char buf[80];
sprintf(buf, "Can't create '%s'. Do you have sufficient permissions?\n", nvclock.path);
set_error_str(buf);
return 0;
}
}
/* Check if .nvclock really is a directory. For some users it was a file and this led to a segfault. */
if(!S_ISDIR(stat_buf.st_mode))
{
char buf[80];
sprintf(buf, "Can't open '%s'. Is it really a directory?\n", nvclock.path);
set_error_str(buf);
return 0;
}
/* If there's no config or if the config is corrupt create a new one */
if(!parse_config(file))
{
create_config(file);
}
free(file);
return 1;
}
void write_config(cfg_entry *cfg, char *file)
{
FILE *fp = fopen(file, "w+");
cfg_entry *pCfg = NULL;
pCfg = cfg;
fprintf(fp, "#This is NVClock's config file. Don't edit the hw and general section!\n");
while(pCfg != NULL)
{
fprintf(fp, "%s.%s=%u\n", pCfg->section, pCfg->name, pCfg->value);
pCfg = pCfg->next;
}
fclose(fp);
}
/* Some basic check to see if frequencies can be valid */
static int validate_clock(int arch, int freq)
{
if(arch & (NV5))
{
if((freq > 75) && (freq < 250))
return 1;
}
/* Geforce2/2MX/4MX */
else if(arch & (NV1X))
{
if((freq > 125) && (freq < 500))
return 1;
}
/* Geforce3/3Ti/4Ti/FX5200/FX5500 */
else if(arch & (NV2X))
{
if((freq > 200) && (freq < 800))
return 1;
}
/* GeforceFX */
else if(arch & (NV3X))
{
if((freq > 250) && (freq < 1100))
return 1;
}
/* Geforce6/7/8*/
else if(arch & (NV4X | NV5X))
{
if((freq > 250) && (freq < 1200))
return 1;
}
return 0;
}
/* Some basic check to verify if a stored pll can be correct */
static int validate_pll(int arch, int base_freq, unsigned int pll, unsigned int pll2)
{
int freq;
if(arch & (NV5 | NV1X | NV2X))
{
freq = (int)GetClock(base_freq, pll);
if(validate_clock(arch, freq))
return 1;
}
else if(arch & (NV3X))
{
freq = (int)GetClock_nv30(base_freq, pll, pll2);
if(validate_clock(arch, freq))
return 1;
}
else if(arch & (NV4X))
{
freq = (int)GetClock_nv40(base_freq, pll, pll2);
if(validate_clock(arch, freq))
return 1;
}
else if(arch & (NV5X))
{
freq = (int)GetClock_nv50(base_freq, pll, pll2);
if(validate_clock(arch, freq))
return 1;
}
return 0;
}
/* Parse the config file and do something with its contents */
int parse_config(char *file)
{
int i;
cfg_entry *general;
if(!read_config(&nvclock.cfg, file))
return 0;
if((general = (cfg_entry*)lookup_entry(&nvclock.cfg, "general", "cards")) == NULL)
{
return 0;
}
else
{
/* Check if we have the same number of videocards as before */
if(general->value != nvclock.num_cards)
return 0;
/* Walk through all detected cards */
for(i=0; i < nvclock.num_cards; i++)
{
cfg_entry *entry;
char section[4];
char filename[80];
int base_freq=0;
struct stat tmp;
struct nvbios *bios;
if(!set_card_info(i))
return 0; /* Make us fail; set_card_info already set the error */
sprintf(section, "hw%d", i);
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "card")))
{
/* The order in wich the detected cards are listed should match the order of the ones in the config file. Mask the last digit for device_id modding purposes.*/
if((nvclock.card[i].device_id & 0xfff0) != (entry->value & 0xfff0))
return 0;
}
else
{
return 0;
}
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "basefreq")))
{
base_freq = entry->value;
}
else
return 0;
/* The bios works differently on ppc and other architectures; it is also stored in a different place */
#if defined(__i386__) || defined(__ia64__) || defined(__x86_64__)
/* During this stage we also need to parse the nvidia bios.
/ This can't be done in probe_devices as it depends on the config file
/ which might not exist at that time yet
*/
sprintf(filename, "%s/bios%d.rom", nvclock.path, i);
/* Redump the bios when the file doesn't exist */
if(stat(filename, &tmp))
return 0;
/* Read the bios. Note the result can be NULL in case the
/ bios is corrupt. We don't redump the bios because the bios
/ is not dumpable on some systems. For example on various laptops
/ the bios is stored at a different place not reachable by us.
*/
bios = read_bios(filename); /* GCC 4.0.1 (what about others?) doesn't like it when we directly do nclock.card[i].bios = readbios(filename); works fine without optimizations */
nvclock.card[i].bios = bios;
#else
nvclock.card[i].bios = NULL;
#endif
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "mpll")))
nvclock.card[i].mpll = entry->value;
else
/* corrupted config file */
return 0;
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "nvpll")))
nvclock.card[i].nvpll = entry->value;
else
return 0;
/* NV31, NV4X and NV5X cards have extra pll registers; in case of NV30 the second pll is ignored but that happens in GetClock_nv30 */
if(nv_card->arch & (NV3X | NV4X | NV5X))
{
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "mpll2")))
nvclock.card[i].mpll2 = entry->value;
else
return 0;
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "nvpll2")))
nvclock.card[i].nvpll2 = entry->value;
else
return 0;
}
/* Do some basic check on mpll/nvpll to see if they can be correct */
if(!validate_pll(nvclock.card[i].arch, base_freq, nvclock.card[i].mpll, nvclock.card[i].mpll2))
return 0;
if(!validate_pll(nvclock.card[i].arch, base_freq, nvclock.card[i].nvpll, nvclock.card[i].nvpll2))
return 0;
}
}
/* Reset the nv_card object else things might go wrong */
unset_card();
/* Return succes */
return 1;
}
/* Create a config file based on info we get from the low-level part of nvclock */
int create_config(char *file)
{
int i;
if(nvclock.cfg)
{
destroy(&nvclock.cfg);
nvclock.cfg = NULL;
}
add_entry(&nvclock.cfg, "general", "cards", nvclock.num_cards);
/* Write the config file */
for(i=0; i < nvclock.num_cards; i++)
{
char section[4];
char bios[80];
int base_freq;
/* Set the nv_card object to the card; Note this is a bit basic; function pointers can't be used */
if(!set_card_info(i))
return 0; /* Make us fail; set_card_info already set the error */
sprintf(section, "hw%d", i);
add_entry(&nvclock.cfg, section, "card", nv_card->device_id);
#if defined(__i386__) || defined(__ia64__) || defined(__x86_64__)
/* needs to be changed to dump the file in the home dir; further we need some CRC check */
sprintf(bios, "%s/bios%d.rom", nvclock.path, i);
dump_bios(bios);
nvclock.card[i].bios = read_bios(bios);
#else
nvclock.card[i].bios = NULL;
#endif
base_freq = (nv_card->PEXTDEV[0x0000/4] & 0x40) ? 14318 : 13500;
if(nv_card->arch & (NV17 | NV25 | NV3X | NV4X | NV5X))
{
if (nv_card->PEXTDEV[0x0000/4] & (1<<22))
base_freq = 27000;
}
add_entry(&nvclock.cfg, section, "basefreq", base_freq);
/* TNT(1/2) and Geforce(1/2/3/4) */
if(nv_card->arch & (NV5 | NV1X | NV2X))
{
add_entry(&nvclock.cfg, section, "mpll", nv_card->PRAMDAC[0x504/4]);
add_entry(&nvclock.cfg, section, "nvpll", nv_card->PRAMDAC[0x500/4]);
nvclock.card[i].nvpll = nv_card->PRAMDAC[0x500/4];
nvclock.card[i].mpll = nv_card->PRAMDAC[0x504/4];
}
/* GeforceFX 5200/5500/5600/5700/5800/5900, note that FX5600/5700 require two PLLs */
else if(nv_card->arch & NV3X)
{
add_entry(&nvclock.cfg, section, "mpll", nv_card->PRAMDAC[0x504/4]);
add_entry(&nvclock.cfg, section, "mpll2", nv_card->PRAMDAC[0x574/4]);
add_entry(&nvclock.cfg, section, "nvpll", nv_card->PRAMDAC[0x500/4]);
add_entry(&nvclock.cfg, section, "nvpll2", nv_card->PRAMDAC[0x570/4]);
nvclock.card[i].mpll = nv_card->PRAMDAC[0x504/4];
nvclock.card[i].mpll2 = nv_card->PRAMDAC[0x574/4];
nvclock.card[i].nvpll = nv_card->PRAMDAC[0x500/4];
nvclock.card[i].nvpll2 = nv_card->PRAMDAC[0x570/4];
}
/* Geforce6/7 */
else if(nv_card->arch & NV4X)
{
add_entry(&nvclock.cfg, section, "mpll", nv_card->PMC[0x4020/4]);
add_entry(&nvclock.cfg, section, "mpll2", nv_card->PMC[0x4024/4]);
add_entry(&nvclock.cfg, section, "nvpll", nv_card->PMC[0x4000/4]);
add_entry(&nvclock.cfg, section, "nvpll2", nv_card->PMC[0x4004/4]);
nvclock.card[i].mpll = nv_card->PMC[0x4020/4];
nvclock.card[i].mpll2 = nv_card->PMC[0x4024/4];
nvclock.card[i].nvpll = nv_card->PMC[0x4000/4];
nvclock.card[i].nvpll2 = nv_card->PMC[0x4004/4];
}
/* Geforce8 */
else if(nv_card->arch & NV5X)
{
add_entry(&nvclock.cfg, section, "mpll", nv_card->PMC[0x4008/4]);
add_entry(&nvclock.cfg, section, "mpll2", nv_card->PMC[0x400c/4]);
add_entry(&nvclock.cfg, section, "nvpll", nv_card->PMC[0x4028/4]);
add_entry(&nvclock.cfg, section, "nvpll2", nv_card->PMC[0x402c/4]);
nvclock.card[i].mpll = nv_card->PMC[0x4008/4];
nvclock.card[i].mpll2 = nv_card->PMC[0x400c/4];
nvclock.card[i].nvpll = nv_card->PMC[0x4028/4];
nvclock.card[i].nvpll2 = nv_card->PMC[0x402c/4];
}
unset_card();
}
write_config(nvclock.cfg, file);
return 1;
}

0
nvclock/config.h Normal file
View File

BIN
nvclock/config.o Normal file

Binary file not shown.

63
nvclock/error.c Normal file
View File

@@ -0,0 +1,63 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2004 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 <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nvclock.h"
void set_error(int code)
{
nvclock.nv_errno = code;
}
void set_error_str(const char *str)
{
nvclock.nv_errno = NV_ERR_OTHER;
/* hacky; we need to think about memory management .. */
nvclock.nv_err_str = (char*)strdup(str);
}
char *get_error(char *buf, int size)
{
switch(nvclock.nv_errno)
{
case NV_ERR_NO_DEVICES_FOUND:
strcpy(buf, "No nvidia cards found in your system!");
break;
case NV_ERR_NO_DRIVERS_FOUND:
strcpy(buf, "You don't have enough permissions to run NVClock! Retry as root or install the Nvidia drivers.");
break;
case NV_ERR_NOT_ENOUGH_PERMISSIONS:
strcpy(buf, "You don't have enough permissions to run NVClock! Retry as root.");
break;
case NV_ERR_OTHER:
strcpy(buf, nvclock.nv_err_str);
break;
}
return buf;
}

BIN
nvclock/error.o Normal file

Binary file not shown.

167
nvclock/f75375.c Normal file
View File

@@ -0,0 +1,167 @@
/* Fintek F75375 sensor module
/ Copyright(C) 2005, Matt Wright
*/
#include <string.h>
#include "i2c.h"
#include "f75375.h"
int f75375_detect(I2CDevPtr dev)
{
I2CByte nvl, nvh;
xf86I2CReadByte(dev, FINTEK_VENDOR1, &nvl);
xf86I2CReadByte(dev, FINTEK_VENDOR2, &nvh);
if (MERGE_BYTE(nvh, nvl) != 0x3419)
{
return 0;
}
xf86I2CReadByte(dev, ASUS_NV40_CHIPID_H, &nvh);
xf86I2CReadByte(dev, ASUS_NV40_CHIPID_L, &nvl);
if (MERGE_BYTE(nvh, nvl) == 0x0306)
{
dev->chip_id = F75375;
dev->chip_name = (char*)strdup("Fintek F75375S");
return 1;
}
if (MERGE_BYTE(nvh, nvl) == 0x0204)
{
dev->chip_id = F75375;
dev->chip_name = (char*)strdup("Fintek F75373S");
return 1;
}
return 0;
}
int f75375_get_gpu_temp(I2CDevPtr dev)
{
I2CByte nvh;
xf86I2CReadByte(dev, F75375S_TEMP_GPU, &nvh);
return (int)nvh;
}
int f75375_get_fanspeed_rpm(I2CDevPtr dev)
{
I2CByte nvh, nvl;
int rpm;
xf86I2CReadByte(dev, F75375S_FAN1_COUNT_H, &nvh);
xf86I2CReadByte(dev, F75375S_FAN1_COUNT_L, &nvl);
rpm = FAN_TO_RPM(nvh, nvl);
return rpm;
}
int f75375_get_board_temp(I2CDevPtr dev)
{
I2CByte nvh;
xf86I2CReadByte(dev, F75375S_TEMP_RAM, &nvh);
return (int)nvh;
}
int f75375_set_fanspeed_rpm(I2CDevPtr dev, int desired_rpm)
{
I2CByte nvh, nvl;
int desired_count;
desired_count = RPM_TO_FAN(desired_rpm);
nvh = (desired_count>>8) & 0x00ff;
nvl = (desired_count) & 0x00ff;
xf86I2CWriteByte(dev, F75375S_FAN1_EXPECT_H, nvh);
xf86I2CWriteByte(dev, F75375S_FAN1_EXPECT_L, nvl);
return 1;
}
int f75375_set_gpu_tempctl(I2CDevPtr dev, fan_vtemp speeds)
{
I2CByte nvh, nvl;
int i, temp_speed;
for (i=0; i<4; i++)
{
xf86I2CWriteByte(dev, F75375S_VT1_B1 + i, speeds.temp[i]);
}
for (i=0; i<5; i++)
{
temp_speed = RPM_TO_FAN(speeds.speed[i]);
nvh = (temp_speed >> 8) & 0x00ff;
nvl = (temp_speed) & 0x00ff;
xf86I2CWriteByte(dev, F75375S_VT1_S1_H + (i*2), nvh);
xf86I2CWriteByte(dev, F75375S_VT1_S1_L + (i*2), nvl);
}
return 0;
}
int f75375_get_gpu_tempctl(I2CDevPtr dev, fan_vtemp *speeds)
{
I2CByte nvh, nvl;
int i;
for (i=0; i<4; i++)
{
xf86I2CReadByte(dev, F75375S_VT1_B1 + i, &nvl);
speeds->temp[i] = (int)nvl;
}
for (i=0; i<5; i++)
{
xf86I2CReadByte(dev, F75375S_VT1_S1_H + (i*2), &nvh);
xf86I2CReadByte(dev, F75375S_VT1_S1_L + (i*2), &nvl);
speeds->speed[i] = FAN_TO_RPM(nvh, nvl);
}
return 0;
}
int f75375_get_gpu_fanmode(I2CDevPtr dev)
{
I2CByte mode;
xf86I2CReadByte(dev, F75375S_FAN1_MODE, &mode);
return (int)mode;
}
int f75375_set_gpu_fanmode(I2CDevPtr dev, I2CByte mode)
{
xf86I2CWriteByte(dev, F75375S_FAN1_MODE, (I2CByte)mode);
return 0;
}
float f75375_get_fanspeed_pwm(I2CDevPtr dev)
{
I2CByte speed;
xf86I2CReadByte(dev, F75375S_FAN1_PWM, &speed);
return (float)speed*100/256;
}
int f75375_set_fanspeed_pwm(I2CDevPtr dev, float speed)
{
I2CByte value = (I2CByte)(speed * 255/100);
xf86I2CWriteByte(dev, F75375S_FAN1_PWM, value);
return 0;
}

71
nvclock/f75375.h Normal file
View File

@@ -0,0 +1,71 @@
/* Fintek F75375 sensor module
/ Copyright(C) 2005, Matt Wright
*/
#include "nvclock.h"
#include "i2c.h"
int debug;
typedef struct _fan_vtemp
{
int temp[4];
int speed[5];
} fan_vtemp;
#define dbg_printf(x) if (debug==1) printf(x)
#define MODE_SPEED 0x00
#define MODE_TEMP 0x10
#define MODE_PWM 0x20
int f75375_get_gpu_fanmode(I2CDevPtr dev);
int f75375_set_gpu_fanmode(I2CDevPtr dev, I2CByte mode);
int f75375_set_gpu_tempctl(I2CDevPtr dev, fan_vtemp speeds);
int f75375_get_gpu_tempctl(I2CDevPtr dev, fan_vtemp *speeds);
#define FINTEK_VENDOR1 0x5d
#define FINTEK_VENDOR2 0x5e
#define ASUS_NV40_CHIPID_H 0x5a
#define ASUS_NV40_CHIPID_L 0x5b
#define F75375S_VRAM_VCC 0x10
#define F75375S_VRAM_V1 0x11
#define F75375S_VRAM_V2 0x12
#define F75375S_VRAM_V3 0x13
#define F75375S_VRAM_TEMP1 0x14
#define F75375S_VRAM_TEMP2 0x15
#define F75375S_VRAM_FAN1_MSB 0x16
#define F75375S_VRAM_FAN1_LSB 0x17
#define F75375S_VRAM_FAN2_MSB 0x18
#define F75375S_VRAM_FAN2_LSB 0x19
#define F75375S_FAN1_PWM 0x76
#define F75375S_FAN1_COUNT_H 0x16
#define F75375S_FAN1_COUNT_L 0x17
#define F75375S_FAN1_MODE 0x60
#define F75375S_FAN1_EXPECT_H 0x74
#define F75375S_FAN1_EXPECT_L 0x75
#define F75375S_TEMP_GPU 0x14
#define F75375S_TEMP_RAM 0x15
#define F75375S_VT1_B1 0xa0
#define F75375S_VT1_B2 0xa1
#define F75375S_VT1_B3 0xa2
#define F75375S_VT1_B4 0xa3
#define F75375S_VT1_S1_H 0xa4
#define F75375S_VT1_S1_L 0xa5
#define F75375S_VT1_S2_H 0xa6
#define F75375S_VT1_S2_L 0xa7
#define F75375S_VT1_S3_H 0xa8
#define F75375S_VT1_S3_L 0xa9
#define F75375S_VT1_S4_H 0xaa
#define F75375S_VT1_S4_L 0xab
#define F75375S_VT1_S5_H 0xac
#define F75375S_VT1_S5_L 0xad
#define FAN_TO_RPM(msb, lsb) (1500000/((msb<<8)+lsb))
#define RPM_TO_FAN(x) (1500000/x)
#define MERGE_BYTE(msb, lsb) ((msb<<8)+lsb)

BIN
nvclock/f75375.o Normal file

Binary file not shown.

344
nvclock/i2c.c Normal file
View File

@@ -0,0 +1,344 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2006 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
*
* I2C support needed for hardware monitoring, this code is partly based on code from nvtv
* and the opensource xfree86 nv driver.
*/
/* NVTV TV common routines -- Dirk Thierbach <dthierbach@gmx.de>
*
* This file is part of nvtv, a tool for tv-output on NVidia cards.
*
* nvtv 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.
*
* nvtv 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
*
* $Id: i2c.c,v 1.14 2007/04/24 08:10:47 thunderbird Exp $
*
* Contents:
*
* Header: Common tv-related routines.
*
*/
/***************************************************************************\
|* *|
|* Copyright 2003 NVIDIA, Corporation. All rights reserved. *|
|* *|
|* NOTICE TO USER: The source code is copyrighted under U.S. and *|
|* international laws. Users and possessors of this source code are *|
|* hereby granted a nonexclusive, royalty-free copyright license to *|
|* use this code in individual and commercial software. *|
|* *|
|* Any use of this source code must include, in the user documenta- *|
|* tion and internal comments to the code, notices to the end user *|
|* as follows: *|
|* *|
|* Copyright 2003 NVIDIA, Corporation. All rights reserved. *|
|* *|
|* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *|
|* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *|
|* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *|
|* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *|
|* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *|
|* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *|
|* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *|
|* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *|
|* SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION *|
|* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *|
|* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. *|
|* *|
|* U.S. Government End Users. This source code is a "commercial *|
|* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *|
|* consisting of "commercial computer software" and "commercial *|
|* computer software documentation," as such terms are used in *|
|* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *|
|* ment only as a commercial end item. Consistent with 48 C.F.R. *|
|* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *|
|* all U.S. Government End Users acquire the source code with only *|
|* those rights set forth herein. *|
|* *|
\***************************************************************************/
/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nv_dac.c,v 1.43 2004/11/30 23:50:26 mvojkovi Exp $ */
#include <unistd.h>
#include <stdarg.h>
#include "xf86i2c.h"
#include "backend.h"
/*
* DDC1 support only requires DDC_SDA_MASK,
* DDC2 support requires DDC_SDA_MASK and DDC_SCL_MASK
*/
#define DDC_SDA_READ_MASK (1 << 3)
#define DDC_SCL_READ_MASK (1 << 2)
#define DDC_SDA_WRITE_MASK (1 << 4)
#define DDC_SCL_WRITE_MASK (1 << 5)
static void NVLockUnlock(int lock)
{
unsigned char cr11;
nv_card->PCIO[0x3d4] = 0x1f;
nv_card->PCIO[0x3d5] = lock ? 0x99 : 0x57;
nv_card->PCIO[0x3d4] = 0x11;
cr11 = nv_card->PCIO[0x3d5];
if(lock) cr11 |= 0x80;
else cr11 &= ~0x80;
nv_card->PCIO[0x3d5] = cr11;
}
static void NV_I2CGetBits(I2CBusPtr b, int *clock, int *data)
{
unsigned char val;
int DDCBase = (int)b->DriverPrivate.val;
/* Get the result. */
nv_card->PCIO[0x3d4] = DDCBase;
val = nv_card->PCIO[0x3d5];
*clock = (val & DDC_SCL_READ_MASK) != 0;
*data = (val & DDC_SDA_READ_MASK) != 0;
}
static void NV_I2CPutBits(I2CBusPtr b, int clock, int data)
{
unsigned char val;
int DDCBase = (int)b->DriverPrivate.val;
nv_card->PCIO[0x3d4] = DDCBase + 1;
val = nv_card->PCIO[0x3d5] & 0xf0;
if (clock)
val |= DDC_SCL_WRITE_MASK;
else
val &= ~DDC_SCL_WRITE_MASK;
if (data)
val |= DDC_SDA_WRITE_MASK;
else
val &= ~DDC_SDA_WRITE_MASK;
nv_card->PCIO[0x3d4] = DDCBase + 1;
nv_card->PCIO[0x3d5] = val | 0x1;
}
I2CBusPtr NV_I2CCreateBusPtr(char *name, int bus)
{
I2CBusPtr I2CPtr;
I2CPtr = xf86CreateI2CBusRec();
if(!I2CPtr) return NULL;
I2CPtr->BusName = name;
I2CPtr->scrnIndex = nv_card->number; /* We need to use unique indices or else it can lead to a segfault in multicard situations */
I2CPtr->I2CAddress = I2CAddress;
I2CPtr->I2CPutBits = NV_I2CPutBits;
I2CPtr->I2CGetBits = NV_I2CGetBits;
I2CPtr->AcknTimeout = 5;
I2CPtr->DriverPrivate.val = bus;
if (!xf86I2CBusInit(I2CPtr))
{
return 0;
}
return I2CPtr;
}
static void ProbeDevice (I2CBusPtr bus, I2CSlaveAddr addr, char *format, ...)
{
I2CDevPtr dev;
char *s;
va_list ap;
if(xf86I2CProbeAddress(bus, addr))
{
dev = xf86CreateI2CDevRec();
s = (char*)malloc(8);
va_start (ap, format);
vsnprintf (s, 7, format, ap);
va_end (ap);
dev->DevName = s;
dev->SlaveAddr = addr;
dev->pI2CBus = bus;
if (!xf86I2CDevInit(dev))
{
free(dev->DevName);
xf86DestroyI2CDevRec(dev, TRUE);
}
}
}
static void I2CProbeAllDevices (I2CBusPtr busses[], int nbus)
{
I2CSlaveAddr addr;
int bus;
for (bus = 0; bus < nbus; bus++)
{
for (addr = 0x00; addr < 0x100; addr += 2)
{
ProbeDevice (busses[bus], addr, "%1i:%02X", bus, addr);
}
}
}
static I2CDevPtr I2cProbeDevices(I2CBusPtr busses[], int num_busses)
{
int bus;
I2CDevPtr dev;
/* Unlock the extended CRTC registers to get i2c working */
NVLockUnlock(0);
/* On NV40 cards the i2c busses can be disabled */
if(nv_card->arch & NV4X)
{
nv_card->PCIO[0x3d4] = 0x49;
nv_card->PCIO[0x3d5] |= 0x4; /* Unlock the i2c busses */
}
I2CProbeAllDevices(busses, num_busses);
if(nv_card->debug)
printf("Probing I2C busses\n");
for(bus = 0; bus < num_busses; bus++)
{
for(dev = busses[bus]->FirstDev; dev; dev = dev->NextDev)
{
if(nv_card->debug)
printf("bus: %x device: %x\n", bus, dev->SlaveAddr);
dev->arch = nv_card->arch;
switch(dev->SlaveAddr)
{
/* LM99 */
case 0x98:
if(lm99_detect(dev))
return dev;
break;
case 0x5a:
if(w83l785r_detect(dev))
return dev;
if(w83781d_detect(dev))
return dev;
case 0x5c:
if(f75375_detect(dev))
return dev;
if(adt7473_detect(dev))
return dev;
case 0x6e: /* DDC/CI ? */
/* The addresses below oftenly appear on most cards but what are these? */
case 0x70:
case 0xa0:
case 0xa2:
case 0xa4:
case 0xa6:
case 0xa8:
case 0xaa:
case 0xac:
case 0xae:
break;
default:
/* Unknown device */
break;
}
}
}
NVLockUnlock(1);
return NULL;
}
void i2c_sensor_init(void)
{
nv_card->sensor = I2cProbeDevices(nv_card->busses, nv_card->num_busses);
/* When a sensor is available, enable the correct function pointers */
if(nv_card->sensor)
{
nv_card->sensor_name = nv_card->sensor->chip_name;
switch(nv_card->sensor->chip_id)
{
case LM99:
case MAX6559:
nv_card->caps |= BOARD_TEMP_MONITORING | GPU_TEMP_MONITORING;
nv_card->get_board_temp = lm99_get_board_temp;
nv_card->get_gpu_temp = lm99_get_gpu_temp;
break;
case F75375:
nv_card->caps |= BOARD_TEMP_MONITORING | GPU_TEMP_MONITORING | I2C_FANSPEED_MONITORING;
nv_card->get_board_temp = f75375_get_board_temp;
nv_card->get_gpu_temp = f75375_get_gpu_temp;
nv_card->get_i2c_fanspeed_rpm = f75375_get_fanspeed_rpm;
nv_card->get_i2c_fanspeed_pwm = f75375_get_fanspeed_pwm;
nv_card->set_i2c_fanspeed_pwm = f75375_set_fanspeed_pwm;
break;
case W83781D:
nv_card->caps |= BOARD_TEMP_MONITORING | GPU_TEMP_MONITORING | I2C_FANSPEED_MONITORING;
nv_card->get_board_temp = w83781d_get_board_temp;
nv_card->get_gpu_temp = w83781d_get_gpu_temp;
nv_card->get_i2c_fanspeed_rpm = w83781d_get_fanspeed_rpm;
nv_card->get_i2c_fanspeed_pwm = w83781d_get_fanspeed_pwm;
nv_card->set_i2c_fanspeed_pwm = w83781d_set_fanspeed_pwm;
break;
case W83L785R:
nv_card->caps |= BOARD_TEMP_MONITORING | GPU_TEMP_MONITORING | I2C_FANSPEED_MONITORING;
nv_card->get_board_temp = w83l785r_get_board_temp;
nv_card->get_gpu_temp = w83l785r_get_gpu_temp;
nv_card->get_i2c_fanspeed_rpm = w83l785r_get_fanspeed_rpm;
nv_card->get_i2c_fanspeed_pwm = w83l785r_get_fanspeed_pwm;
nv_card->set_i2c_fanspeed_pwm = w83l785r_set_fanspeed_pwm;
break;
case ADT7473:
nv_card->caps |= BOARD_TEMP_MONITORING | GPU_TEMP_MONITORING | I2C_FANSPEED_MONITORING | I2C_AUTOMATIC_FANSPEED_CONTROL;
nv_card->get_board_temp = adt7473_get_board_temp;
nv_card->get_gpu_temp = adt7473_get_gpu_temp;
nv_card->get_i2c_fanspeed_mode = adt7473_get_fanspeed_mode;
nv_card->set_i2c_fanspeed_mode = adt7473_set_fanspeed_mode;
nv_card->get_i2c_fanspeed_rpm = adt7473_get_fanspeed_rpm;
nv_card->get_i2c_fanspeed_pwm = adt7473_get_fanspeed_pwm;
nv_card->set_i2c_fanspeed_pwm = adt7473_set_fanspeed_pwm;
}
}
else
{
nv_card->sensor = NULL;
}
}

76
nvclock/i2c.h Normal file
View File

@@ -0,0 +1,76 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2005 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
*/
#ifndef I2C_H
#define I2C_H
#include "xf86i2c.h"
#define LM99 0x1
#define MAX6559 0x2
#define F75375 0x4
#define W83L785R 0x8
#define W83781D 0x16
#define ADT7473 0x32
Bool I2CAddress(I2CDevPtr d, I2CSlaveAddr addr);
I2CBusPtr NV_I2CCreateBusPtr(char *name, int bus);
/* ADT7473 */
int adt7473_detect(I2CDevPtr dev);
int adt7473_get_board_temp(I2CDevPtr dev);
int adt7473_get_gpu_temp(I2CDevPtr dev);
int adt7473_get_fanspeed_rpm(I2CDevPtr dev);
float adt7473_get_fanspeed_pwm(I2CDevPtr dev);
int adt7473_set_fanspeed_pwm(I2CDevPtr dev, float speed);
int adt7473_get_fanspeed_mode(I2CDevPtr dev);
void adt7473_set_fanspeed_mode(I2CDevPtr dev, int mode);
/* LM99 */
int lm99_detect(I2CDevPtr dev);
int lm99_get_board_temp(I2CDevPtr dev);
int lm99_get_gpu_temp(I2CDevPtr dev);
/* Fintek F75375 */
int f75375_detect(I2CDevPtr dev);
int f75375_get_gpu_temp(I2CDevPtr dev);
int f75375_get_board_temp(I2CDevPtr dev);
int f75375_get_fanspeed_rpm(I2CDevPtr dev);
int f75375_set_fanspeed_rpm(I2CDevPtr dev, int desired_rpm);
float f75375_get_fanspeed_pwm(I2CDevPtr dev);
int f75375_set_fanspeed_pwm(I2CDevPtr dev, float speed);
/* Winbond W83781D */
int w83781d_detect(I2CDevPtr dev);
int w83781d_get_board_temp(I2CDevPtr dev);
int w83781d_get_gpu_temp(I2CDevPtr dev);
int w83781d_get_fanspeed_rpm(I2CDevPtr dev);
float w83781d_get_fanspeed_pwm(I2CDevPtr dev);
int w83781d_set_fanspeed_pwm(I2CDevPtr dev, float speed);
/* Winbond W83L785R */
int w83l785r_detect(I2CDevPtr dev);
int w83l785r_get_board_temp(I2CDevPtr dev);
int w83l785r_get_gpu_temp(I2CDevPtr dev);
int w83l785r_get_fanspeed_rpm(I2CDevPtr dev);
float w83l785r_get_fanspeed_pwm(I2CDevPtr dev);
int w83l785r_set_fanspeed_pwm(I2CDevPtr dev, float speed);
#endif /* I2C_H */

BIN
nvclock/i2c.o Normal file

Binary file not shown.

1161
nvclock/info.c Normal file

File diff suppressed because it is too large Load Diff

BIN
nvclock/info.o Normal file

Binary file not shown.

BIN
nvclock/libbackend.a Normal file

Binary file not shown.

67
nvclock/libc_wrapper.c Normal file
View File

@@ -0,0 +1,67 @@
/* Excerpt from: */
/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/libc_wrapper.c,v 1.70 2000/09/26 15:57:20 tsi Exp $ */
/*
* Copyright 1997 by The XFree86 Project, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the names of Orest Zborowski and David Wexelblat
* not be used in advertising or publicity pertaining to distribution of
* the software without specific, written prior permission. Orest Zborowski
* and David Wexelblat make no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* THE XFREE86 PROJECT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL OREST ZBOROWSKI OR DAVID WEXELBLAT BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <stdio.h>
#include "xfree.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/time.h>
/* Misc functions. Some are ANSI C, some are not. */
/* FIXME might use nanosleep (POSIX) here. */
/* resolution of 1/HZ s (i.e, 10 ms on Linux/i386 and 1 ms on Linux/Alpha). */
/* If the process is scheduled under a real-time policy like
SCHED_FIFO or SCHED_RR, then pauses of up to 2 ms will be
performed as busy waits with microsecond precision.
*/
void
xf86usleep(usec)
unsigned long usec;
{
#if (defined(SYSV) || defined(SVR4)) && !defined(sun)
syscall(3112, (usec) / 1000 + 1);
#else
usleep(usec);
#endif
}
void xf86getsecs(long * secs, long * usecs)
{
struct timeval tv;
gettimeofday(&tv, NULL);
*secs = tv.tv_sec;
*usecs= tv.tv_usec;
return;
}

BIN
nvclock/libc_wrapper.o Normal file

Binary file not shown.

99
nvclock/lm99.c Normal file
View File

@@ -0,0 +1,99 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2005 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
*
* LM99 hardware monitoring
*/
#include <string.h>
#include "i2c.h"
#include "nvclock.h"
/* various defines for register offsets and such are needed */
#define LM99_REG_LOCAL_TEMP 0x0
#define LM99_REG_REMOTE_TEMP 0x1
#define LM99_REG_MAN_ID 0xfe
#define NATSEM_MAN_ID 0x1
#define MAXIM_MAN_ID 0x4d
#define LM99_REG_CHIP_ID 0xff
/* This function should return the chip type .. */
int lm99_detect(I2CDevPtr dev)
{
I2CByte man_id, chip_id;
xf86I2CReadByte (dev, LM99_REG_MAN_ID, &man_id);
xf86I2CReadByte (dev, LM99_REG_CHIP_ID, &chip_id);
switch(man_id)
{
/* National Semiconductor LM99; needs offset? */
case NATSEM_MAN_ID:
dev->chip_id = LM99;
dev->chip_name = (char*)strdup("National Semiconductor LM99");
break;
/* Unknown vendor; this chip was used in a FX5700Go laptop and looks similar to the MAx6659 */
case 0x47:
/* Maxim; likely a 655x model */
case MAXIM_MAN_ID:
dev->chip_id = MAX6559;
dev->chip_name = (char*)strdup("Maxim MAX6659");
break;
default:
return 0;
}
return 1;
}
int lm99_get_board_temp(I2CDevPtr dev)
{
I2CByte temp;
xf86I2CReadByte(dev, LM99_REG_LOCAL_TEMP, &temp);
return temp;
}
int lm99_get_gpu_temp(I2CDevPtr dev)
{
I2CByte temp;
xf86I2CReadByte(dev, LM99_REG_REMOTE_TEMP, &temp);
/* Cards with lm99 chips need an offset of 16C according to the datasheets.
/ Further an extra offset of 10C seems to be needed on Geforce6800 cards to match nvidia-settings.
/ Last but not least Geforce6600GT boards containing an LM99 sensor seem to need a total offset of 21C.
*/
if(dev->chip_id == LM99)
{
if(dev->arch == NV43)
temp += 21;
else if(dev->arch & NV4X)
temp += 26;
else
temp += 16;
}
/* Geforce6 boards need an extra offset of +10C on both LM99 and MAX6659 chipsets.
/ The code below is only executed for MAX6659 as we already handle the extra 10C above.
*/
else if(dev->arch & NV4X)
temp += 10;
return temp;
}

BIN
nvclock/lm99.o Normal file

Binary file not shown.

426
nvclock/nv30.c Normal file
View 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);
}
}

BIN
nvclock/nv30.o Normal file

Binary file not shown.

1099
nvclock/nv40.c Normal file

File diff suppressed because it is too large Load Diff

BIN
nvclock/nv40.o Normal file

Binary file not shown.

321
nvclock/nv50.c Normal file
View File

@@ -0,0 +1,321 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 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 <string.h>
#include "backend.h"
static void nv50_i2c_get_bits(I2CBusPtr bus, int *clock, int *data)
{
const int offset = bus->DriverPrivate.val;
unsigned char val;
val = nv_card->PMC[(0x0000E138 + offset)/4];
*clock = !!(val & 1);
*data = !!(val & 2);
}
static void nv50_i2c_put_bits(I2CBusPtr bus, int clock, int data)
{
const int offset = bus->DriverPrivate.val;
nv_card->PMC[(0x0000E138 + offset)/4] = 4 | clock | data << 1;
}
static I2CBusPtr nv50_i2c_create_bus_ptr(char *name, int bus)
{
I2CBusPtr I2CPtr;
I2CPtr = xf86CreateI2CBusRec();
if(!I2CPtr) return NULL;
I2CPtr->BusName = name;
I2CPtr->scrnIndex = nv_card->number; /* We need to use unique indices or else it can lead to a segfault in multicard situations */
I2CPtr->I2CAddress = I2CAddress;
I2CPtr->I2CPutBits = nv50_i2c_put_bits;
I2CPtr->I2CGetBits = nv50_i2c_get_bits;
I2CPtr->AcknTimeout = 40;
I2CPtr->DriverPrivate.val = bus;
if (!xf86I2CBusInit(I2CPtr))
{
return 0;
}
return I2CPtr;
}
static int nv50_get_default_mask(char *smask, char *rmask)
{
int mask;
switch(nv_card->arch)
{
case NV50:
mask = 0x3f00ff;
break;
case G84:
mask = 0x030003;
break;
case G86:
mask = 0x010001;
break;
}
if(smask)
*smask = mask & 0xff;
if(rmask)
*rmask = (mask >> 16) & 0xff;
return mask;
}
/* Receive the number of enabled stream processors and also
/ store a mask with active pipelines.
*/
static int nv50_get_stream_units(char *mask, char *default_mask)
{
int i, stream_units=0;
unsigned char stream_units_cfg = nv_card->PMC[0x1540/4] & 0xff;
/* The number of shaders is again stored in 0x1540
bit7-0: number of unified pixel shaders in blocks of 16
bit23-16: number of ROP units in blocks of 4
bit31-24: what's in here?
*/
for(i=0; i<8; i++)
if((stream_units_cfg >> i) & 0x1)
stream_units++;
nv50_get_default_mask(default_mask, 0);
*mask = stream_units_cfg;
return (stream_units << 4); /* stream units are stored in blocks of 16 */
}
/* Receive the number of enabled ROP units and also
/ store a mask with active units.
*/
static int nv50_get_rop_units(char *mask, char *default_mask)
{
int i, rop_units=0;
unsigned char rop_units_cfg = (nv_card->PMC[0x1540/4] >> 16) & 0xff;
for(i=0; i<8; i++)
if((rop_units_cfg >> i) & 0x1)
rop_units++;
nv50_get_default_mask(0, default_mask);
*mask = rop_units_cfg;
return (rop_units << 2); /* rop units are stored in blocks of 4 */
}
/* Reading of the internal gpu sensor, it not entirely correct yet */
static int nv50_get_gpu_temp(void *sensor)
{
int temp;
int correction=0;
float offset;
float slope;
/* For now use a hardcoded offset and gain. This isn't correct but I don't know how the new temperture table works yet; this at least gives something */
offset = -227.0;
slope = 430.0/10000.0;
temp = nv_card->PMC[0x20008/4] & 0x1fff;
return (int)(temp * slope + offset) + correction;
}
static int CalcSpeed_nv50(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_nv50(int base_freq, unsigned int pll, unsigned int pll2)
{
int m1, m2, n1, n2, p;
p = (pll >> 16) & 0x03;
m1 = pll2 & 0xFF;
n1 = (pll2 >> 8) & 0xFF;
/* This is always 1 for NV5x? */
m2 = 1;
n2 = 1;
if(nv_card->debug)
printf("m1=%d m2=%d n1=%d n2=%d p=%d\n", m1, m2, n1, n2, p);
/* The clocks need to be multiplied by 4 for some reason. Is this 4 stored in 0x4000/0x4004? */
return (float)4*CalcSpeed_nv50(base_freq, m1, m2, n1, n2, p)/1000;
}
static float nv50_get_gpu_speed()
{
int pll = nv_card->PMC[0x4028/4];
int pll2 = nv_card->PMC[0x402c/4];
if(nv_card->debug == 1)
{
printf("NVPLL_COEFF=%08x\n", pll);
printf("NVPLL2_COEFF=%08x\n", pll2);
}
return (float)GetClock_nv50(nv_card->base_freq, pll, pll2);
}
static void nv50_set_gpu_speed(unsigned int nvclk)
{
}
static float nv50_get_memory_speed()
{
/* The memory clock appears to be in 0x4008/0x400c, 0x4010/0x4014 and 0x4018/0x401c but the second and third set aren't always set to the same values as 0x4008/0x400c */
int pll = nv_card->PMC[0x4008/4];
int pll2 = nv_card->PMC[0x400c/4];
if(nv_card->debug == 1)
{
printf("MPLL_COEFF=%08x\n", pll);
printf("MPLL2_COEFF=%08x\n", pll2);
}
return (float)GetClock_nv50(nv_card->base_freq, pll, pll2);
}
static void nv50_set_memory_speed(unsigned int memclk)
{
printf("blaat: %d %p %x\n", memclk, nvclock.dpy, nv_card->state);
}
static float nv50_get_shader_speed()
{
int pll = nv_card->PMC[0x4020/4];
int pll2 = nv_card->PMC[0x4024/4];
if(nv_card->debug == 1)
{
printf("SPLL_COEFF=%08x\n", pll);
printf("SPLL2_COEFF=%08x\n", pll2);
}
return (float)GetClock_nv50(nv_card->base_freq, pll, pll2);
}
static void nv50_set_shader_speed(unsigned int clk)
{
}
static void nv50_reset_gpu_speed()
{
}
static void nv50_reset_memory_speed()
{
}
static void nv50_reset_shader_speed()
{
}
static void nv50_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;
nv_card->get_gpu_speed = nv50_get_gpu_speed;
nv_card->get_memory_speed = nv50_get_memory_speed;
nv_card->set_memory_speed = nv50_set_memory_speed;
nv_card->set_gpu_speed = nv50_set_gpu_speed;
nv_card->reset_gpu_speed = nv50_reset_gpu_speed;
nv_card->reset_memory_speed = nv50_reset_memory_speed;
}
nv_card->get_shader_speed = nv50_get_shader_speed;
nv_card->set_shader_speed = nv50_set_shader_speed;
nv_card->reset_shader_speed = nv50_reset_shader_speed;
}
void nv50_init(void)
{
nv_card->base_freq = 27000;
nv_card->set_state = nv50_set_state;
nv_card->set_state(STATE_LOWLEVEL); /* Set the clock function pointers */
nv_card->get_stream_units = nv50_get_stream_units;
nv_card->get_rop_units = nv50_get_rop_units;
/* Initialize the NV50 I2C busses; compared to older hardware they are located at different register addresses */
if(nv_card->busses[0] == NULL)
{
nv_card->num_busses = 4;
nv_card->busses[0] = nv50_i2c_create_bus_ptr("BUS0", 0x0);
nv_card->busses[1] = nv50_i2c_create_bus_ptr("BUS1", 0x18);
nv_card->busses[2] = nv50_i2c_create_bus_ptr("BUS2", 0x30);
nv_card->busses[3] = nv50_i2c_create_bus_ptr("BUS3", 0x48);
i2c_sensor_init();
}
/* Temperature monitoring; all NV50 cards feature an internal temperature sensor
/ but only use it when there is no I2C sensor around.
*/
if(!(nv_card->caps & GPU_TEMP_MONITORING))
{
nv_card->caps |= GPU_TEMP_MONITORING;
nv_card->sensor_name = (char*)strdup("GPU Internal Sensor");
nv_card->get_gpu_temp = (int(*)(I2CDevPtr))nv50_get_gpu_temp;
}
/* 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);
#if 0
/* Set the speed range */
if(nv_card->bios)
{
}
else
#endif
{
float memclk = GetClock_nv50(nv_card->base_freq, nv_card->mpll, nv_card->mpll2);
float nvclk = GetClock_nv50(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);
}
}

BIN
nvclock/nv50.o Normal file

Binary file not shown.

341
nvclock/nvclock.h Normal file
View File

@@ -0,0 +1,341 @@
/* 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
*/
#ifndef NVCLOCK_H
#define NVCLOCK_H
#define MAX_CARDS 4
#define NV5 (1<<0)
#define NV10 (1<<1)
#define NV17 (1<<2)
#define NV1X (NV10 | NV17)
#define NV20 (1<<3)
#define NV25 (1<<4)
#define NV2X (NV20 | NV25)
#define NV30 (1<<5)
#define NV31 (1<<6)
#define NV35 (1<<7)
#define NV3X (NV30 | NV31 | NV35)
#define NV40 (1<<8)
#define NV41 (1<<9)
#define NV43 (1<<10)
#define NV44 (1<<11)
#define NV46 (1<<12)
#define NV47 (1<<13)
#define NV49 (1<<14)
#define NV4B (1<<15)
#define C51 (1<<16)
#define NV4X (NV40 | NV41 | NV43 | NV44 | NV46 | NV47 | NV49 | NV4B | C51)
#define NV50 (1<<17)
#define G84 (1<<18)
#define G86 (1<<19)
#define NV5X (NV50 | G84 | G86)
#define NV_ERR_NO_DEVICES_FOUND 1
#define NV_ERR_NO_DRIVERS_FOUND 2
#define NV_ERR_NOT_ENOUGH_PERMISSIONS 3
#define NV_ERR_OTHER 4
#define GPU_OVERCLOCKING (1<<0)
#define MEM_OVERCLOCKING (1<<1)
#define COOLBITS_OVERCLOCKING (1<<2)
#define PIPELINE_MODDING (1<<3)
#define GPU_FANSPEED_MONITORING (1<<4) /* Fanspeed monitoring based on fan voltage */
#define BOARD_TEMP_MONITORING (1<<5) /* Board temperature; not available using NVSensor */
#define GPU_TEMP_MONITORING (1<<6) /* Internal GPU temperature */
#define I2C_FANSPEED_MONITORING (1<<7) /* Fanspeed monitoring using a i2c sensor chip */
#define I2C_AUTOMATIC_FANSPEED_CONTROL (1<<8) /* The sensor supports automatic fanspeed control */
#define SMARTDIMMER (1<<9) /* Smartdimmer support for mobile GPUs */
#define GPU_ID_MODDING (1<<10) /* PCI id modding is supported on this board */
#define STATE_LOWLEVEL (1<<0)
#define STATE_2D (1<<1)
#define STATE_3D (1<<2)
#define STATE_BOTH (STATE_2D | STATE_3D)
/* Define some i2c types, so that we don't depend on additional headers when using NVClock as a library */
#ifndef _XF86I2C_H
typedef void* I2CBusPtr;
typedef void* I2CDevPtr;
#endif
typedef struct
{
void *next;
char *section;
char *name;
unsigned int value;
} cfg_entry;
typedef enum
{
SDR,
DDR
} mem_type;
typedef enum
{
UNKNOWN,
DESKTOP,
NFORCE,
MOBILE
} gpu_type;
struct pci_ids {
short id;
const char *name;
gpu_type gpu;
};
struct voltage
{
unsigned char VID;
float voltage;
};
struct performance
{
int nvclk;
int delta;
int memclk;
int shaderclk;
int fanspeed;
float voltage;
};
struct vco
{
unsigned int minInputFreq, maxInputFreq;
unsigned int minFreq, maxFreq;
unsigned char minN, maxN;
unsigned char minM, maxM;
};
struct pll
{
unsigned int reg;
unsigned char var1d;
unsigned char var1e;
struct vco VCO1;
struct vco VCO2;
};
struct sensor
{
int slope_div;
int slope_mult;
int diode_offset_div;
int diode_offset_mult;
int temp_correction;
};
struct nvbios
{
char *signon_msg;
char *vendor_name;
unsigned short device_id;
char* version;
unsigned char major;
unsigned char minor;
short volt_entries;
short volt_mask;
struct voltage volt_lst[4];
short perf_entries;
struct performance perf_lst[3];
short pll_entries;
struct pll pll_lst[8];
struct sensor sensor_cfg;
/* Cache the 'empty' PLLs, this is needed for PLL calculation */
unsigned int mpll;
unsigned int nvpll;
unsigned int spll;
unsigned int pipe_cfg; /* Used to cache the NV4x pipe_cfg register */
};
typedef struct {
char *card_name; /* Name of the card */
short number; /* internal card number; used by the gtk client and set_card to see if we really need to switch cards */
short caps; /* A bitmask that contains what card features are supported; A normal gpu can do gpu/memory overclocking but a nforce can do only gpu. */
short device_id;
int arch; /* Architecture NV10, NV15, NV20 ..; for internal use only as we don't list all architectures */
unsigned int reg_address;
char *dev_name; /* /dev/mem or /dev/nvidiaX */
unsigned short devbusfn;
int irq; /* We might need the IRQ to sync NV-CONTROL info with nvclock */
short base_freq;
gpu_type gpu; /* Tells what type of gpu is used: mobile, nforce .. */
short debug; /* Enable/Disable debug information */
struct nvbios *bios; /* pointer to bios information */
int have_coolbits; /* Tells if Coolbits (NV-CONTROL) is enabled */
int state; /* Tells which clocks to change for NV-CONTROL: 2D, 3D or BOTH */
/* card registers */
int mem_mapped; /* Check for set_card to see if the memory has been mapped or not. */
volatile unsigned int *PFB;
volatile unsigned int *PBUS;
volatile unsigned int *PMC;
volatile unsigned int *PRAMDAC;
volatile unsigned int *PEXTDEV;
volatile unsigned char *PROM; /* Nvidia bios */
volatile unsigned char *PCIO;
/* Overclock range of speeds */
short nvclk_min;
short nvclk_max;
short memclk_min;
short memclk_max;
/* Various GeforceFX/Geforce6 boards use different clocks in 3d. We need to store those clocks */
short nvclk_3d;
short memclk_3d;
/* Card info */
short (*get_gpu_architecture)();
short (*get_gpu_revision)();
int (*set_gpu_pci_id)(short id); /* Changes the least significant 2 or 4 bits of the pci id */
/* Memory info */
int mem_type; /* needs to replace memory_type ?? */
char* (*get_memory_type)(); /* Memory type: SDR/DDR */
short (*get_memory_width)(); /* Memory width 64bit or 128bit */
short (*get_memory_size)(); /* Amount of memory between 4 and 128 MB */
/* BUS info: PCI/PCI-Express/AGP */
char* (*get_bus_type)(); /* Bus type: AGP/PCI/PCI-Express */
short (*get_bus_rate)(); /* Current AGP rate: 1, 2, 4 or 8; PCI-Express: 1-32X*/
/* AGP */
char* (*get_agp_status)(); /* Current AGP status: Enabled/Disabled */
char* (*get_agp_fw_status)(); /* Current FW status: Enabled/Disabled */
char* (*get_agp_sba_status)(); /* Current SBA status: Enabled/Disabled */
char* (*get_agp_supported_rates)(); /* Supported AGP rates */
/* PCI-Express */
short (*get_pcie_max_bus_rate)(); /* Get the maximum PCI-E busrate */
/* Hardware monitoring */
short num_busses; /* Number of available i2c busses */
I2CBusPtr busses[3]; /* I2C busses on the videocard; this bus is needed for communication with sensor chips */
I2CDevPtr sensor; /* When a sensor chip is available, this device pointer can be used to access it */
char *sensor_name; /* Name of the sensor; although sensor contains the name too, we add sensor_name because of the builtin temperature sensor used on various NV4x cards */
int (*get_board_temp)(I2CDevPtr dev); /* Temperature of the sensor chip or for example the ram chips */
int (*get_gpu_temp)(I2CDevPtr dev); /* Internal gpu temperature */
float (*get_fanspeed)(); /* Get the speed in % from the pwm register in %*/
void (*set_fanspeed)(float speed); /* Set the speed of the fan using the pwm register of the gpu */
int (*get_i2c_fanspeed_mode)(I2CDevPtr dev); /* Advanced sensors like the ADT7473 support manual and automatic fanspeed adjustments */
void (*set_i2c_fanspeed_mode)(I2CDevPtr dev, int mode); /* Set the fanspeed mode to manual or automatic; Note that a pwm fanspeed change already causes a switch to implicit switch to manual, so this function should only be used to deactivate manual mode */
int (*get_i2c_fanspeed_rpm)(I2CDevPtr dev); /* Speed of the fan in rpm */
float (*get_i2c_fanspeed_pwm)(I2CDevPtr dev); /* Duty cycle of the pwm signal that controls the fan */
int (*set_i2c_fanspeed_pwm)(I2CDevPtr dev, float speed); /* By adjusting the duty cycle of the pwm signal, the fanspeed can be adjusted. */
/* Pipeline stuff for NV4x; On various Geforce6 boards disabled pixel/vertex pipelines can be re-enabled. */
int (*get_default_mask)(char *pmask, char *vmask);
int (*get_hw_masked_units)(char *pmask, char *vmask);
int (*get_sw_masked_units)(char *pmask, char *vmask);
int (*get_pixel_pipelines)(char *mask, int *total);
void (*set_pixel_pipelines)(unsigned char mask);
int (*get_vertex_pipelines)(char *mask);
void (*set_vertex_pipelines)(unsigned char mask);
/* NV5x (Geforce8) shader stuff */
float (*get_shader_speed)(); /* NV5X-only */
void (*set_shader_speed)(unsigned int clk); /* NV5X-only */
void (*reset_shader_speed)();
int (*get_stream_units)(char *mask, char *default_mask);
int (*get_rop_units)(char *mask, char *default_mask);
/* Smartdimmer (adjustment of the brigthenss of the backlight on Laptops) */
int (*get_smartdimmer)();
void (*set_smartdimmer)(int level);
/* Overclocking */
volatile unsigned int mpll; /* default memory speed */
volatile unsigned int mpll2;
volatile unsigned int nvpll; /* default gpu speed */
volatile unsigned int nvpll2;
void (*set_state)(int state);
float (*get_gpu_speed)();
void (*set_gpu_speed)(unsigned int nvclk);
float (*get_memory_speed)();
void (*set_memory_speed)(unsigned int memclk);
void (*reset_gpu_speed)();
void (*reset_memory_speed)();
/* Debug */
void (*get_debug_info)();
} NVCard, *NVCardPtr;
typedef struct
{
int num_cards;
NVCard card[MAX_CARDS];
cfg_entry *cfg;
void *dpy; /* X display needed for the NV-CONTROL backend */
char *path; /* path to home directory */
int nv_errno;
char *nv_err_str;
} NVClock;
extern NVClock nvclock;
extern NVCard* nv_card;
#ifdef __cplusplus
extern "C" {
#endif
int init_nvclock();
int set_card(int number);
void unset_card();
/* config file stuff */
int open_config();
int create_config(char *file);
int read_config(cfg_entry **cfg, char *file);
int parse_config(char *file);
void write_config(cfg_entry *cfg, char *file);
void add_entry(cfg_entry **cfg, char *section, char *name, int value);
void change_entry(cfg_entry **cfg, char *section, char *name, int value);
cfg_entry* lookup_entry(cfg_entry **cfg, char *section, char *name);
void destroy(cfg_entry **cfg);
/* error handling */
char *get_error(char *buf, int size);
void set_error(int code);
void set_error_str(const char *err);
/* utility functions */
int convert_gpu_architecture(short arch, char *buf);
void convert_unit_mask_to_binary(char mask, char hw_default, char *buf);
#ifdef __cplusplus
};
#endif
#endif /* NVCLOCK_H */

164
nvclock/nvcontrol.c Normal file
View File

@@ -0,0 +1,164 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2004 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 "libnvcontrol.h"
#include "backend.h"
static void nvcontrol_toggle_overclocking(int toggle)
{
int state;
if(NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_OVERCLOCKING_STATE, &state))
{
if(state != toggle)
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_OVERCLOCKING_STATE, toggle);
}
}
float nvcontrol_get_gpu_speed()
{
int clocks, nvclk;
if(nv_card->state & STATE_3D)
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_3D_CLOCK, &clocks);
else
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_2D_CLOCK, &clocks);
nvclk = (clocks >> 16) & 0xffff;
return (float)nvclk;
}
void nvcontrol_set_gpu_speed(unsigned int nvclk)
{
int clocks, memclk;
/* The nvcontrol commands change both clocks at the same time.
/ In order to only reset the gpu clock, we need to set the
/ same memory clock again.
*/
memclk = (int)nvcontrol_get_memory_speed();
clocks = (nvclk << 16) + memclk;
nvcontrol_toggle_overclocking(1); /* Make sure overclocking is on or else we get X failures */
/* The 2d clock is set in the nv_card->states 2d and both; the same for 3d */
if(nv_card->state & STATE_2D)
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_2D_CLOCK, clocks);
if(nv_card->state & STATE_3D)
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_3D_CLOCK, clocks);
}
float nvcontrol_get_memory_speed()
{
int clocks, memclk;
/* In nv_card->state 3d and both, we return the 3d clocks */
if(nv_card->state & STATE_3D)
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_3D_CLOCK, &clocks);
else
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_2D_CLOCK, &clocks);
memclk = clocks & 0xffff;
return (float)memclk;
}
void nvcontrol_set_memory_speed(unsigned int memclk)
{
int clocks, nvclk;
/* The nvcontrol commands change both clocks at the same time.
/ In order to only reset the gpu clock, we need to set the
/ same memory clock again.
*/
nvclk = (int)nvcontrol_get_gpu_speed();
clocks = (nvclk << 16) + memclk;
nvcontrol_toggle_overclocking(1); /* Make sure overclocking is on or else we get X failures */
/* The 2d clock is set in the nv_card->states 2d and both; the same for 3d */
if(nv_card->state & STATE_2D)
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_2D_CLOCK, clocks);
if(nv_card->state & STATE_3D)
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_3D_CLOCK, clocks);
}
void nvcontrol_reset_gpu_speed()
{
int clocks, memclk, nvclk;
nvcontrol_toggle_overclocking(1); /* Make sure overclocking is on or else we get X failures */
if(nv_card->state & STATE_2D)
{
/* We need to set the current memory clock again as we can't change just one clock */
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_2D_CLOCK, &clocks);
memclk = clocks & 0xffff;
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_DEFAULT_2D_CLOCK, &clocks);
nvclk = (clocks >> 16) & 0xffff;
clocks = (nvclk << 16) + memclk;
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_2D_CLOCK, clocks);
}
if(nv_card->state & STATE_3D)
{
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_3D_CLOCK, &clocks);
memclk = clocks & 0xffff;
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_DEFAULT_3D_CLOCK, &clocks);
nvclk = (clocks >> 16) & 0xffff;
clocks = (nvclk << 16) + memclk;
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_3D_CLOCK, clocks);
}
}
void nvcontrol_reset_memory_speed()
{
int clocks, memclk, nvclk;
nvcontrol_toggle_overclocking(1); /* Make sure overclocking is on or else we get X failures */
if(nv_card->state & STATE_2D)
{
/* We need to set the current gpu clock again as we can't change just one clock */
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_2D_CLOCK, &clocks);
nvclk = (clocks >> 16) & 0xffff;
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_DEFAULT_2D_CLOCK, &clocks);
memclk = clocks & 0xffff;
clocks = (nvclk << 16) + memclk;
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_2D_CLOCK, clocks);
}
if(nv_card->state & STATE_3D)
{
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_3D_CLOCK, &clocks);
nvclk = (clocks >> 16) & 0xffff;
NVGetAttribute(nvclock.dpy, 0, 0, NV_GPU_DEFAULT_3D_CLOCK, &clocks);
memclk = clocks & 0xffff;
clocks = (nvclk << 16) + memclk;
NVSetAttribute(nvclock.dpy, 0, 0, NV_GPU_3D_CLOCK, clocks);
}
}

81
nvclock/nvreg.h Normal file
View File

@@ -0,0 +1,81 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* Copyright(C) 2001-2007 Roderick Colenbrander
*
* site: http://nvclock.sourceforge.net
*
* 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
*/
/* PCI stuff */
#define PCI_VENDOR_ID 0x0 /* 16-bit */
#define PCI_DEVICE_ID 0x2 /* 16-bit */
#define PCI_CAPABILITY_LIST 0x34
#define PCI_CAP_LIST_ID 0x0
#define PCI_CAP_LIST_NEXT 0x1
#define PCI_CAP_ID_AGP 0x2 /* AGP */
#define PCI_AGP_STATUS 0x4
# define PCI_AGP_STATUS_SBA 0x200 /* Sideband Addressing */
# define PCI_AGP_STATUS_64BIT 0x20
# define PCI_AGP_STATUS_FW 0x10 /* Fast Writes */
# define PCI_AGP_STATUS_RATE_8X_SUPPORT 0x8 /* If set AGP1x/AGP2x need to be interpreted as AGP4x/AGP8x */
# define PCI_AGP_STATUS_RATE_8X_SHIFT 0x2 /* Needs to be used when 8x support is enabled to translate 1x/ -> 4x/8x*/
# define PCI_AGP_STATUS_RATE_4X 0x4
# define PCI_AGP_STATUS_RATE_2X 0x2
# define PCI_AGP_STATUS_RATE_1X 0x1
# define PCI_AGP_STATUS_RATE_MASK 0x7 /* AGP4X | AGP2X | AGP1X */
#define PCI_AGP_COMMAND 0x8
# define PCI_AGP_COMMAND_SBA 0x200 /* Sideband Addressing */
# define PCI_AGP_COMMAND_AGP 0x100 /* Tells if AGP is enabled */
# define PCI_AGP_COMMAND_64BIT 0x20
# define PCI_AGP_COMMAND_FW 0x10 /* Fast Writes */
# define PCI_AGP_COMMAND_RATE_4X 0x4
# define PCI_AGP_COMMAND_RATE_2X 0x2
# define PCI_AGP_COMMAND_RATE_1X 0x1
# define PCI_AGP_COMMAND_RATE_MASK 0x7 /* AGP4X | AGP2X | AGP1X */
#define PCI_CAP_ID_EXP 0x10 /* PCI-Express */
#define PCIE_LINKCAP 0xc
#define PCIE_LINKCONTROL 0x10
#define PCIE_LINKSTATUS 0x12
# define PCIE_LINK_SPEED_MASK 0x3f0
# define PCIE_LINK_SPEED_SHIFT 4
/* PMC */
#define NV_PMC_BOOT_0 0x0
# define NV_PMC_BOOT_0_REVISION_MINOR 0xf
# define NV_PMC_BOOT_0_REVISION_MAJOR 0xf0 /* in general A or B, on pre-NV10 it was different */
# define NV_PMC_BOOT_0_REVISION_MASK 0xff
/* NV4X registers
*
* 0xc040: used to enble/disable parts of the GPU?
* bit1:0, enable/disable PLL 0x4000/0x4004; perhaps one bit is enable and the other a PLL layout switch?
* bit3:2, ??
* bit5:4, seems similar to bit1:0, perhaps for a VPLL? but in various cases it is equal to bit1:0
* bit7:6, ??
* bit9:8, seems similar to bit1:0, perhaps for a VPLL? but in various cases it is equal to bit1:0
* bit11:10, seems related to 0x4030/0x4034 (??)
* bit12:13, ??
* bit15:14, seems related to 0x4020/0x4024; perhaps it is for all MPLL ones? (what about bit11:10 then?)
* bit17:16, seems related to 0x680508/0x680578 (primary VPLL)
* bit29:28, seems to be off when dumping bios
*/
/* NV5x registers
*
* The following register is around again but much more bits are used, so start a new description
* 0xc040: used to enable/disable parts of the GPU
* bit21:20, used to enable/disable PLL 0x4008/0x400c (gpu clock)
*/

335
nvclock/overclock.c Normal file
View File

@@ -0,0 +1,335 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2005 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 <stdlib.h>
#include "backend.h"
static int CalcSpeed(int base_freq, int m, int n, int p)
{
return (int)((float)n/(float)m * base_freq) >> p;
}
/* Generic clock algorithm for all cards upto the Geforce4 */
float GetClock(int base_freq, unsigned int pll)
{
int m, n, p;
m = pll & 0xff;
n = (pll >> 8) & 0xff;
p = (pll >> 16) & 0x0f;
if(nv_card->debug)
printf("m=%d n=%d p=%d\n", m, n, p);
return (float)CalcSpeed(base_freq, m, n, p)/1000;
}
/* Calculate the requested speed. */
static void ClockSelect(int base_freq, int clockIn, int *PLL)
{
int m, n, p, bestm, bestn, bestp;
int diff, diffOld, mlow, mhigh, nlow, nhigh, plow, phigh;
diffOld = clockIn;
if(base_freq == 14318)
{
mlow = 7;
mhigh = 14;
nlow = 14;
nhigh = 255;
}
else
{
mlow = 6;
mhigh = 13;
nlow = 14;
nhigh = 255;
}
if(clockIn > 250000)
{
mlow = 1;
mhigh = 6;
nlow = 14;
nhigh = 93;
}
if(clockIn > 340000)
{
/* When DDR memory is used we should perhaps force mhigh to 1, since
* on some cards the framebuffer needs to be reinitialized and image corruption
* can occur.
*/
mlow = 1;
mhigh = 2;
nlow = 14;
nhigh = 93;
}
/* postdivider locking to improve stability.
* in the near future we will provide some tuning options for the
* overclocking algorithm which will extend this.
*/
plow = (*PLL >> 16) & 0x0f;
phigh = (*PLL >> 16) & 0x0f;
/*
Calculate the m and n values. There are a lot of values which give the same speed;
We choose the speed for which the difference with the request speed is as small as possible.
*/
for(p = plow; p <= phigh; p++)
{
for(m = mlow; m <= mhigh; m++)
{
for(n = nlow; n <= nhigh; n++)
{
diff = abs((int)(clockIn - CalcSpeed(base_freq, m, n, p)));
/* When the new difference is smaller than the old one, use this one */
if(diff < diffOld)
{
diffOld = diff;
bestm = m;
bestn = n;
bestp = p;
#if 0
/* When the difference is 0 or less than .5% accept the speed */
if(((diff == 0) || ((float)diff/(float)clockIn <= 0.005)))
{
*PLL = ((int)bestp << 16) + ((int)bestn << 8) + bestm;
return;
}
#endif
}
}
}
}
*PLL = ((int)bestp << 16) + ((int)bestn << 8) + bestm;
return;
}
static void set_gpu_speed(unsigned int clk)
{
int PLL;
/* MHz -> KHz */
clk *= 1000;
PLL = nv_card->PRAMDAC[0x500/4];
/* HERE the new clocks are selected (in KHz). */
ClockSelect(nv_card->base_freq, clk, &PLL);
/* Unlock the programmable NVPLL/MPLL */
nv_card->PRAMDAC[0x50c/4] |= 0x500;
/* Overclock */
nv_card->PRAMDAC[0x500/4] = PLL;
}
static void set_memory_speed(unsigned int clk)
{
int PLL = nv_card->PRAMDAC[0x504/4];
/* MHz -> KHz */
clk *= 1000;
/* This is a workaround meant for some Geforce2 MX/Geforce4 MX cards
* using SDR memory. Gf2MX/Gf4MX cards use 4x16 SDR memory report
* twice as high clockspeeds. I call that "fake ddr".
* By detecting the memory type, pci id and clockspeed we check
* if this occurs. It is a workaround.
*/
if(nv_card->mem_type == SDR && ( nv_card->device_id == 0x110 || nv_card->device_id == 0x111
|| nv_card->device_id == 0x172 || nv_card->device_id == 0x17a))
{
if(GetClock(nv_card->base_freq, PLL) > 280) clk *= 2;
}
/* Here the clocks are selected in kHz */
ClockSelect(nv_card->base_freq, clk, &PLL);
/* Unlock the programmable NVPLL/MPLL */
nv_card->PRAMDAC[0x50c/4] |= 0x500;
/* Overclock */
nv_card->PRAMDAC[0x504/4] = PLL;
}
static float get_gpu_speed()
{
int pll = nv_card->PRAMDAC[0x500/4];
/* Unlock the programmable NVPLL/MPLL */
nv_card->PRAMDAC[0x50c/4] |= 0x500;
if(nv_card->debug == 1)
{
printf("NVPLL_COEFF=%08x\n", pll);
}
return (float)GetClock(nv_card->base_freq, pll);
}
static float get_memory_speed()
{
int factor = 1;
int pll = nv_card->PRAMDAC[0x504/4];
/* Unlock the programmable NVPLL/MPLL */
nv_card->PRAMDAC[0x50c/4] |= 0x500;
/* This is a workaround meant for some Geforce2 MX/Geforce4 MX cards
* using SDR memory. Gf2MX/Gf4MX cards use 4x16 SDR memory report
* twice as high clockspeeds. I call that "fake ddr".
* By detecting the memory type, pci id and clockspeed we check
* if this occurs. It is a workaround. We divide the memclk later by 2.
*/
if(nv_card->mem_type == SDR && ( nv_card->device_id == 0x110 || nv_card->device_id == 0x111 ||
nv_card->device_id == 0x172 || nv_card->device_id == 0x17a || nv_card->device_id == 0x182 \
|| nv_card->device_id == 0x183))
{
if(GetClock(nv_card->base_freq, pll) > 280)
{
factor = 2;
}
}
if(nv_card->debug == 1)
{
printf("MPLL_COEFF=%08x\n", nv_card->PRAMDAC[0x504/4]);
}
return ((float)GetClock(nv_card->base_freq, pll)) / factor;
}
static float nforce_get_memory_speed()
{
unsigned short p = (pciReadLong(0x3, 0x6c) >> 8) & 0xf;
if(!p) p = 4;
return 400.0 / (float)p;
}
static void reset_gpu_speed()
{
/* 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 reset_memory_speed()
{
/* Unlock the programmable NVPLL/MPLL */
nv_card->PRAMDAC[0x50c/4] |= 0x500;
/* Don't overclock the memory of integrated GPUs */
if(nv_card->gpu == NFORCE)
return;
/* Set the memory speed */
nv_card->PRAMDAC[0x504/4] = nv_card->mpll;
}
static void nv_set_state(int state)
{
}
void nv_init(void)
{
float memclk, nvclk;
/* Get the base frequency */
nv_card->base_freq = (nv_card->PEXTDEV[0x0000/4] & 0x40) ? 14318 : 13500;
if(nv_card->arch & (NV17 | NV25))
{
if (nv_card->PEXTDEV[0x0000/4] & (1<<22))
nv_card->base_freq = 27000;
}
nv_card->set_state = nv_set_state;
nv_card->get_gpu_speed = get_gpu_speed;
nv_card->set_gpu_speed = set_gpu_speed;
nv_card->get_memory_speed = get_memory_speed;
nv_card->set_memory_speed = set_memory_speed;
nv_card->reset_gpu_speed = reset_gpu_speed;
nv_card->reset_memory_speed = reset_memory_speed;
/* Register I2C busses for hardware monitoring purposes */
if(nv_card->busses[0] == NULL)
{
nv_card->num_busses = 2;
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 */
/* There's an extra bus available on geforce4mx/ti, geforcefx and geforce6 cards.
/ The check below looks for geforce4mx/geforcefx/geforce6 architecture.
*/
if(nv_card->arch & (NV17 | NV25 | NV3X | NV4X))
{
nv_card->num_busses = 3;
nv_card->busses[2] = NV_I2CCreateBusPtr("BUS2", 0x50);
}
}
/* 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 if(nv_card->gpu == NFORCE)
{
/* Only support gpu overclocking because the memory is normal system memory of which we can't adjust the clocks */
nv_card->caps |= GPU_OVERCLOCKING;
nv_card->get_memory_speed = nforce_get_memory_speed;
nv_card->set_memory_speed = NULL;
nv_card->reset_memory_speed = NULL;
}
else
nv_card->caps |= (GPU_OVERCLOCKING | MEM_OVERCLOCKING);
/* Set the speed range */
memclk = GetClock(nv_card->base_freq, nv_card->mpll);
nvclk = GetClock(nv_card->base_freq, nv_card->nvpll);
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);
/* Find out what memory is being used */
nv_card->mem_type = (nv_card->PFB[0x200/4] & 0x01) ? DDR : SDR;
/* Hack.
* Nvidia was so nice to ship support both DDR and SDR memory on some gf2mx and gf4mx cards :(
* Because of this the speed ranges of the memory speed can be different.
* Check if the card is a gf2mx/gf4mx using SDR and if the speed is "too" high.
* Then adjust the speed range.
*/
if((nv_card->device_id == 0x110 || nv_card->device_id == 0x111 \
|| nv_card->device_id == 0x172 || nv_card->device_id == 0x182 \
|| nv_card->device_id == 0x183) && ((nv_card->PFB[0x200/4] & 0x1) == SDR) && memclk > 280)
{
nv_card->memclk_min /= 2;
nv_card->memclk_max /= 2;
}
}

BIN
nvclock/overclock.o Normal file

Binary file not shown.

49
nvclock/tete.c Normal file
View File

@@ -0,0 +1,49 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* Copyright(C) 2001-2008 Roderick Colenbrander
* Copyright(C) 2012 Roman Dobosz
*
* site: http://NVClock.sourceforge.net
*
* 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 <stdlib.h>
#include "nvclock.h"
void unload_nvclock() {
/* Free the config file structure */
if(nvclock.cfg) destroy(&nvclock.cfg);
}
int get_GPU_temp() {
nvclock.dpy = NULL;
if(!init_nvclock()) return 0;
atexit(unload_nvclock);
if(!set_card(0)) return 0;
if(!(nv_card->caps & (GPU_TEMP_MONITORING))) return 0;
return nv_card->get_gpu_temp(nv_card->sensor);
}
int main(){
printf("%dC\n", get_temp());
}

77
nvclock/utils.c Normal file
View File

@@ -0,0 +1,77 @@
/* 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>
/* Convert the gpu architecture to a string using NVxx/Gxx naming */
int convert_gpu_architecture(short arch, char *buf)
{
if(!buf)
return 0;
switch(arch)
{
case 0x46:
sprintf(buf, "NV46/G72"); /* 7300 */
break;
case 0x47:
sprintf(buf, "NV47/G70"); /* 7800 */
break;
case 0x49:
sprintf(buf, "NV49/G71"); /* 7900 */
break;
case 0x4b:
sprintf(buf, "NV4B/G73"); /* 7600 */
break;
case 0x4c: /* is this correct also a C51? */
case 0x4e:
sprintf(buf, "C51"); /* Geforce 6x00 nForce */
break;
// sprintf(buf, "C68"); /* Geforce 70xx nForce */
case 0x50:
sprintf(buf, "NV50/G80"); /* 8800 */
break;
default:
if(arch <= 0x44) /* The NV44/6200TC is the last card with only an NV name */
sprintf(buf, "NV%X", arch);
else /* Use Gxx for anything else */
sprintf(buf, "G%X", arch);
}
return 1;
}
/* Convert a mask containing enabled/disabled pipelines for nv4x cards
/ to a binary string.
*/
void convert_unit_mask_to_binary(char mask, char hw_default, char *buf)
{
int i, len;
/* Count the number of pipelines on the card */
for(i=0, len=0; i<8; i++)
len += (hw_default & (1<<i)) ? 1 : 0;
for(i=0; i<len; i++)
{
buf[i] = (mask & (1<<(len-i-1))) ? '1' : '0';
}
buf[len] = 0;
}

BIN
nvclock/utils.o Normal file

Binary file not shown.

81
nvclock/w83781d.c Normal file
View File

@@ -0,0 +1,81 @@
/* NVClock Winbond W83781D hardware monitoring
*/
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include "i2c.h"
#include "nvclock.h"
/* various defines for register offsets and such are needed */
#define W83781D_REG_LOCAL_TEMP 0x27
#define W83781D_REG_REMOTE_TEMP 0x27
#define W83781D_REG_FAN1_COUNT 0x28
#define W83781D_REG_MAN_ID 0x4f
#define ASUS_MAN_ID 0x12
#define W83781D_MAN_ID 0x5c
#define W83781D_REG_CHIP_ID 0x58
#define W83781D_REG_FAN_DIVISOR 0x47
/* This function should return the chip type .. */
int w83781d_detect(I2CDevPtr dev)
{
I2CByte man_id, chip_id;
xf86I2CReadByte (dev, W83781D_REG_MAN_ID, &man_id);
xf86I2CReadByte (dev, W83781D_REG_CHIP_ID, &chip_id);
switch(man_id)
{
case ASUS_MAN_ID:
case W83781D_MAN_ID:
/* We still need a chip_id check (0x11 for w83781d) */
dev->chip_id = W83781D;
dev->chip_name = (char*)strdup("Winbond W83781D");
break;
default:
printf("Uknown Winbond vendor: %x\n", man_id);
return 0;
}
return 1;
}
int w83781d_get_board_temp(I2CDevPtr dev)
{
I2CByte temp;
xf86I2CReadByte(dev, W83781D_REG_LOCAL_TEMP, &temp);
return temp;
}
/* only one temperature exists ... */
int w83781d_get_gpu_temp(I2CDevPtr dev)
{
I2CByte temp;
xf86I2CReadByte(dev, W83781D_REG_REMOTE_TEMP, &temp);
return temp;
}
int w83781d_get_fanspeed_rpm(I2CDevPtr dev)
{
I2CByte count, divisor;
xf86I2CReadByte(dev, W83781D_REG_FAN1_COUNT, &count);
xf86I2CReadByte(dev, W83781D_REG_FAN_DIVISOR, &divisor);
divisor = 1 << ((divisor >> 4) & 0x3); /* bit 5:4 are for fan1; a value of 0 means a divider of 1, while 2 means 2^3 = 8 */
/* A count of 0xff indicates that something is wrong i.e. no fan is connected */
if(count == 0xff)
return 0;
return 1350000/(count * divisor);
}
float w83781d_get_fanspeed_pwm(I2CDevPtr dev)
{
}
int w83781d_set_fanspeed_pwm(I2CDevPtr dev, float speed)
{
}

BIN
nvclock/w83781d.o Normal file

Binary file not shown.

123
nvclock/w83l785r.c Normal file
View File

@@ -0,0 +1,123 @@
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2005 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
*
* W83L785R hardware monitoring
*/
#include <string.h>
#include "i2c.h"
#include "nvclock.h"
/* various defines for register offsets and such are needed */
#define W83L785R_REG_LOCAL_TEMP 0x26
#define W83L785R_REG_LOCAL_TEMP_OFFSET 0x85
#define W83L785R_REG_REMOTE_TEMP 0x27
#define W83L785R_REG_REMOTE_TEMP_OFFSET 0x86
#define W83L785R_REG_FAN1_COUNT 0x28
#define W83L785R_REG_FAN2_COUNT 0x29
#define W83L785R_REG_FAN_DIVISOR 0x47 /* bit 2-0 for fan1; bit 6-4 for fan2 */
#define W83L785R_REG_MAN_ID_L 0x4c
#define W83L785R_REG_MAN_ID_H 0x4d
#define W83L785R_REG_CHIP_ID 0x4e
#define W83L785R_REG_FAN1_PWM 0x81
#define W83L785R_REG_FAN2_PWM 0x83
/* This function should return the chip type .. */
int w83l785r_detect(I2CDevPtr dev)
{
I2CByte man_id_l, man_id_h, chip_id;
xf86I2CReadByte(dev, W83L785R_REG_MAN_ID_L, &man_id_l);
xf86I2CReadByte(dev, W83L785R_REG_MAN_ID_H, &man_id_h);
xf86I2CReadByte(dev, W83L785R_REG_CHIP_ID, &chip_id);
/* Winbond chip */
if((man_id_l == 0xa3) && (man_id_h == 0x5c))
{
if((chip_id & 0xfe) == 0x60)
{
dev->chip_id = W83L785R;
dev->chip_name = (char*)strdup("Winbond W83L785R");
return 1;
}
}
return 0;
}
int w83l785r_get_board_temp(I2CDevPtr dev)
{
I2CByte temp, offset;
xf86I2CReadByte(dev, W83L785R_REG_LOCAL_TEMP, &temp);
xf86I2CReadByte(dev, W83L785R_REG_LOCAL_TEMP_OFFSET, &offset);
return temp + offset;
}
int w83l785r_get_gpu_temp(I2CDevPtr dev)
{
I2CByte temp, offset;
xf86I2CReadByte(dev, W83L785R_REG_REMOTE_TEMP, &temp);
xf86I2CReadByte(dev, W83L785R_REG_REMOTE_TEMP_OFFSET, &offset);
return temp + offset;
}
int w83l785r_get_fanspeed_rpm(I2CDevPtr dev)
{
I2CByte count, divisor;
xf86I2CReadByte(dev, W83L785R_REG_FAN1_COUNT, &count);
xf86I2CReadByte(dev, W83L785R_REG_FAN_DIVISOR, &divisor);
divisor &= 0x7;
/* By default count useally is 153, it seems that a value of 255 means that something is wrong.
/ For example it retuns this value on boards on which the fan is replaced with a heatpipe and because
/ of that the fan was removed.
*/
if(count == 0xff)
return 0;
return 1350000/(count * (1<<divisor));
}
float w83l785r_get_fanspeed_pwm(I2CDevPtr dev)
{
I2CByte value;
xf86I2CReadByte(dev, W83L785R_REG_FAN1_PWM, &value);
return (float)(0xff-value)*100/256;
}
int w83l785r_set_fanspeed_pwm(I2CDevPtr dev, float speed)
{
/* We have 8 bits to adjust the duty cycle, to set a speed of 100% we need to write a value of 0
/ as the register is inverted. (0 means 100% and 0xff means 0)
/
/ Below we scale the speed (in %) with 255/100 to turn '100%' into the max value 255.
*/
I2CByte value = (100 - (int)speed) * 255/100;
xf86I2CWriteByte(dev, W83L785R_REG_FAN1_PWM, value);
xf86I2CReadByte(dev, W83L785R_REG_FAN1_PWM, &value);
return 1;
}

BIN
nvclock/w83l785r.o Normal file

Binary file not shown.

876
nvclock/xf86i2c.c Normal file
View File

@@ -0,0 +1,876 @@
/*
* Copyright (C) 1998 Itai Nahshon, Michael Schimek
*
* The original code was derived from and inspired by
* the I2C driver from the Linux kernel.
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
*/
/* $XFree86: xc/programs/Xserver/hw/xfree86/i2c/xf86i2c.c,v 1.6 1999/06/12 15:37:08 dawes Exp $ */
#include "xfree.h"
#include <string.h>
#if 0
#define NULL ((void *)0)
#include "misc.h"
#include "xf86.h"
#include "xf86_ansic.h"
#include "xf86_OSproc.h"
#include "X.h"
#include "Xproto.h"
#include "scrnintstr.h"
#include "regionstr.h"
#include "windowstr.h"
#include "pixmapstr.h"
#include "validate.h"
#include "resource.h"
#include "gcstruct.h"
#include "dixstruct.h"
/* #else */
typedef int Bool;
typedef void *Pointer;
#define TRUE 1
#define FALSE 0
#endif
#include "xf86i2c.h"
#define I2C_TIMEOUT(x) /*(x)*/ /* Report timeouts */
#define I2C_TRACE(x) /*(x)*/ /* Report progress */
/* Set which OSs have bad gettimeofday resolution. */
#if defined(SVR4) && !defined(sun)
#define BAD_GETTIMEOFDAY_RESOLUTION
#endif
/* This is the default I2CUDelay function if not supplied by the driver.
* High level I2C interfaces implementing the bus protocol in hardware
* should supply this function too.
*
* Delay execution at least usec microseconds.
* All values 0 to 1e6 inclusive must be expected.
*/
#ifdef BAD_GETTIMEOFDAY_RESOLUTION
/*
* This is temporary until a better, portable
* way is found. Adjust bogo_usec to match CPU speed.
*/
static int bogo_usec = 500;
static void
I2CUDelay(I2CBusPtr b, int usec)
{
volatile long i;
if (usec > 0)
for (i = usec * bogo_usec; i > 0; i--)
/* (perhaps hw delay action) */;
}
#else
static void
I2CUDelay(I2CBusPtr b, int usec)
{
long b_secs, b_usecs;
long a_secs, a_usecs;
long d_secs, d_usecs;
long diff;
if (usec > 0) {
xf86getsecs(&b_secs, &b_usecs);
do {
/* It would be nice to use {xf86}usleep,
* but usleep (1) takes >10000 usec !
*/
xf86getsecs(&a_secs, &a_usecs);
d_secs = (a_secs - b_secs);
d_usecs = (a_usecs - b_usecs);
diff = d_secs*1000000 + d_usecs;
} while (diff>0 && diff< (usec + 1));
}
}
#endif
/* Most drivers will register just with GetBits/PutBits functions.
* The following functions implement a software I2C protocol
* by using the promitive functions given by the driver.
* ================================================================
*
* It is assumed that there is just one master on the I2C bus, therefore
* there is no explicit test for conflits.
*/
#define RISEFALLTIME 2 /* usec, actually 300 to 1000 ns according to the i2c specs */
/* Some devices will hold SCL low to slow down the bus or until
* ready for transmission.
*
* This condition will be noticed when the master tries to raise
* the SCL line. You can set the timeout to zero if the slave device
* does not support this clock synchronization.
*/
static Bool
I2CRaiseSCL(I2CBusPtr b, int sda, int timeout)
{
int i, scl;
b->I2CPutBits(b, 1, sda);
b->I2CUDelay(b, b->RiseFallTime);
for (i = timeout; i > 0; i -= b->RiseFallTime) {
b->I2CGetBits(b, &scl, &sda);
if (scl) break;
b->I2CUDelay(b, b->RiseFallTime);
}
if (i <= 0) {
I2C_TIMEOUT(ErrorF("[I2CRaiseSCL(<%s>, %d, %d) timeout]", b->BusName, sda, timeout));
return FALSE;
}
return TRUE;
}
/* Send a start signal on the I2C bus. The start signal notifies
* devices that a new transaction is initiated by the bus master.
*
* The start signal is always followed by a slave address.
* Slave addresses are 8+ bits. The first 7 bits identify the
* device and the last bit signals if this is a read (1) or
* write (0) operation.
*
* There may be more than one start signal on one transaction.
* This happens for example on some devices that allow reading
* of registers. First send a start bit followed by the device
* address (with the last bit 0) and the register number. Then send
* a new start bit with the device address (with the last bit 1)
* and then read the value from the device.
*
* Note this is function does not implement a multiple master
* arbitration procedure.
*/
static Bool
I2CStart(I2CBusPtr b, int timeout)
{
int i, scl, sda;
b->I2CPutBits(b, 1, 1);
b->I2CUDelay(b, b->RiseFallTime);
for (i = timeout; i > 0; i -= b->RiseFallTime) {
b->I2CGetBits(b, &scl, &sda);
if (scl) break;
b->I2CUDelay(b, b->RiseFallTime);
}
if (i <= 0) {
I2C_TIMEOUT(ErrorF("\ni2c: <[I2CStart(<%s>, %d) timeout]", b->BusName, timeout));
return FALSE;
}
b->I2CPutBits(b, 1, 0);
b->I2CUDelay(b, b->HoldTime);
b->I2CPutBits(b, 0, 0);
b->I2CUDelay(b, b->HoldTime);
I2C_TRACE(ErrorF("\ni2c: <"));
return TRUE;
}
/* This is the default I2CStop function if not supplied by the driver.
*
* Signal devices on the I2C bus that a transaction on the
* bus has finished. There may be more than one start signal
* on a transaction but only one stop signal.
*/
static void
I2CStop(I2CDevPtr d)
{
I2CBusPtr b = d->pI2CBus;
b->I2CPutBits(b, 0, 0);
b->I2CUDelay(b, b->RiseFallTime);
b->I2CPutBits(b, 1, 0);
b->I2CUDelay(b, b->HoldTime);
b->I2CPutBits(b, 1, 1);
b->I2CUDelay(b, b->HoldTime);
I2C_TRACE(ErrorF(">\n"));
}
/* Write/Read a single bit to/from a device.
* Return FALSE if a timeout occurs.
*/
static Bool
I2CWriteBit(I2CBusPtr b, int sda, int timeout)
{
Bool r;
b->I2CPutBits(b, 0, sda);
b->I2CUDelay(b, b->RiseFallTime);
r = I2CRaiseSCL(b, sda, timeout);
b->I2CUDelay(b, b->HoldTime);
b->I2CPutBits(b, 0, sda);
b->I2CUDelay(b, b->HoldTime);
return r;
}
static Bool
I2CReadBit(I2CBusPtr b, int *psda, int timeout)
{
Bool r;
int scl;
r = I2CRaiseSCL(b, 1, timeout);
b->I2CUDelay(b, b->HoldTime);
b->I2CGetBits(b, &scl, psda);
b->I2CPutBits(b, 0, 1);
b->I2CUDelay(b, b->HoldTime);
return r;
}
/* This is the default I2CPutByte function if not supplied by the driver.
*
* A single byte is sent to the device.
* The function returns FALSE if a timeout occurs, you should send
* a stop condition afterwards to reset the bus.
*
* A timeout occurs,
* if the slave pulls SCL to slow down the bus more than ByteTimeout usecs,
* or slows down the bus for more than BitTimeout usecs for each bit,
* or does not send an ACK bit (0) to acknowledge the transmission within
* AcknTimeout usecs, but a NACK (1) bit.
*
* AcknTimeout must be at least b->HoldTime, the other timeouts can be
* zero according to the comment on I2CRaiseSCL.
*/
static Bool
I2CPutByte(I2CDevPtr d, I2CByte data)
{
Bool r;
int i, scl, sda;
I2CBusPtr b = d->pI2CBus;
if (!I2CWriteBit(b, (data >> 7) & 1, d->ByteTimeout))
return FALSE;
for (i = 6; i >= 0; i--)
if (!I2CWriteBit(b, (data >> i) & 1, d->BitTimeout))
return FALSE;
b->I2CPutBits(b, 0, 1);
b->I2CUDelay(b, b->RiseFallTime);
r = I2CRaiseSCL(b, 1, b->HoldTime);
if (r) {
for (i = d->AcknTimeout; i > 0; i -= b->HoldTime) {
b->I2CUDelay(b, b->HoldTime);
b->I2CGetBits(b, &scl, &sda);
if (sda == 0) break;
}
if (i <= 0) {
I2C_TIMEOUT(ErrorF("[I2CPutByte(<%s>, 0x%02x, %d, %d, %d) timeout]",
b->BusName, data, d->BitTimeout,
d->ByteTimeout, d->AcknTimeout));
r = FALSE;
}
I2C_TRACE(ErrorF("W%02x%c ", (int) data, sda ? '-' : '+'));
}
b->I2CPutBits(b, 0, 1);
b->I2CUDelay(b, b->HoldTime);
return r;
}
/* This is the default I2CGetByte function if not supplied by the driver.
*
* A single byte is read from the device.
* The function returns FALSE if a timeout occurs, you should send
* a stop condition afterwards to reset the bus.
*
* A timeout occurs,
* if the slave pulls SCL to slow down the bus more than ByteTimeout usecs,
* or slows down the bus for more than b->BitTimeout usecs for each bit.
*
* ByteTimeout must be at least b->HoldTime, the other timeouts can be
* zero according to the comment on I2CRaiseSCL.
*
* For the <last> byte in a sequence the acknowledge bit NACK (1),
* otherwise ACK (0) will be sent.
*/
static Bool
I2CGetByte(I2CDevPtr d, I2CByte *data, Bool last)
{
int i, sda;
I2CBusPtr b = d->pI2CBus;
b->I2CPutBits(b, 0, 1);
b->I2CUDelay(b, b->RiseFallTime);
if (!I2CReadBit(b, &sda, d->ByteTimeout))
return FALSE;
*data = (sda > 0) << 7;
for (i = 6; i >= 0; i--)
if (!I2CReadBit(b, &sda, d->BitTimeout))
return FALSE;
else
*data |= (sda > 0) << i;
if (!I2CWriteBit(b, last ? 1 : 0, d->BitTimeout))
return FALSE;
I2C_TRACE(ErrorF("R%02x%c ", (int) *data, last ? '+' : '-'));
return TRUE;
}
/* This is the default I2CAddress function if not supplied by the driver.
*
* It creates the start condition, followed by the d->SlaveAddr.
* Higher level functions must call this routine rather than
* I2CStart/PutByte because a hardware I2C master may not be able
* to send a slave address without a start condition.
*
* The same timeouts apply as with I2CPutByte and additional a
* StartTimeout, similar to the ByteTimeout but for the start
* condition.
*
* In case of a timeout, the bus is left in a clean idle condition.
* I. e. you *must not* send a Stop. If this function succeeds, you *must*.
*
* The slave address format is 16 bit, with the legacy _8_bit_ slave address
* in the least significant byte. This is, the slave address must include the
* R/_W flag as least significant bit.
*
* The most significant byte of the address will be sent _after_ the LSB,
* but only if the LSB indicates:
* a) an 11 bit address, this is LSB = 1111 0xxx.
* b) a 'general call address', this is LSB = 0000 000x - see the I2C specs
* for more.
*/
Bool
I2CAddress(I2CDevPtr d, I2CSlaveAddr addr)
{
if (I2CStart(d->pI2CBus, d->StartTimeout)) {
if (I2CPutByte(d, addr & 0xFF)) {
if ((addr & 0xF8) != 0xF0 &&
(addr & 0xFE) != 0x00)
return TRUE;
if (I2CPutByte(d, (addr >> 8) & 0xFF))
return TRUE;
}
I2CStop(d);
}
return FALSE;
}
/* These are the hardware independent I2C helper functions.
* ========================================================
*/
/* Function for probing. Just send the slave address
* and return true if the device responds. The slave address
* must have the lsb set to reflect a read (1) or write (0) access.
* Don't expect a read- or write-only device will respond otherwise.
*/
Bool
xf86I2CProbeAddress(I2CBusPtr b, I2CSlaveAddr addr)
{
int r;
I2CDevRec d;
d.DevName = "Probing";
d.BitTimeout = b->BitTimeout;
d.ByteTimeout = b->ByteTimeout;
d.AcknTimeout = b->AcknTimeout;
d.StartTimeout = b->StartTimeout;
d.SlaveAddr = addr;
d.pI2CBus = b;
d.NextDev = NULL;
r = b->I2CAddress(&d, addr);
if (r) b->I2CStop(&d);
return r;
}
/* All functions below are related to devices and take the
* slave address and timeout values from an I2CDevRec. They
* return FALSE in case of an error (presumably a timeout).
*/
/* General purpose read and write function.
*
* 1st, if nWrite > 0
* Send a start condition
* Send the slave address (1 or 2 bytes) with write flag
* Write n bytes from WriteBuffer
* 2nd, if nRead > 0
* Send a start condition [again]
* Send the slave address (1 or 2 bytes) with read flag
* Read n bytes to ReadBuffer
* 3rd, if a Start condition has been successfully sent,
* Send a Stop condition.
*
* The functions exits immediately when an error occures,
* not proceeding any data left. However, step 3 will
* be executed anyway to leave the bus in clean idle state.
*/
Bool
xf86I2CWriteRead(I2CDevPtr d,
I2CByte *WriteBuffer, int nWrite,
I2CByte *ReadBuffer, int nRead)
{
Bool r = TRUE;
I2CBusPtr b = d->pI2CBus;
int s = 0;
if (r && nWrite > 0) {
r = b->I2CAddress(d, d->SlaveAddr & ~1);
if (r) {
for (; nWrite > 0; WriteBuffer++, nWrite--)
if (!(r = b->I2CPutByte(d, *WriteBuffer)))
break;
s++;
}
}
if (r && nRead > 0) {
r = b->I2CAddress(d, d->SlaveAddr | 1);
if (r) {
for (; nRead > 0; ReadBuffer++, nRead--)
if (!(r = b->I2CGetByte(d, ReadBuffer, nRead == 1)))
break;
s++;
}
}
if (s) b->I2CStop(d);
return r;
}
/* Read a byte, the only readable register of a device.
*/
Bool
xf86I2CReadStatus(I2CDevPtr d, I2CByte *pbyte)
{
return xf86I2CWriteRead(d, NULL, 0, pbyte, 1);
}
/* Read a byte from one of the registers determined by its sub-address.
*/
Bool
xf86I2CReadByte(I2CDevPtr d, I2CByte subaddr, I2CByte *pbyte)
{
return xf86I2CWriteRead(d, &subaddr, 1, pbyte, 1);
}
/* Read bytes from subsequent registers determined by the
* sub-address of the first register.
*/
Bool
xf86I2CReadBytes(I2CDevPtr d, I2CByte subaddr, I2CByte *pbyte, int n)
{
return xf86I2CWriteRead(d, &subaddr, 1, pbyte, n);
}
/* Read a word (high byte, then low byte) from one of the registers
* determined by its sub-address.
*/
Bool
xf86I2CReadWord(I2CDevPtr d, I2CByte subaddr, unsigned short *pword)
{
I2CByte rb[2];
if (!xf86I2CWriteRead(d, &subaddr, 1, rb, 2)) return FALSE;
*pword = (rb[0] << 8) | rb[1];
return TRUE;
}
/* Write a byte to one of the registers determined by its sub-address.
*/
Bool
xf86I2CWriteByte(I2CDevPtr d, I2CByte subaddr, I2CByte byte)
{
I2CByte wb[2];
wb[0] = subaddr;
wb[1] = byte;
return xf86I2CWriteRead(d, wb, 2, NULL, 0);
}
/* Write bytes to subsequent registers determined by the
* sub-address of the first register.
*/
Bool
xf86I2CWriteBytes(I2CDevPtr d, I2CByte subaddr,
I2CByte *WriteBuffer, int nWrite)
{
I2CBusPtr b = d->pI2CBus;
Bool r = TRUE;
if (nWrite > 0) {
r = b->I2CAddress(d, d->SlaveAddr & ~1);
if (r){
if ((r = b->I2CPutByte(d, subaddr)))
for (; nWrite > 0; WriteBuffer++, nWrite--)
if (!(r = b->I2CPutByte(d, *WriteBuffer)))
break;
b->I2CStop(d);
}
}
return r;
}
/* Write a word (high byte, then low byte) to one of the registers
* determined by its sub-address.
*/
Bool
xf86I2CWriteWord(I2CDevPtr d, I2CByte subaddr, unsigned short word)
{
I2CByte wb[3];
wb[0] = subaddr;
wb[1] = word >> 8;
wb[2] = word & 0xFF;
return xf86I2CWriteRead(d, wb, 3, NULL, 0);
}
/* Write a vector of bytes to not adjacent registers. This vector is,
* 1st byte sub-address, 2nd byte value, 3rd byte sub-address asf.
* This function is intended to initialize devices. Note this function
* exits immediately when an error occurs, some registers may
* remain uninitialized.
*/
Bool
xf86I2CWriteVec(I2CDevPtr d, I2CByte *vec, int nValues)
{
I2CBusPtr b = d->pI2CBus;
Bool r = TRUE;
int s = 0;
if (nValues > 0) {
for (; nValues > 0; nValues--, vec += 2) {
if (!(r = b->I2CAddress(d, d->SlaveAddr & ~1)))
break;
s++;
if (!(r = b->I2CPutByte(d, vec[0])))
break;
if (!(r = b->I2CPutByte(d, vec[1])))
break;
}
if (s > 0) b->I2CStop(d);
}
return r;
}
/* Administrative functions.
* =========================
*/
/* Allocates an I2CDevRec for you and initializes with propper defaults
* you may modify before calling xf86I2CDevInit. Your I2CDevRec must
* contain at least a SlaveAddr, and a pI2CBus pointer to the bus this
* device shall be linked to.
*
* See function I2CAddress for the slave address format. Always set
* the least significant bit, indicating a read or write access, to zero.
*/
I2CDevPtr
xf86CreateI2CDevRec(void)
{
return xcalloc(1, sizeof(I2CDevRec));
}
/* Unlink an I2C device. If you got the I2CDevRec from xf86CreateI2CDevRec
* you should set <unalloc> to free it.
*/
void
xf86DestroyI2CDevRec(I2CDevPtr d, Bool unalloc)
{
if (d) {
I2CDevPtr *p;
/* Remove this from the list of active I2C devices. */
for (p = &d->pI2CBus->FirstDev; *p != NULL; p = &(*p)->NextDev)
if (*p == d) {
*p = (*p)->NextDev;
break;
}
if (d->pI2CBus->scrnIndex >= 0)
xf86DrvMsg(d->pI2CBus->scrnIndex, X_INFO,
"I2C device \"%s:%s\" removed.\n",
d->pI2CBus->BusName, d->DevName);
else
xf86Msg(X_INFO, "I2C device \"%s:%s\" removed.\n",
d->pI2CBus->BusName, d->DevName);
if (unalloc) xfree(d);
}
}
/* I2C transmissions are related to an I2CDevRec you must link to a
* previously registered bus (see xf86I2CBusInit) before attempting
* to read and write data. You may call xf86I2CProbeAddress first to
* see if the device in question is present on this bus.
*
* xf86I2CDevInit will not allocate an I2CBusRec for you, instead you
* may enter a pointer to a statically allocated I2CDevRec or the (modified)
* result of xf86CreateI2CDevRec.
*
* If you don't specify timeouts for the device (n <= 0), it will inherit
* the bus-wide defaults. The function returns TRUE on success.
*/
Bool
xf86I2CDevInit(I2CDevPtr d)
{
I2CBusPtr b;
if (d == NULL ||
(b = d->pI2CBus) == NULL ||
(d->SlaveAddr & 1) ||
xf86I2CFindDev(b, d->SlaveAddr) != NULL)
return FALSE;
if (d->BitTimeout <= 0) d->BitTimeout = b->BitTimeout;
if (d->ByteTimeout <= 0) d->ByteTimeout = b->ByteTimeout;
if (d->AcknTimeout <= 0) d->AcknTimeout = b->AcknTimeout;
if (d->StartTimeout <= 0) d->StartTimeout = b->StartTimeout;
d->NextDev = b->FirstDev;
b->FirstDev = d;
if(b->scrnIndex >= 0)
xf86DrvMsg(b->scrnIndex, X_INFO, "I2C device \"%s:%s\" registered.\n",
b->BusName, d->DevName);
else
xf86Msg(X_INFO, "I2C device \"%s:%s\" registered.\n",
b->BusName, d->DevName);
return TRUE;
}
I2CDevPtr
xf86I2CFindDev(I2CBusPtr b, I2CSlaveAddr addr)
{
I2CDevPtr d;
if (b) {
for (d = b->FirstDev; d != NULL; d = d->NextDev)
if (d->SlaveAddr == addr)
return d;
}
return NULL;
}
static I2CBusPtr I2CBusList;
/* Allocates an I2CBusRec for you and initializes with propper defaults
* you may modify before calling xf86I2CBusInit. Your I2CBusRec must
* contain at least a BusName, a scrnIndex (or -1), and a complete set
* of either high or low level I2C function pointers. You may pass
* bus-wide timeouts, otherwise inplausible values will be replaced
* with safe defaults.
*/
I2CBusPtr
xf86CreateI2CBusRec(void)
{
I2CBusPtr b;
b = (I2CBusPtr) xcalloc(1, sizeof(I2CBusRec));
if (b != NULL) {
b->scrnIndex = -1;
b->HoldTime = 5; /* 100 kHz bus */
b->BitTimeout = 5;
b->ByteTimeout = 5;
b->AcknTimeout = 5;
b->StartTimeout = 5;
b->RiseFallTime = RISEFALLTIME;
}
return b;
}
/* Unregister an I2C bus. If you got the I2CBusRec from xf86CreateI2CBusRec
* you should set <unalloc> to free it. If you set <devs_too>, the function
* xf86DestroyI2CDevRec will be called for all devices linked to the bus
* first, passing down the <unalloc> option.
*/
void
xf86DestroyI2CBusRec(I2CBusPtr b, Bool unalloc, Bool devs_too)
{
if (b) {
I2CBusPtr *p;
/* Remove this from the list of active I2C busses. */
for (p = &I2CBusList; *p != NULL; p = &(*p)->NextBus)
if (*p == b) {
*p = (*p)->NextBus;
break;
}
if (b->FirstDev != NULL) {
if (devs_too) {
I2CDevPtr d;
while ((d = b->FirstDev) != NULL)
xf86DestroyI2CDevRec(d, unalloc);
} else {
if (unalloc) {
xf86Msg(X_ERROR, "i2c bug: Attempt to remove I2C bus \"%s\", "
"but device list is not empty.\n",
b->BusName);
return;
}
}
}
if (b->scrnIndex >= 0)
xf86DrvMsg(b->scrnIndex, X_INFO, "I2C bus \"%s\" removed.\n",
b->BusName);
else
xf86Msg(X_INFO, "I2C bus \"%s\" removed.\n", b->BusName);
if (unalloc) xfree(b);
}
}
/* I2C masters have to register themselves using this function.
* It will not allocate an I2CBusRec for you, instead you may enter
* a pointer to a statically allocated I2CBusRec or the (modified)
* result of xf86CreateI2CBusRec. Returns TRUE on success.
*
* At this point there won't be any traffic on the I2C bus.
*/
Bool
xf86I2CBusInit(I2CBusPtr b)
{
/* I2C busses must be identified by a unique scrnIndex
* and name. If scrnIndex is unspecified (a negative value),
* then the name must be unique throughout the server.
*/
if (b->BusName == NULL ||
xf86I2CFindBus(b->scrnIndex, b->BusName) != NULL)
return FALSE;
/* If the high level functions are not
* supplied, use the generic functions.
* In this case we need the low-level
* function.
*/
if (b->I2CPutBits == NULL ||
b->I2CGetBits == NULL)
{
if (b->I2CPutByte == NULL ||
b->I2CGetByte == NULL ||
b->I2CAddress == NULL ||
b->I2CStop == NULL)
return FALSE;
} else {
b->I2CPutByte = I2CPutByte;
b->I2CGetByte = I2CGetByte;
b->I2CAddress = I2CAddress;
b->I2CStop = I2CStop;
}
if (b->I2CUDelay == NULL)
b->I2CUDelay = I2CUDelay;
if (b->HoldTime < 2) b->HoldTime = 5;
if (b->BitTimeout <= 0) b->BitTimeout = b->HoldTime;
if (b->ByteTimeout <= 0) b->ByteTimeout = b->HoldTime;
if (b->AcknTimeout <= 0) b->AcknTimeout = b->HoldTime;
if (b->StartTimeout <= 0) b->StartTimeout = b->HoldTime;
/* Put new bus on list. */
b->NextBus = I2CBusList;
I2CBusList = b;
if (b->scrnIndex >= 0)
xf86DrvMsg(b->scrnIndex, X_INFO, "I2C bus \"%s\" initialized.\n",
b->BusName);
else
xf86Msg(X_INFO, "I2C bus \"%s\" initialized.\n", b->BusName);
return TRUE;
}
I2CBusPtr
xf86I2CFindBus(int scrnIndex, char *name)
{
I2CBusPtr p;
if (name != NULL)
for (p = I2CBusList; p != NULL; p = p->NextBus)
if (scrnIndex < 0 || p->scrnIndex == scrnIndex)
if (!strcmp(p->BusName, name))
return p;
return NULL;
}

94
nvclock/xf86i2c.h Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 1998 Itai Nahshon, Michael Schimek
*/
/* $XFree86: xc/programs/Xserver/hw/xfree86/i2c/xf86i2c.h,v 1.4 1999/04/11 13:11:01 dawes Exp $ */
#ifndef _XF86I2C_H
#define _XF86I2C_H
#include "xfree.h"
typedef unsigned char I2CByte;
typedef unsigned short I2CSlaveAddr;
typedef struct _I2CBusRec *I2CBusPtr;
typedef struct _I2CDevRec *I2CDevPtr;
/* I2C masters have to register themselves */
typedef struct _I2CBusRec {
char * BusName;
int scrnIndex;
void (*I2CUDelay) (I2CBusPtr b, int usec);
void (*I2CPutBits)(I2CBusPtr b, int scl, int sda);
void (*I2CGetBits)(I2CBusPtr b, int *scl, int *sda);
/* Look at the generic routines to see how these functions should behave. */
Bool (*I2CAddress)(I2CDevPtr d, I2CSlaveAddr);
void (*I2CStop) (I2CDevPtr d);
Bool (*I2CPutByte)(I2CDevPtr d, I2CByte data);
Bool (*I2CGetByte)(I2CDevPtr d, I2CByte *data, Bool);
DevUnion DriverPrivate;
int HoldTime; /* 1 / bus clock frequency, 5 or 2 usec */
int BitTimeout; /* usec */
int ByteTimeout; /* usec */
int AcknTimeout; /* usec */
int StartTimeout; /* usec */
int RiseFallTime; /* usec */
I2CDevPtr FirstDev;
I2CBusPtr NextBus;
} I2CBusRec;
I2CBusPtr xf86CreateI2CBusRec(void);
void xf86DestroyI2CBusRec(I2CBusPtr pI2CBus, Bool unalloc, Bool devs_too);
Bool xf86I2CBusInit(I2CBusPtr pI2CBus);
I2CBusPtr xf86I2CFindBus(int scrnIndex, char *name);
/* I2C slave devices */
typedef struct _I2CDevRec {
char * DevName;
int BitTimeout; /* usec */
int ByteTimeout; /* usec */
int AcknTimeout; /* usec */
int StartTimeout; /* usec */
short chip_id; /* type of i2c chip; required atleast by the lm99 to decide whether to add an offset or not */
short arch; /* architecture to which the gpu belongs; the lm99 code needs this for adding offsets too */
char *chip_name;
I2CSlaveAddr SlaveAddr;
I2CBusPtr pI2CBus;
I2CDevPtr NextDev;
} I2CDevRec;
I2CDevPtr xf86CreateI2CDevRec(void);
void xf86DestroyI2CDevRec(I2CDevPtr pI2CDev, Bool unalloc);
Bool xf86I2CDevInit(I2CDevPtr pI2CDev);
I2CDevPtr xf86I2CFindDev(I2CBusPtr, I2CSlaveAddr);
/* See descriptions of these functions in xf86i2c.c */
Bool xf86I2CProbeAddress(I2CBusPtr pI2CBus, I2CSlaveAddr);
Bool xf86I2CWriteRead(I2CDevPtr d, I2CByte *WriteBuffer, int nWrite,
I2CByte *ReadBuffer, int nRead);
#define xf86I2CRead(d, rb, nr) xf86I2CWriteRead(d, NULL, 0, rb, nr)
Bool xf86I2CReadStatus(I2CDevPtr d, I2CByte *pbyte);
Bool xf86I2CReadByte(I2CDevPtr d, I2CByte subaddr, I2CByte *pbyte);
Bool xf86I2CReadBytes(I2CDevPtr d, I2CByte subaddr, I2CByte *pbyte, int n);
Bool xf86I2CReadWord(I2CDevPtr d, I2CByte subaddr, unsigned short *pword);
#define xf86I2CWrite(d, wb, nw) xf86I2CWriteRead(d, wb, nw, NULL, 0)
Bool xf86I2CWriteByte(I2CDevPtr d, I2CByte subaddr, I2CByte byte);
Bool xf86I2CWriteBytes(I2CDevPtr d, I2CByte subaddr, I2CByte *WriteBuffer, int nWrite);
Bool xf86I2CWriteWord(I2CDevPtr d, I2CByte subaddr, unsigned short word);
Bool xf86I2CWriteVec(I2CDevPtr d, I2CByte *vec, int nValues);
#endif /*_XF86I2C_H */

BIN
nvclock/xf86i2c.o Normal file

Binary file not shown.

76
nvclock/xfree.h Normal file
View File

@@ -0,0 +1,76 @@
/* NVTV xfree -- Dirk Thierbach <dthierbach@gmx.de>
*
* Header: All definitions from xfree that are needed.
*
*/
#ifndef _XFREE_H
#define _XFREE_H 1
#include <stdio.h>
#include <stdlib.h>
#define xf86Msg(type,format,args...) /* */
#define xf86DrvMsg(scrnIndex,type,format, args...) /* */
#ifndef Bool
# ifndef _XTYPEDEF_BOOL
# define _XTYPEDEF_BOOL
typedef int Bool;
# endif
#endif
#ifndef _XTYPEDEF_POINTER
# define _XTYPEDEF_POINTER
typedef void *pointer;
#endif
/* Flags for driver messages */
typedef enum {
X_PROBED, /* Value was probed */
X_CONFIG, /* Value was given in the config file */
X_DEFAULT, /* Value is a default */
X_CMDLINE, /* Value was given on the command line */
X_NOTICE, /* Notice */
X_ERROR, /* Error message */
X_WARNING, /* Warning message */
X_INFO, /* Informational message */
X_NONE, /* No prefix */
X_NOT_IMPLEMENTED /* Not implemented */
} MessageType;
typedef union _DevUnion {
pointer ptr;
long val;
unsigned long uval;
pointer (*fptr)(void);
} DevUnion;
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
void xf86usleep(unsigned long usec);
void xf86getsecs(long * secs, long * usecs);
#define xcalloc(_num, _size) calloc(_num, _size)
#define xfree(_ptr) free(_ptr)
/* ---------------- nv driver files ---------------- */
/**** nv_dac.c */
#define DDC_SDA_READ_MASK (1 << 3)
#define DDC_SCL_READ_MASK (1 << 2)
#define DDC_SDA_WRITE_MASK (1 << 4)
#define DDC_SCL_WRITE_MASK (1 << 5)
#endif

View File

@@ -1,5 +1,4 @@
# By RedSeb 1999, Liverbugg 2002
system=Linux
SRC = wmtempnv.c ../wmgeneral/wmgeneral.c ../wmgeneral/misc.c ../wmgeneral/list.c
@@ -9,7 +8,7 @@ OBJ = $(SRC:.c=.o)
CFLAGS = -Wall -O3
LIB = -L/usr/X11R6/lib -lXpm -lXext -lX11 -lsensors
LIB = -L/usr/X11R6/lib -lXpm -lXext -lX11 -lsensors -lbackend -L../nvclock
INSTALL = /usr/bin/install
@@ -17,7 +16,7 @@ CC = gcc
all: $(OBJ)
$(CC) -o $(EXE) $(OBJ) $(LIB)
strip $(EXE)
#strip $(EXE)
$(OBJ): %.o : %.c
$(CC) $(CFLAGS) -c -o $@ $<

View File

@@ -1,6 +1,6 @@
#define wmtempnv_mask_width 64
#define wmtempnv_mask_height 64
static unsigned char wmtempnv_mask_bits[] = {
static char wmtempnv_mask_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,