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