From 28d2a7fd2ecd5444fba8df73ba80088ce403728a Mon Sep 17 00:00:00 2001 From: gryf Date: Wed, 30 May 2012 11:01:21 +0200 Subject: [PATCH] Attempt to use nvclock code for reading GPU temperature, since running nvidia-settings couple of times for a second is highly inefficient. --- nvclock/Makefile | 39 ++ nvclock/adt7473.c | 173 ++++++ nvclock/adt7473.o | Bin 0 -> 2284 bytes nvclock/back_bsd.c | 250 ++++++++ nvclock/back_linux.c | 296 +++++++++ nvclock/back_linux.o | Bin 0 -> 5044 bytes nvclock/back_win32.c | 162 +++++ nvclock/backend.c | 127 ++++ nvclock/backend.h | 87 +++ nvclock/backend.o | Bin 0 -> 1704 bytes nvclock/bios.c | 1068 +++++++++++++++++++++++++++++++++ nvclock/bios.o | Bin 0 -> 11332 bytes nvclock/config.c | 606 +++++++++++++++++++ nvclock/config.h | 0 nvclock/config.o | Bin 0 -> 9076 bytes nvclock/error.c | 63 ++ nvclock/error.o | Bin 0 -> 1440 bytes nvclock/f75375.c | 167 ++++++ nvclock/f75375.h | 71 +++ nvclock/f75375.o | Bin 0 -> 2784 bytes nvclock/i2c.c | 344 +++++++++++ nvclock/i2c.h | 76 +++ nvclock/i2c.o | Bin 0 -> 6924 bytes nvclock/info.c | 1161 ++++++++++++++++++++++++++++++++++++ nvclock/info.o | Bin 0 -> 27548 bytes nvclock/libbackend.a | Bin 0 -> 115052 bytes nvclock/libc_wrapper.c | 67 +++ nvclock/libc_wrapper.o | Bin 0 -> 888 bytes nvclock/lm99.c | 99 +++ nvclock/lm99.o | Bin 0 -> 1364 bytes nvclock/nv30.c | 426 +++++++++++++ nvclock/nv30.o | Bin 0 -> 7160 bytes nvclock/nv40.c | 1099 ++++++++++++++++++++++++++++++++++ nvclock/nv40.o | Bin 0 -> 12912 bytes nvclock/nv50.c | 321 ++++++++++ nvclock/nv50.o | Bin 0 -> 5084 bytes nvclock/nvclock.h | 341 +++++++++++ nvclock/nvcontrol.c | 164 +++++ nvclock/nvreg.h | 81 +++ nvclock/overclock.c | 335 +++++++++++ nvclock/overclock.o | Bin 0 -> 4888 bytes nvclock/tete.c | 49 ++ nvclock/utils.c | 77 +++ nvclock/utils.o | Bin 0 -> 1740 bytes nvclock/w83781d.c | 81 +++ nvclock/w83781d.o | Bin 0 -> 1588 bytes nvclock/w83l785r.c | 123 ++++ nvclock/w83l785r.o | Bin 0 -> 1892 bytes nvclock/xf86i2c.c | 876 +++++++++++++++++++++++++++ nvclock/xf86i2c.h | 94 +++ nvclock/xf86i2c.o | Bin 0 -> 6136 bytes nvclock/xfree.h | 76 +++ wmtempnv/Makefile | 7 +- wmtempnv/wmtempnv_mask.xbm | 2 +- 54 files changed, 9003 insertions(+), 5 deletions(-) create mode 100644 nvclock/Makefile create mode 100644 nvclock/adt7473.c create mode 100644 nvclock/adt7473.o create mode 100644 nvclock/back_bsd.c create mode 100644 nvclock/back_linux.c create mode 100644 nvclock/back_linux.o create mode 100644 nvclock/back_win32.c create mode 100644 nvclock/backend.c create mode 100644 nvclock/backend.h create mode 100644 nvclock/backend.o create mode 100644 nvclock/bios.c create mode 100644 nvclock/bios.o create mode 100644 nvclock/config.c create mode 100644 nvclock/config.h create mode 100644 nvclock/config.o create mode 100644 nvclock/error.c create mode 100644 nvclock/error.o create mode 100644 nvclock/f75375.c create mode 100644 nvclock/f75375.h create mode 100644 nvclock/f75375.o create mode 100644 nvclock/i2c.c create mode 100644 nvclock/i2c.h create mode 100644 nvclock/i2c.o create mode 100644 nvclock/info.c create mode 100644 nvclock/info.o create mode 100644 nvclock/libbackend.a create mode 100644 nvclock/libc_wrapper.c create mode 100644 nvclock/libc_wrapper.o create mode 100644 nvclock/lm99.c create mode 100644 nvclock/lm99.o create mode 100644 nvclock/nv30.c create mode 100644 nvclock/nv30.o create mode 100644 nvclock/nv40.c create mode 100644 nvclock/nv40.o create mode 100644 nvclock/nv50.c create mode 100644 nvclock/nv50.o create mode 100644 nvclock/nvclock.h create mode 100644 nvclock/nvcontrol.c create mode 100644 nvclock/nvreg.h create mode 100644 nvclock/overclock.c create mode 100644 nvclock/overclock.o create mode 100644 nvclock/tete.c create mode 100644 nvclock/utils.c create mode 100644 nvclock/utils.o create mode 100644 nvclock/w83781d.c create mode 100644 nvclock/w83781d.o create mode 100644 nvclock/w83l785r.c create mode 100644 nvclock/w83l785r.o create mode 100644 nvclock/xf86i2c.c create mode 100644 nvclock/xf86i2c.h create mode 100644 nvclock/xf86i2c.o create mode 100644 nvclock/xfree.h diff --git a/nvclock/Makefile b/nvclock/Makefile new file mode 100644 index 0000000..98da800 --- /dev/null +++ b/nvclock/Makefile @@ -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: \ No newline at end of file diff --git a/nvclock/adt7473.c b/nvclock/adt7473.c new file mode 100644 index 0000000..5f0adfb --- /dev/null +++ b/nvclock/adt7473.c @@ -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 +#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); +} diff --git a/nvclock/adt7473.o b/nvclock/adt7473.o new file mode 100644 index 0000000000000000000000000000000000000000..c1f4cb14b602a5507ddcc4e4f97bd4d0c2b54c56 GIT binary patch literal 2284 zcma)7-)mb{9RFrXT)K5cMPzJ+A#I>K#oQ;SU#lAGCq zWndiaI)XnQ#6Mx+tJ|s%l57u6VGpf7g@Fdy!Z6!|TidHch@bDxIcaXSR=hYn2qW&8bFMrod zbr)SK7<=L02Y+6DEVtF`%#c?f$w+7JN6p;bNckAK-Rf5DfW5ZU!;}@Yr09EUihgGV zLY%_9S^UE%M7uUjb!)YtpCNdk1gsUAV65fR20LUd$iz*x`ny^!dk-S~+j`mC!}yOV zaICC$mshI(8*253Zcy)*6L;^NtCSPfE3*}Iqge5o#s1Bo0+(v1&64$J@rlZE+jx0(TSmq@6t>6f#$hz1uZ|EmW+$cDvu)eA zn{D?RnzXj`D2`95w}1rP8g_HmdZfD|-J*5zGSr_n?jASx6 zqP(sg8%PfaQ$xzo(O_yp9~e|dl+pf1ghGZELnCD_ zlOZLYY#7mGB7qd7EF}$HnSO6}AY*9JkGYO#8v)Sx0G|_d{`P$976w3klISqjUAt}y z`-rXzA3x7LxQO3Ywxz^vxqy#{)H{MT^JVyHJZ>G<+k?r2c|m*C_>p?teavG%j|bo> zjivp|F`knGZ6|&XNde(t4v!ZwWo>LT?=&yOkUU0N8;3$T$U!*yBgXq<`R6hD>{|6^ z_7XjWPY$UkZHcXZ5BBzn89zJ5|yh+OgHqXLCf*6hiEr?Q>SxVDr@>3S@ZP9+>7%|Pmt-9QHFj)17pT>A3TMvGEi>XG Wz7h5{VQ&bug#RS&g!x}Fg8v^Y;Xz~o literal 0 HcmV?d00001 diff --git a/nvclock/back_bsd.c b/nvclock/back_bsd.c new file mode 100644 index 0000000..16de178 --- /dev/null +++ b/nvclock/back_bsd.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "backend.h" + +#if __FreeBSD_version < 430000 +# include +#else +# include +#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;counterpc_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; +} diff --git a/nvclock/back_linux.c b/nvclock/back_linux.c new file mode 100644 index 0000000..277c9ec --- /dev/null +++ b/nvclock/back_linux.c @@ -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 /* needed for htonl */ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/nvclock/back_linux.o b/nvclock/back_linux.o new file mode 100644 index 0000000000000000000000000000000000000000..324bf3744851cd064854835df021daec649864e8 GIT binary patch literal 5044 zcmcgwe{5S<6~1=d#v2(<*Oql`Fn6^f0qUe%y0&OV?vh8t6nEX)v`DP;9Q!#ji+{*| z?%Hh?6xZtBjHQBLOi&mVLqdR5q=8z7M3XjBR;a`#G!zM#x~gcYVrgl~N(6}TefQnh zJii9PUp&R{yWhFzoO|xM_n!OOFKGjx^>{qOemtT@EHWW3+-mjoNazqB5PmUe{^f9E z3WC=tJnhBH+#T|nzR>hP7Zw&wZLV@?P6Vna*-Moxf5hvxcD(A^EA`lMb0E}Sk9{A| zR<94xWFX{sx~R;zl%JmL3;FJ(O;xHW@71w#u%^w~gVnU-)iI;>G-IB5T8=hTpyj)e zG8qW&Zo9E^(ih@rl3N3Yrx_>a)N1k7O4sJg;D|f ze@x7=iZ zr}6Lv&*2yLS%-deP4lNs?J|zSWoF?PsM-Ed2_2`stTB=;NVPJjbX9ZT75X`P6=I z$0O@?9lWVpH!6o3!WcmCa?jQ@n)`6pZ8J9eLTyx#`whKZ`MP$&siEp~O}l`Jp-3)N zCQ??Cc2yb%Q-jKY65_Xwtb1ai+1htPqU!35p7HwZ9 z+S|GR`~o}Iid^0fv(Pkq2i(fgT0;4pRDQ5onR*KgUZ>OVNWH#hJw))N-{G@j4LWv4rFb&B8CF=Y8&|E|{FMpA-uY`R^C)+YPbME*;9b}#`GZdR z?-u2Kl*5r_)a!a&--F#udrX{EZ5F4z zHrQsq**R{$ZfXrl8?{(_ebowNW)-((Ur63FYNz9>lvgXN0a~g~YtuLjwZZAS&uiVu zm4@}rQYxa&EIyOGpC%#P0k6qrO(Pl2)PuZD7p&8e6lN>PnAdY zk_eZA;S~PFjy(5- zBarRAy`K(tY}a!}J|FCfY>eE$zIb1-upzSHQ^7(;U*8?+iEQe$dW49S4rYzS5YQ-E zIAXVo`e?+^4;UhnN*Df!qE*i8l3}dUhYg zH}tyErhnE;KJ@px)s%1BhMS0b)aNsRZ}o`PlyCO&w@slG;`NOBrnfnD_^iOsj*Xmp zkFud2TaReR#;zl6Li`K@y5*M$<3p-so}B)A0z&MuouJ>*1Y3NZa-X=0(zp0D|LcVD zI!7S4vt!w`h-Ija*0U0hy%?42T0;AqFeUE<_u~_>A4t9t_-!myR`G4XR;KN`#m~Dq?+^Q7{FiM4B%g8dYcBqG@SVubpMi{bIbydT+PAqlKX&bh z{dc+eTJZHa%x?kduhZ2Zaq%8-FV^=Z_@e%2T>THb_#-YZhEQ(eqnTX!KqM)WBRZHW z)TrY_dfcwepDXQQsqICn71>0A@;K{jIp<`#{mIch>Q`=moJCorS;<8_o+;&%TeieY zrDP(P7ATF$Y(b=W|CM5tmv7{Suy;#f_1>MDy`CK|PyeN&s zA9dryg>pPmOpZvx@#DEfR_Ac3azP{$qlitE3dKy$NDEv$kLiijKt4AtMvQ!JRFrb6 zD_zp{r$oAlZaG)ZQ%=MR*qSnh7_^WW)=QaZbdg0KL{|1;Em%K1*cT`h5_wL&Ae$6_ zf*c!wrJi~R$x*bPC*Cdu_ci6OD*0I@f1kJ#XPoN}y$&M2dNvS0g!)LlLx_Dud;(R0 z^z(wk7b!;^KUVxCIXtd`#g5BJAE){3_7G|{lJ{xEF4xNPqy28H-Kkp6x_z83!#q4;@n z!7?i}*X#7_vZ6-a;o2K{aA=Kufz literal 0 HcmV?d00001 diff --git a/nvclock/back_win32.c b/nvclock/back_win32.c new file mode 100644 index 0000000..1e1e181 --- /dev/null +++ b/nvclock/back_win32.c @@ -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 +#include +#include +#include + +#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 +} diff --git a/nvclock/backend.c b/nvclock/backend.c new file mode 100644 index 0000000..dcad037 --- /dev/null +++ b/nvclock/backend.c @@ -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 +#include +#include + +#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(); +} diff --git a/nvclock/backend.h b/nvclock/backend.h new file mode 100644 index 0000000..e4633be --- /dev/null +++ b/nvclock/backend.h @@ -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 + +/* 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); + diff --git a/nvclock/backend.o b/nvclock/backend.o new file mode 100644 index 0000000000000000000000000000000000000000..9baeef26be237744cf1002c70d7c94475c8a952e GIT binary patch literal 1704 zcmbVMO=uHQ5T4CX6Sc-zizr1K(L#%MX>Foa5DGQ6{!j!dEffj)SvA=FkZeS~c!)hL zp&q;_h@j|6&qDDaUF)T{N-ti7T!hm0(u)KY;(WXBrJE`$4$OY@&3rR&-`l+0i(WdX zC<;ka$W5D!Xwqd@!WMMXcJk4<@#RW+Jy?nQjrZoaPIL93vQ)0RdzQ?q+lbblLt42! z#P!)x_ssmRQPZrs%&KeFO#}B)lL?DteI8Me+~NM(qyeA zBxis7cDAIvzOORwH+VZPg&!?m4{zuNc4apY4bIG$Iz-%Vc#f?6sMqV9^UXoD=ABa* z7O^vo;$!+nGGVod5v^TS*3ZoAJ}bKxY2>M{IPwQJ^AvF8y;hrZQNNX0T;Q=I!^5XT z-6L97&*eg4HKGpm7LJGV{c8WQP(H2o_Nig@WRG1TQj2#odOX3X7i_#Cqk=ZA>e_9c z)KpxLlbR?NNloT586Hh)Hm7Uq$k=#qQI98Y^1A z`w`@Z#~k|EA%_lem*8$jSk*BLT+TbnG4y}a^|1ycfH2Hc?a+Fn20taxO7bHh?v3Qo zz;?6CHNl}fk3g8z0i7dOxGB#`oPm!W`v|gwJLgS$iQaHdnw%%OL}%VZ8+DlnHva>F zJ68agr)YP%8(&XyFYT~H8e?qZ6WT900PlT>9X0G7b)4@b_QQgoVSMZ0(0yjV$PWDk zqcqHmNx}$zoxPhkSz?4f&nOM`yOJ=1-`~O?Y~lE=NrV0w`%|93lM(Y@GfG395_}D` zY)Va1_ExN*#Z$3-qEs9NOU4VSrfmO-Cb~&7Nh)f()euW(ujROsoX+w6%Ea@rjFxFs z300P)b(ReD$tc{2B8|wdm9la$m9lagzjlaS;fs+L_(b44fg3>H^jE>Z3-s|>ps$V5 q6(EWLTZy7TK9CEdJ_XK~AR%}Wyp6wy;1%#aL=S=OL@$ARiGBj%pF_I< literal 0 HcmV?d00001 diff --git a/nvclock/bios.c b/nvclock/bios.c new file mode 100644 index 0000000..8ee71fd --- /dev/null +++ b/nvclock/bios.c @@ -0,0 +1,1068 @@ +/* NVClock 0.8 - Linux overclocker for NVIDIA cards + * + * Copyright(C) 2001-2007 Roderick Colenbrander + * + * Copyright(C) 2005 Hans-Frieder Vogt + * NV40 bios parsing improvements (BIT parsing rewrite + performance table fixes) + * + * 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 + */ + +/* +TODO: +- support for parsing some other init/script tables +- support for pre-GeforceFX bioses +*/ + +#include "backend.h" +#include "nvclock.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define READ_BYTE(rom, offset) (rom[offset]&0xff) +#define READ_SHORT(rom, offset) ((rom[offset+1]&0xff) << 8 | (rom[offset]&0xff)) +#define READ_INT(rom, offset) ((rom[offset+3]&0xff) << 24 | (rom[offset+2]&0xff) << 16 | (rom[offset+1]&0xff) << 8 | (rom[offset]&0xff)) +#define READ_LONG(rom, offset) (READ_INT(rom, offset+4)<<32 | READ_INT(rom, offset)) + +static unsigned int locate(char *rom, char *str, int offset); +struct nvbios *read_bios(char *file); +static struct nvbios *parse_bios(char *rom); + +typedef struct +{ + unsigned char version; + unsigned char start; + unsigned char entry_size; + unsigned char num_entries; +} BitTableHeader; + +typedef struct +{ + unsigned char version; + unsigned char start; + unsigned char num_active_entries; + unsigned char offset; + unsigned char entry_size; + unsigned char num_entries; +} BitPerformanceTableHeader; + +/* Read a string from a given offset */ +static char* nv_read(char *rom, unsigned short offset) +{ + char *res = (char*)strdup(&rom[offset]); + short len=0; + short i; + len = strlen(res); + +/* + Currently we only use this function for reading the signon message. + The string ends with a '\n' which we don't want so remove it. +*/ + for(i=0; i>24) & 0xff, (version>>16) & 0xff, (version>>8) & 0xff, version&0xff, '\0'); + return (char*)strdup(res); +} + + +/* Parse the GeforceFX performance table */ +static void parse_nv30_performance_table(struct nvbios *bios, char *rom, int offset) +{ + short i, num_entries; + unsigned char start; + unsigned char size; + int tmp = 0; + + /* read how far away the start is */ + start = rom[offset]; + num_entries = rom[offset+2]; + size = rom[offset + 3]; + + bios->perf_entries=0; + offset += start + 1; + for(i=0; i < num_entries; i++) + { + bios->perf_lst[i].nvclk = (READ_INT(rom, offset))/100; + + /* The list can contain multiple distinct memory clocks. + / Later on the ramcfg register can tell which of the ones is the right one. + / But for now assume the first one is correct. It doesn't matter much if the + / clocks are a little lower/higher as we mainly use this to detect 3d clocks + / + / Further the clock stored here is the 'real' memory frequency, the effective one + / is twice as high. It doesn't seem to be the case for all bioses though. In some effective + / and real speed entries existed but this might be patched dumps. + */ + bios->perf_lst[i].memclk = (READ_INT(rom, offset+4))/50; + + /* Move behind the timing stuff to the fanspeed and voltage */ + bios->perf_lst[i].fanspeed = (float)(unsigned char)rom[offset + 54]; + bios->perf_lst[i].voltage = (float)(unsigned char)rom[offset + 55]/100; + /* In case the voltage is 0, assume the voltage is similar to the previous voltage */ + if(bios->perf_lst[i].voltage==0 && i>0) + bios->perf_lst[i].voltage = bios->perf_lst[i-1].voltage; + + bios->perf_entries++; + offset += size; + } +} + + +/* Convert the bios version which is stored in a numeric way to a string. +/ On NV40 bioses it is stored in 5 numbers instead of 4 which was the +/ case on old cards. The bios version on old cards could be bigger than +/ 4 numbers too but that version was only stored in a string which was +/ hard to locate. On NV40 cards the version is stored in a string too, +/ for which the offset can be found at +3 in the 'S' table. +*/ +static char *nv40_bios_version_to_str(char *rom, short offset) +{ + char res[15]; + int version = READ_INT(rom, offset); + unsigned char extra = rom[offset+4]; + + sprintf(res, "%02X.%02x.%02x.%02x.%02x%c", (version>>24) & 0xff, (version>>16) & 0xff, (version>>8) & 0xff, version&0xff, extra, '\0'); + return (char*)strdup(res); +} + + +/* Init script tables contain dozens of entries containing commands to initialize +/ the card. There are lots of different commands each having a different 'id' useally +/ most entries also have a different size. The task of this function is to move to the +/ next entry in the table. +*/ +static int bit_init_script_table_get_next_entry(char *rom, int offset) +{ + unsigned char id = rom[offset]; + int i=0; + + switch(id) + { + case '2': /* 0x32 */ + offset += 43; + break; + case '3': /* 0x33: INIT_REPEAT */ + offset += 2; + break; + case '6': /* 0x36: INIT_REPEAT_END */ + offset += 1; + break; + case '7': /* 0x37 */ + offset += 11; + break; + case '8': /* 0x38: INIT_NOT */ + offset += 1; + break; + case '9': /* 0x39 */ + offset += 2; + break; + case 'J': /* 0x4A */ + offset += 43; + break; + case 'K': /* 0x4B */ +#if DEBUG + /* +1 = PLL register, +5 = value */ + printf("'%c'\t%08x %08x\n", id, READ_INT(rom, offset+1), READ_INT(rom, offset+5)); +#endif + offset += 9; + break; + case 'M': /* 0x4D: INIT_ZM_I2C_BYTE */ +#if DEBUG + printf("'%c'\ti2c bytes: %x\n", id, rom[offset+3]); +#endif + offset += 4 + rom[offset+3]*2; + break; + case 'Q': /* 0x51 */ + offset += 5 + rom[offset+4]; + break; + case 'R': /* 0x52 */ + offset += 4; + break; + case 'S': /* 0x53: INIT_ZM_CR */ +#if DEBUG + /* +1 CRTC index (8-bit) + / +2 value (8-bit) + */ + printf("'%c'\tCRTC index: %x value: %x\n", id, rom[offset+1], rom[offset+2]); +#endif + offset += 3; + break; + case 'T': /* 0x54 */ + offset += 2 + rom[offset+1] * 2; + break; + case 'V': /* 0x56 */ + offset += 3; + break; + case 'X': /* 0x58 */ +#if DEBUG + { + /* +1 register base (32-bit) + / +5 number of values (8-bit) + / +6 value (32-bit) to regbase+4 + / .. */ + int base = READ_INT(rom, offset+1); + int number = (unsigned char)rom[offset+5]; + + printf("'%c'\tbase: %08x number: %d\n", id, base, number); + for(i=0; ipipe_cfg = val; + break; + case 0x4000: + bios->nvpll = val; + break; + case 0x4020: + bios->mpll = val; + break; + } + } + } + +#if DEBUG /* Read all init tables and print some debug info */ +/* Table 1 */ + offset = READ_SHORT(rom, init_offset); + + for(i=0; i<=len; i+=2) + { + /* Not all tables have to exist */ + if(!offset) + { + init_offset += 2; + offset = READ_SHORT(rom, init_offset); + continue; + } + + printf("Init script table %d\n", i/2+1); + id = rom[offset]; + + while(id != 'q') + { + /* Break out of the loop if we find an unknown entry id */ + if(!offset) + break; + + if(!(id == 'K' || id == 'n' || id == 'x' || id == 'y' || id == 'z')) + printf("'%c' (%x)\n", id, id); + offset = bit_init_script_table_get_next_entry(rom, offset); + id = rom[offset]; + } + + /* Pointer to next init table */ + init_offset += 2; + /* Get location of next table */ + offset = READ_SHORT(rom, init_offset); + } +#endif + +} + + +/* Parse the Geforce6/7/8 performance table */ +static void parse_bit_performance_table(struct nvbios *bios, char *rom, int offset) +{ + short i, entry; + unsigned char entry_size; + short nvclk_offset, memclk_offset, shader_offset, fanspeed_offset, voltage_offset; + BitPerformanceTableHeader *header = (BitPerformanceTableHeader*)&rom[offset]; + + /* The first byte contains a version number; based on this we set offsets to interesting entries */ + switch(header->version) + { + case 0x25: /* First seen on Geforce 8800GTS bioses */ + fanspeed_offset = 4; + voltage_offset = 5; + nvclk_offset = 8; + shader_offset = 10; + memclk_offset = 12; + break; + case 0x30: /* First seen on Geforce 8600GT bioses */ + fanspeed_offset = 6; + voltage_offset = 7; + nvclk_offset = 8; + shader_offset = 10; + memclk_offset = 12; + break; + case 0x35: /* First seen on Geforce 8800GT bioses; what else is different? */ + fanspeed_offset = 6; + voltage_offset = 7; + nvclk_offset = 8; + shader_offset = 10; + memclk_offset = 12; + break; + default: /* Default to this for all other bioses, I haven't seen issues yet for the entries we use */ + shader_offset = 0; + fanspeed_offset = 4; + voltage_offset = 5; + nvclk_offset = 6; + memclk_offset = 11; + } + + /* +5 contains the number of entries, +4 the size of one in bytes and +3 is some 'offset' */ + entry_size = header->offset + header->entry_size * header->num_entries; + + /* now read entries + / entries start with 0x20 for entry 0, 0x21 for entry 1, ... + */ + offset += header->start; + + for(entry=0, i=0; entrynum_active_entries; entry++) + { + /* On bios version 0x35, this 0x20, 0x21 .. pattern doesn't exist anymore; do the last 4 bits of the first byte tell if an entry is active on 0x35? */ + if ( (header->version != 0x35) && (rom[offset] & 0xf0) != 0x20) + { + break; + } + + bios->perf_lst[i].fanspeed = (unsigned char)rom[offset+fanspeed_offset]; + + bios->perf_lst[i].voltage = (float)(unsigned char)rom[offset+voltage_offset]/100; + /* In case the voltage is 0, assume the voltage is similar to the previous voltage */ + if(bios->perf_lst[i].voltage==0 && i>0) + bios->perf_lst[i].voltage = bios->perf_lst[i-1].voltage; + + /* HACK: My collection of bioses contains a (valid) 6600 bios with two 'bogus' entries at 0x21 (100MHz) and 0x22 (200MHz) + / these entries aren't the default ones for sure, so skip them until we have a better entry selection algorithm. + */ + if(READ_SHORT(rom, offset+nvclk_offset) > 200) + { + bios->perf_lst[i].nvclk = READ_SHORT(rom, offset+nvclk_offset); + + /* Support delta clock reading on some NV4X boards. The entries seem to be present on most Geforce7 boards but are as far as I know only used on 7800/7900 boards. + / On other boards the delta clocks are set to 0. Offset +8 contains the actual delta clock and offset +7 contains a divider for it. If the divider is 0 we don't read the delta clock. */ + if((get_gpu_arch(bios->device_id) & (NV47 | NV49)) && rom[offset+7]) + { + bios->perf_lst[i].delta = rom[offset+8]/rom[offset+7]; + bios->perf_lst[i].memclk = READ_SHORT(rom, offset+memclk_offset); + } + /* Geforce8 cards have a shader clock, further the memory clock is at a different offset as well */ + else if(get_gpu_arch(bios->device_id) & NV5X) + { + bios->perf_lst[i].shaderclk= READ_SHORT(rom, offset+shader_offset); + bios->perf_lst[i].memclk = READ_SHORT(rom, offset+memclk_offset); + } + else + bios->perf_lst[i].memclk = READ_SHORT(rom, offset+memclk_offset)*2; + + bios->perf_entries = i+1; + i++; + } + offset += entry_size; + } +} + +/* Parse the table containing pll programming limits */ +static void parse_bit_pll_table(struct nvbios *bios, char *rom, unsigned short offset) +{ + BitTableHeader *header = (BitTableHeader*)&rom[offset]; + int i; + + offset += header->start; + for(i=0; inum_entries; i++) + { + /* Each type of pll (corresponding to a certain register) has its own limits */ + bios->pll_lst[i].reg = READ_INT(rom, offset); + + /* Minimum/maximum frequency each VCO can generate */ + bios->pll_lst[i].VCO1.minFreq = READ_SHORT(rom, offset+4)*1000; + bios->pll_lst[i].VCO1.maxFreq = READ_SHORT(rom, offset+6)*1000; + bios->pll_lst[i].VCO2.minFreq = READ_SHORT(rom, offset+8)*1000; + bios->pll_lst[i].VCO2.maxFreq = READ_SHORT(rom, offset+0xa)*1000; + + /* Minimum/maximum input frequency for each VCO */ + bios->pll_lst[i].VCO1.minInputFreq = READ_SHORT(rom, offset+0xc)*1000; + bios->pll_lst[i].VCO1.maxInputFreq = READ_SHORT(rom, offset+0xe)*1000; + bios->pll_lst[i].VCO2.minInputFreq = READ_SHORT(rom, offset+0x10)*1000; + bios->pll_lst[i].VCO2.maxInputFreq = READ_SHORT(rom, offset+0x12)*1000; + + /* Low and high values for the dividers and multipliers */ + bios->pll_lst[i].VCO1.minN = READ_BYTE(rom, offset+0x14); + bios->pll_lst[i].VCO1.maxN = READ_BYTE(rom, offset+0x15); + bios->pll_lst[i].VCO1.minM = READ_BYTE(rom, offset+0x16); + bios->pll_lst[i].VCO1.maxM = READ_BYTE(rom, offset+0x17); + bios->pll_lst[i].VCO2.minN = READ_BYTE(rom, offset+0x18); + bios->pll_lst[i].VCO2.maxN = READ_BYTE(rom, offset+0x19); + bios->pll_lst[i].VCO2.minM = READ_BYTE(rom, offset+0x1a); + bios->pll_lst[i].VCO2.maxM = READ_BYTE(rom, offset+0x1b); + + bios->pll_lst[i].var1d = READ_BYTE(rom, offset+0x1d); + bios->pll_lst[i].var1e = READ_BYTE(rom, offset+0x1e); + +#if DEBUG + printf("register: %#08x\n", READ_INT(rom, offset)); + + /* Minimum/maximum frequency each VCO can generate */ + printf("minVCO_1: %d\n", READ_SHORT(rom, offset+4)); + printf("maxVCO_1: %d\n", READ_SHORT(rom, offset+6)); + printf("minVCO_2: %d\n", READ_SHORT(rom, offset+8)); + printf("maxVCO_2: %d\n", READ_SHORT(rom, offset+0xa)); + + /* Minimum/maximum input frequency for each VCO */ + printf("minVCO_1_in: %d\n", READ_SHORT(rom, offset+0xc)); + printf("minVCO_2_in: %d\n", READ_SHORT(rom, offset+0xe)); + printf("maxVCO_1_in: %d\n", READ_SHORT(rom, offset+0x10)); + printf("maxVCO_2_in: %d\n", READ_SHORT(rom, offset+0x12)); + + /* Low and high values for the dividers and multipliers */ + printf("N1_low: %d\n", READ_BYTE(rom, offset+0x14)); + printf("N1_high: %d\n", READ_BYTE(rom, offset+0x15)); + printf("M1_low: %d\n", READ_BYTE(rom, offset+0x16)); + printf("M1_high: %d\n", READ_BYTE(rom, offset+0x17)); + printf("N2_low: %d\n", READ_BYTE(rom, offset+0x18)); + printf("N2_high: %d\n", READ_BYTE(rom, offset+0x19)); + printf("M2_low: %d\n", READ_BYTE(rom, offset+0x1a)); + printf("M2_high: %d\n", READ_BYTE(rom, offset+0x1b)); + + /* What's the purpose of these? */ + printf("1c: %d\n", READ_BYTE(rom, offset+0x1c)); + printf("1d: %d\n", READ_BYTE(rom, offset+0x1d)); + printf("1e: %d\n", READ_BYTE(rom, offset+0x1e)); + printf("\n"); +#endif + + bios->pll_entries = i+1; + offset += header->entry_size; + } +} + +/* The internal gpu sensor most likely consists of a diode and a resistor. +/ The voltage across this resistor is meassured using a ADC. Since the +/ voltage-current relationship of a diode isn't linear the value needs some correction. +/ The temperature can be calculated by scaling the output value of the ADC and adding an offset +/ to it. +/ +/ This function reads the temperature table and reads the offset/scaling constants for the +/ temperature calculation formula. Before I didn't know where and how these values were stored and +/ used some hardcoded (wrong) values. I expected the values to be tored near the place where +/ the temperature sensor enable/disable bit was but I didn't have the time to figure it all out. +/ The code below is very similar to the code from the Rivatuner gpu diode by Alexey Nicolaychuk with a few adjustments. +/ Rivatuner's code didn't contain constants for the latest Geforce7 (NV46/NV49/NV4B) cards so I had to add those myself. +*/ +static void parse_bit_temperature_table(struct nvbios *bios, char *rom, int offset) +{ + short i; + BitTableHeader *header = (BitTableHeader*)&rom[offset]; + + switch(get_gpu_arch(bios->device_id)) + { + case NV43: + bios->sensor_cfg.diode_offset_mult = 32060; + bios->sensor_cfg.diode_offset_div = 1000; + bios->sensor_cfg.slope_mult = 792; + bios->sensor_cfg.slope_div = 1000; + break; + case NV44: + case NV47: + bios->sensor_cfg.diode_offset_mult = 27839; + bios->sensor_cfg.diode_offset_div = 1000; + bios->sensor_cfg.slope_mult = 780; + bios->sensor_cfg.slope_div = 1000; + break; + case NV46: /* are these really the default ones? they come from a 7300GS bios */ + bios->sensor_cfg.diode_offset_mult = -24775; + bios->sensor_cfg.diode_offset_div = 100; + bios->sensor_cfg.slope_mult = 467; + bios->sensor_cfg.slope_div = 10000; + break; + case NV49: /* are these really the default ones? they come from a 7900GT/GTX bioses */ + bios->sensor_cfg.diode_offset_mult = -25051; + bios->sensor_cfg.diode_offset_div = 100; + bios->sensor_cfg.slope_mult = 458; + bios->sensor_cfg.slope_div = 10000; + break; + case NV4B: /* are these really the default ones? they come from a 7600GT bios */ + bios->sensor_cfg.diode_offset_mult = -24088; + bios->sensor_cfg.diode_offset_div = 100; + bios->sensor_cfg.slope_mult = 442; + bios->sensor_cfg.slope_div = 10000; + break; + } + + offset += header->start; + for(i=0; inum_entries; i++) + { + unsigned char id = rom[offset]; + short value = READ_SHORT(rom, offset+1); + + switch(id) + { + /* The temperature section can store settings for more than just the builtin sensor. + / The value of 0x0 sets the channel for which the values below are meant. Right now + / we ignore this as we only use option 0x10-0x13 which are specific to the builtin sensor. + / Further what do 0x33/0x34 contain? Those appear on Geforce7300/7600/7900 cards. + */ + case 0x1: +#if DEBUG + printf("0x1: (%0x) %d 0x%0x\n", value, (value>>9) & 0x7f, value & 0x3ff); +#endif + if((value & 0x8f) == 0) + bios->sensor_cfg.temp_correction = (value>>9) & 0x7f; + break; + /* An id of 4 seems to correspond to a temperature threshold but 5, 6 and 8 have similar values, what are they? */ + case 0x4: + case 0x5: + case 0x6: + case 0x8: + /* printf("0x%x: 0x%x %d\n", id, value & 0xf, (value>>4) & 0x1ff); */ + break; + case 0x10: + bios->sensor_cfg.diode_offset_mult = value; + break; + case 0x11: + bios->sensor_cfg.diode_offset_div = value; + break; + case 0x12: + bios->sensor_cfg.slope_mult = value; + break; + case 0x13: + bios->sensor_cfg.slope_div = value; + break; +#if DEBUG + default: + printf("0x%x: %x\n", id, value); +#endif + } + offset += header->entry_size; + } +#if DEBUG + printf("temperature table version: %#x\n", header->version); + printf("correction: %d\n", bios->sensor_cfg.temp_correction); + printf("offset: %.3f\n", (float)bios->sensor_cfg.diode_offset_mult / (float)bios->sensor_cfg.diode_offset_div); + printf("slope: %.3f\n", (float)bios->sensor_cfg.slope_mult / (float)bios->sensor_cfg.slope_div); +#endif +} + +/* Read the voltage table for nv30/nv40/nv50 cards */ +static void parse_voltage_table(struct nvbios *bios, char *rom, int offset) +{ + unsigned char entry_size=0; + unsigned char start=0; + int i; + + /* In case of the first voltage table revision, there was no start pointer? */ + switch(rom[offset]) + { + case 0x10: + case 0x12: + start = 5; + entry_size = rom[offset+1]; + bios->volt_entries = rom[offset+2]; + bios->volt_mask = rom[offset+4]; + break; + default: + start = rom[offset+1]; + bios->volt_entries = rom[offset+2]; + entry_size = rom[offset+3]; + + /* The VID mask is stored right before the start of the first entry? */ + bios->volt_mask = rom[offset+start -1]; + } + + offset += start; + for(i=0; ivolt_entries; i++) + { + /* The voltage is stored in multiples of 10mV, scale it to V */ + bios->volt_lst[i].voltage = (float)(unsigned char)rom[offset] / 100; + bios->volt_lst[i].VID = rom[offset + 1]; + offset += entry_size; + } +} + + +static void nv5_parse(struct nvbios *bios, char *rom, unsigned short nv_offset) +{ + /* Go to the position containing the offset to the card name, it is 30 away from NV. */ + int offset = READ_SHORT(rom, nv_offset + 30); + bios->signon_msg = nv_read(rom, offset); +} + + +static void nv30_parse(struct nvbios *bios, char *rom, unsigned short nv_offset) +{ + unsigned short init_offset = 0; + unsigned short perf_offset=0; + unsigned short volt_offset=0; + + int offset = READ_SHORT(rom, nv_offset + 30); + bios->signon_msg = nv_read(rom, offset); + + init_offset = READ_SHORT(rom, nv_offset + 0x4d); + + volt_offset = READ_SHORT(rom, nv_offset + 0x98); + parse_voltage_table(bios, rom, volt_offset); + + perf_offset = READ_SHORT(rom, nv_offset + 0x94); + parse_nv30_performance_table(bios, rom, perf_offset); +} + + +static void parse_bit_structure(struct nvbios *bios, char *rom, unsigned int bit_offset) +{ + unsigned short init_offset=0; + unsigned short perf_offset=0; + unsigned short pll_offset=0; + unsigned short signon_offset=0; + unsigned short temp_offset=0; + unsigned short volt_offset=0; + unsigned short offset=0; + + struct bit_entry + { + unsigned char id[2]; /* first byte is ID, second byte sub-ID? */ + unsigned short len; /* size of data pointed to by offset */ + unsigned short offset; /* offset of data */ + } *entry; + + /* In older nvidia bioses there was some start position and at fixed positions from there offsets to various tables were stored. + / For Geforce6 bioses this is all different. There is still some start position (now called BIT) but offsets to tables aren't at fixed + / positions from the start. There's now some weird pattern which starts a few places from the start of the BIT section. + / This pattern seems to consist of a subset of the alphabet (all in uppercase). After each such token there is the length of the data + / referred to by the entry and an offset. The first entry "0x00 0x01" is probably somewhat different since the length/offset info + / seems to be a bit strange. The list ends with the entry "0x00 0x00" + */ + + /* skip 'B' 'I' 'T' '\0' */ + offset = bit_offset + 4; + + /* read the entries */ + while (1) + { + entry = (struct bit_entry *)&rom[offset]; + if ((entry->id[0] == 0) && (entry->id[1] == 0)) + break; + + switch (entry->id[0]) + { + case 'B': /* BIOS related data */ + bios->version = nv40_bios_version_to_str(rom, entry->offset); + break; + case 'C': /* Configuration table; it contains at least PLL parameters */ + pll_offset = READ_SHORT(rom, entry->offset + 8); + parse_bit_pll_table(bios, rom, pll_offset); + break; + case 'I': /* Init table */ + init_offset = READ_SHORT(rom, entry->offset); + parse_bit_init_script_table(bios, rom, init_offset, entry->len); + break; + case 'P': /* Performance related data */ + perf_offset = READ_SHORT(rom, entry->offset); + parse_bit_performance_table(bios, rom, perf_offset); + + temp_offset = READ_SHORT(rom, entry->offset + 0xc); + parse_bit_temperature_table(bios, rom, temp_offset); + + /* 0x10 behind perf_offset the voltage table offset is stored */ + volt_offset = READ_SHORT(rom, entry->offset + 0x10); + parse_voltage_table(bios, rom, volt_offset); + break; + case 'S': + /* table with string references of signon-message, + BIOS version, BIOS copyright, OEM string, VESA vendor, + VESA Product Name, and VESA Product Rev. + table consists of offset, max-string-length pairs + for all strings */ + signon_offset = READ_SHORT(rom, entry->offset); + bios->signon_msg = nv_read(rom, signon_offset); + break; + } + offset += sizeof(struct bit_entry); + } +} + + +static unsigned int locate(char *rom, char *str, int offset) +{ + int size = strlen(str); + int i; + char* data; + + /* We shouldn't assume this is allways 64kB */ + for(i=offset; i<0xffff; i++) + { + data = (char*)&rom[i]; + if(strncmp(data, str, size) == 0) + { + return i; + } + } + return 0; +} + + +#if DEBUG +int main(int argc, char **argv) +{ + read_bios("bios.rom"); + return 0; +} + + +#else +void dump_bios(char *filename) +{ + int i; + FILE *fp = NULL; + + /* enable bios parsing; on some boards the display might turn off */ + nv_card->PMC[0x1850/4] = 0x0; + + /* try to dump the bios */ + fp = fopen(filename, "w+"); + if(!fp) return; + + for(i=0; i < 0xffff; i++) + { + unsigned char data; + + /* On some 6600GT/6800LE boards bios there are issues with the rom. + / Normaly when you want to read data from lets say address X, you receive + / the data when it is ready. For some roms the outputs aren't "stable" yet when + / we want to read out the data. A workaround from Unwinder is to try to access the location + / several times in the hope that the outputs will become stable. In the case of instablity + / each fourth byte was wrong (needs to be shifted 4 to the left) and furhter there was some garbage + / + / A delay of 4 extra reads helps for most 6600GT cards but for 6800Go cards atleast 5 are needed. + */ + data = nv_card->PROM[i]; + data = nv_card->PROM[i]; + data = nv_card->PROM[i]; + data = nv_card->PROM[i]; + data = nv_card->PROM[i]; + fprintf(fp, "%c", data); + } + fclose(fp); + + /* disable the rom; if we don't do it the screens stays black on some cards */ + nv_card->PMC[0x1850/4] = 0x1; +} +#endif + +/* This function tries to read a copy of the bios from harddrive. If that doesn't + exist it will dump the bios and then read it. You might wonder why we don't read the bios from + card. The reason behind that is that some bioses are slow to read (can take seconds) and second on some + cards (atleast on my gf2mx) the screen becomes black if I enable reading of the rom. +*/ +struct nvbios *read_bios(char *file) +{ + int fd = 0; + char *rom = NULL; + struct nvbios *res; + + if((fd = open(file, O_RDONLY)) == -1) + return NULL; /* We might need to redump the bios */ + + rom = mmap(0, 0xffff, PROT_READ, MAP_SHARED, fd, 0); + if(!rom) + return NULL; /* We might need to redump the bios */ + + /* Do the actual bios parsing */ + res = parse_bios(rom); + + /* Close the bios */ + close(fd); + + return res; +} + + +struct nvbios *parse_bios(char *rom) +{ + unsigned short bit_offset = 0; + unsigned short nv_offset = 0; + unsigned short pcir_offset = 0; + unsigned short device_id = 0; + struct nvbios *bios; + int i=0; + + /* All bioses start with this '0x55 0xAA' signature */ + if((rom[0] != 0x55) || (rom[1] != (char)0xAA)) + return NULL; + + /* Fail when the PCIR header can't be found; it is present on all PCI bioses */ + if(!(pcir_offset = locate(rom, "PCIR", 0))) + return NULL; + + /* Fail if the bios is not from an Nvidia card */ + if(READ_SHORT(rom, pcir_offset + 4) != 0x10de) + return NULL; + + device_id = READ_SHORT(rom, pcir_offset + 6); + if(get_gpu_arch(device_id) & (NV4X | NV5X)) + { + /* For NV40 card the BIT structure is used instead of the BMP structure (last one doesn't exist anymore on 6600/6800le cards). */ + if(!(bit_offset = locate(rom, "BIT", 0))) + return NULL; + + bios = calloc(1, sizeof(struct nvbios)); + bios->device_id = device_id; + parse_bit_structure(bios, rom, bit_offset); + } + /* We are dealing with a card that only contains the BMP structure */ + else + { + int version; + + /* The main offset starts with "0xff 0x7f NV" */ + if(!(nv_offset = locate(rom, "\xff\x7fNV", 0))) + return NULL; + + /* We don't support old bioses. Mainly some old tnt1 models */ + if(rom[nv_offset + 5] < 5) + return NULL; + + bios = calloc(1, sizeof(struct nvbios)); + bios->device_id = device_id; + + bios->major = (char)rom[nv_offset + 5]; + bios->minor = (char)rom[nv_offset + 6]; + + /* Go to the bios version */ + /* Not perfect for bioses containing 5 numbers */ + version = READ_INT(rom, nv_offset + 10); + bios->version = bios_version_to_str(version); + + /* Use nv30_parse for all NV3X cards; for overclocking purposes the 5200 is considered + / a NV25 card but in this case it really is a NV3X board. + */ + if((get_gpu_arch(device_id) & NV3X) || ((device_id & 0xff0) == 0x320)) + nv30_parse(bios, rom, nv_offset); + else + nv5_parse(bios, rom, nv_offset); + } + +#if DEBUG + printf("signon_msg: %s\n", bios->signon_msg); + printf("bios: %s\n", bios->version); + printf("BMP version: %x.%x\n", bios->major, bios->minor); + for(i=0; i< bios->volt_entries; i++) + printf("volt: %.2fV\n", bios->volt_lst[i].voltage); + + for(i=0; i< bios->perf_entries; i++) + { + printf("gpu freq: %dMHz @ %.2fV\n", bios->perf_lst[i].nvclk, bios->perf_lst[i].voltage); + printf("mem freq: %dMHz\n", bios->perf_lst[i].memclk); + } + + if(bios) + { + int i; + printf("-- VideoBios information --\n"); + printf("Version: %s\n", bios->version); + printf("Signon message: %s\n", bios->signon_msg); + + for(i=0; i< bios->perf_entries; i++) + { + if(bios->volt_entries) + { + if(bios->perf_lst[i].delta) + /* For now assume the first memory entry is the right one; should be fixed as some bioses contain various different entries */ + printf("Performance level %d: gpu %d(+%d)MHz/memory %dMHz/ %.2fV / %d%%\n", i, bios->perf_lst[i].nvclk, bios->perf_lst[i].delta, bios->perf_lst[i].memclk, bios->perf_lst[i].voltage, bios->perf_lst[i].fanspeed); + else if(bios->perf_lst[i].shaderclk) + printf("Performance level %d: gpu %d/shader %dMHz/memory %dMHz/ %.2fV / %d%%\n", i, bios->perf_lst[i].nvclk, bios->perf_lst[i].shaderclk, bios->perf_lst[i].memclk, bios->perf_lst[i].voltage, bios->perf_lst[i].fanspeed); + else + printf("Performance level %d: gpu %dMHz/memory %dMHz/ %.2fV / %d%%\n", i, bios->perf_lst[i].nvclk, bios->perf_lst[i].memclk, bios->perf_lst[i].voltage, bios->perf_lst[i].fanspeed); + } + else + printf("Performance level %d: %dMHz / %dMHz / %d%%\n", i, bios->perf_lst[i].nvclk, bios->perf_lst[i].memclk, bios->perf_lst[i].fanspeed); + } + + if(bios->volt_entries) + printf("VID mask: %x\n", bios->volt_mask); + + for(i=0; i< bios->volt_entries; i++) + { + /* For now assume the first memory entry is the right one; should be fixed as some bioses contain various different entries */ + /* Note that voltage entries in general don't correspond to performance levels!! */ + printf("Voltage level %d: %.2fV, VID: %x\n", i, bios->volt_lst[i].voltage, bios->volt_lst[i].VID); + } + printf("\n"); + } +#endif + return bios; +} diff --git a/nvclock/bios.o b/nvclock/bios.o new file mode 100644 index 0000000000000000000000000000000000000000..424d7e57099fc04062b9f730ec2555b72cc225f1 GIT binary patch literal 11332 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 +#include +#include +#include + +#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; +} diff --git a/nvclock/config.h b/nvclock/config.h new file mode 100644 index 0000000..e69de29 diff --git a/nvclock/config.o b/nvclock/config.o new file mode 100644 index 0000000000000000000000000000000000000000..c45369d1c5e5af506a53edbc27ee928910045e65 GIT binary patch literal 9076 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 literal 0 HcmV?d00001 diff --git a/nvclock/error.c b/nvclock/error.c new file mode 100644 index 0000000..0248a36 --- /dev/null +++ b/nvclock/error.c @@ -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 +#include +#include +#include + +#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; +} diff --git a/nvclock/error.o b/nvclock/error.o new file mode 100644 index 0000000000000000000000000000000000000000..d4e05e148cc74aac21d8d5038de824eb6b45bbc8 GIT binary patch literal 1440 zcmbtU&ubGw6n<&aHd-w8M?vu5RHC+M#?;nYdMjyd4-%+QQLvQl?$|8N?u6M%FnH)8 z7mElUJbEcTdh^t)^iYC-hal8LJ(+_L-^C2b=Ab9zJ^6Y^Qax+0N)^M{=L0I9$kOZy@e0>7QqF>z%O;1s|^sRdsut>$r4?iQZVzr|Iw(ft}QcDh%e z&6t3$5XBYDGu=`AGmCGqY-;p8`LdYTF^Q)!yFnxow%;wRk^gVL0(z*0x%OSsLTdqCPXOwuP1ZIE8Tyo323lk_=68>G)$f=C>MQc`gjBB9s2V+iXmeX%E%b^Hx%#Y literal 0 HcmV?d00001 diff --git a/nvclock/f75375.c b/nvclock/f75375.c new file mode 100644 index 0000000..0486c1b --- /dev/null +++ b/nvclock/f75375.c @@ -0,0 +1,167 @@ +/* Fintek F75375 sensor module +/ Copyright(C) 2005, Matt Wright +*/ + +#include +#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; +} diff --git a/nvclock/f75375.h b/nvclock/f75375.h new file mode 100644 index 0000000..f036fd4 --- /dev/null +++ b/nvclock/f75375.h @@ -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) diff --git a/nvclock/f75375.o b/nvclock/f75375.o new file mode 100644 index 0000000000000000000000000000000000000000..257ddd79976ce1a2eed6f040b9e3d930ff432aa8 GIT binary patch literal 2784 zcma)7?`vCC7=F{-&bS5Qq)tWI@*!fU?RD#qwG@Q8_OuLi=w?thW}7CrUG37u>;u?&SVxEQ!@ZPb4tY6ERN0SrmbS z5m|h=wzgIh_m$^8uO~Dq?uRC)3RkD+0$8tNT{7xkeHQBW_A4CX_iymf1de+Jw4f)v zo-L%e`H99ONN#B6V1q$3Mg*^slV;&6=ltd~SGeB0y;}$vbsGgjwFT;({AaIi>P`dr zT6bjKlGs@iE2>$$yu^}NR-UK59!RWcZCrNSSP=7927cQ`=QRy3m&KGHHQMOSKQ zXbEl^TxeZzHtwqJqtex~UuZx-71G^^5Ug zP1&J>Shy_~%JpHmZ_SqL`Nw+efx0(k5iHj?N|!@3ej%jd9ue0`f} zJJGLlkKbor0P_V*w(%)-hx|`arqX*E8_fSd-ac?x4_{VyK(9%yfl99vYvwVZM-zCa zyL6uH_UTQfvk5K z=tr41rTr7|7uYJ^9oPnv=e(Hz5ZD6yptKthD+BvoY15GNYc}JQum;D6rP-gbY%6Zl z*`dw@hg-UjTCv2TbGAkJO(w?EiZpE5{ln?;e%nfV_J(3(*|cRP`ZMXI&K*p}G6@em z3k45b27D(oVp~o`H@$u)s!(zJw66Le+mKGC5|&~skqz5iMm34_nQF?O + * + * 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 +#include +#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; + } +} + diff --git a/nvclock/i2c.h b/nvclock/i2c.h new file mode 100644 index 0000000..5cc99c0 --- /dev/null +++ b/nvclock/i2c.h @@ -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 */ diff --git a/nvclock/i2c.o b/nvclock/i2c.o new file mode 100644 index 0000000000000000000000000000000000000000..b2ba87d3c8214b2944b56636319fd8dc3ae6320c GIT binary patch literal 6924 zcmd^@e{56N6~~Vq2Mlv-LsMWYZ9V3!X$x+CHi4owKwg10l!zr8?K(n;pCN1FShmBD zinPfLTP(wrPIRLie^8CBXr&TTDTyY!rNA1-*px}oRa&KbQYIx$L_IY_;m7Ko^WOK_ z&kwYsN!>rY(tDr#J@?*o&b{~fy_>wK)IVXf*~ln1DxjPqs-3O3RB9+87tNxk#NXqS zo>#?^tZgj3SU~hH-EZjq{g>Df?{-pb=HM%tOlG2R@LSxq&u;X|~g|Ne}gKvHogYkdjjMy`{jl2mS(LSlOsvRmGI~`XU^tHc&zTH4fk5w$)@S}Xp`; z>+jx^U|&Xx!w#P_C)4|tqJ%P;Y?@3eo|v%BK5cm6P|2or!ozeAG>rxm z4GT4sGtuDXiqVae6PYB#Ag*lsObnBe^01QT2AX&>IILV@R=Wq*U18@}l1loWl)ZqT z97@#tJjr!whKoH(-o!~d#Fc|rJQ!E5*n_>@S2DEyow$;=@zBQ<*X}nEql70bW5zD? z{$O8S#gwtp-l14U-bTMmYYnaMv7ri$hv%H5&n^#SZh0#?1+5!zZb^_`x;Wk}i6@W4 zurejF$FK2?*Tk4ovbHv%q{rW5Yu?yJoICqj=pGmuT*!?)PiL}e%9yTrL`ts2Y3|Eg zFfTh~C_eT~e7bQ*IO*71%7Zg0ZZZ~}6Whdc{D)d1W^*B%C!Wvms@a0Kux-e<-6>?GQ~SG#XI8OckNSN5r@ylIgD+-p2=vx5pA2k z5uYqd-ph7lJ$ept{b2HLG4-B4%y(JyM3Hao0d|szA7hKY6g^Vq7sH;1?S@GkmHd%3 zBsVNb^dHaf%qv`UFg{)G{=qCB!mG81Gs>iNciLF@z=j#|^P72jXK2>po)hds3Z#}f z%ke+(P|Luc^Qq4s2tOAzr&+fn`SK#BQaG0eMKI=nruGY|*#BbEdegZCP6^*JXK4_rN8 zd&F<+c0c#xw+%_!QY!hX;q^8OZ@E_P;k$h1j1stuXd9pE=xpPVpeW zUe)f>CYJ(gM!tPyc5Z5$!*yd0(pUO5Nd-lGNfDaTCa;U=Fc16!+wbH{=HcN>!kGOH zxrvUL42D1MmRHPn2mM3d?b(w5mxK5>Ipkgi9NpNaUe7M-@}e_Hfx(-bAX+oI=L^dgHcx9AlXz22fX zv3`voqL+BhWqm@H_U~Er0gL{XMgPX4-?Qk;7CpoCaLat}wdit-e$t`?7Ts;pFI)8M z7X3Sm{?MYYTC~UX6Oeq1E&6ebe%hiV7M-+cYAbK`x6)JJtnX}nt|`>ccNWcjvRJLg zR<^~WPz_zWlB0)OryVkgJ85!7d%JFlvZ>Y7BTAv|&8;nw0GzCfs4X#dWmmKz7NMQN znn!g5`(fJ#wN*T>2m~T3e?FvHZ&({@iy2anXX=vmz!`{#j+SM_$EC#iAEM3|hP-ALqjCRyiv{%Iy^h;xuEO8t&39aB1mhvfip^ zuq6}?t7@P*67C>V->f~nBWsUx26=X#qc$f?NV7Q`(`?K-k)XG1kf=t(t-|U{dvn~J z-5fY)wMM>03qtEHH^nw~$`aaa&PFyHvw=H011cFUdG7OC#D8{-ER!)LuR(G(8uENe zgM9OB{PCR+E(O=o9FF2_ND$;6t>`$0g(g?i}cQgo}y1 z&<_ch@L3yr8TfUuQRpXn8oE{Z2+tQfA}l4^0o^MsBlXV9;LZ-Q?ND~L`* zUl8&^`_IsS6;|9xcoaMa9tTf=gWxG}2s{g(122Fh;6?C#@B=Uf zj)G(0B$x)Lz-jOX$bXRO`wu!n7w7?tzy;t!& +#include +#include +#include "backend.h" +#include "nvclock.h" + +/* This list isn't used much for the speed ranges anymore */ +/* Mainly mobile gpu speeds are missing */ +const static struct pci_ids ids[] = +{ + { 0x20, "nVidia Riva TnT", DESKTOP }, + { 0x28, "nVidia Riva TnT 2 Pro", DESKTOP }, + { 0x2a, "nVidia Riva TnT 2", DESKTOP }, + { 0x2b, "nVidia Riva TnT 2", DESKTOP }, + { 0xa0, "nVidia Riva TnT 2 Aladdin (Integrated)", DESKTOP }, + { 0x2c, "nVidia Riva TnT 2 VANTA", DESKTOP }, + { 0x2d, "nVidia Riva TnT 2 M64", DESKTOP }, + { 0x2e, "nVidia Riva TnT 2 VANTA", DESKTOP }, + { 0x2f, "nVidia Riva TnT 2 VANTA", DESKTOP }, + { 0x29, "nVidia Riva TnT 2 Ultra", DESKTOP }, + { 0x100, "nVidia Geforce 256 SDR", DESKTOP }, + { 0x101, "nVidia Geforce 256 DDR", DESKTOP }, + { 0x103, "nVidia Quadro", DESKTOP }, + { 0x110, "nVidia Geforce 2 MX/MX400", DESKTOP }, + { 0x111, "nVidia Geforce 2 MX 100/200", DESKTOP }, + { 0x112, "nVidia Geforce 2 GO", MOBILE }, + { 0x113, "nVidia Quadro 2 MXR/EX/GO", MOBILE }, + { 0x1a0, "nVidia Geforce 2 MX integrated", NFORCE }, + { 0x150, "nVidia Geforce 2 GTS/PRO", DESKTOP }, + { 0x151, "nVidia Geforce 2 Ti", DESKTOP }, + { 0x152, "nVidia Geforce 2 Ultra", DESKTOP }, + { 0x153, "nVidia Quadro 2 Pro", DESKTOP }, + { 0x170, "nVidia Geforce 4 MX460", DESKTOP }, + { 0x171, "nVidia Geforce 4 MX440", DESKTOP }, + { 0x172, "nVidia Geforce 4 MX420", DESKTOP }, + { 0x173, "nVidia Geforce 4 MX440 SE", DESKTOP }, + { 0x174, "nVidia Geforce 4 440 Go", MOBILE }, + { 0x175, "nVidia Geforce 4 420 Go", MOBILE }, + { 0x176, "nVidia Geforce 4 420 Go 32M", MOBILE }, + { 0x177, "nVidia Geforce 4 460 Go", MOBILE }, + { 0x178, "nVidia Quadro 4 550 XGL", DESKTOP }, + { 0x179, "nVidia Geforce 4 440 Go 64M", MOBILE }, + { 0x17a, "nVidia Quadro 4 200/400 NVS", DESKTOP }, + { 0x17b, "nVidia Quadro 4 550 XGL", DESKTOP }, + { 0x17c, "nVidia Quadro 4 500 GoGL", MOBILE }, + { 0x17d, "nVidia Geforce 410 Go", MOBILE }, + { 0x180, "nVidia Geforce 4 MX440 8X", DESKTOP }, + { 0x181, "nVidia Geforce 4 MX440 8X", DESKTOP }, + { 0x182, "nVidia Geforce 4 MX440SE 8X", DESKTOP }, + { 0x185, "nVidia Geforce 4 MX4000", DESKTOP }, + { 0x183, "nVidia Geforce 4 MX420 8X", DESKTOP }, + { 0x186, "nVidia Geforce 4 Go", MOBILE }, + { 0x187, "nVidia Geforce 4 488 Go", MOBILE }, + { 0x188, "nVidia Quadro 4 580 XGL", DESKTOP }, + { 0x18a, "nVidia Quadro 4 280 NVS", DESKTOP }, + { 0x18b, "nVidia Quadro 4 380 XGL", DESKTOP }, + { 0x18c, "nVidia Quadro NVS 50 PCI", DESKTOP }, + { 0x18d, "nVidia Geforce 4 448 Go", MOBILE }, + { 0x1f0, "nVidia Geforce 4 MX integrated", NFORCE }, + { 0x200, "nVidia Geforce 3", DESKTOP }, + { 0x201, "nVidia Geforce 3 Titanium 200", DESKTOP }, + { 0x202, "nVidia Geforce 3 Titanium 500", DESKTOP }, + { 0x203, "nVidia Quadro DCC", DESKTOP }, + { 0x250, "nVidia Geforce 4 Ti 4600", DESKTOP }, + { 0x251, "nVidia Geforce 4 Ti 4400", DESKTOP }, + { 0x253, "nVidia Geforce 4 Ti 4200", DESKTOP }, + { 0x258, "nVidia Quadro 4 900 XGL", DESKTOP }, + { 0x259, "nVidia Quadro 4 750 XGL", DESKTOP }, + { 0x25a, "nVidia Quadro 4 600 XGL", DESKTOP }, + { 0x25b, "nVidia Quadro 4 700 XGL", DESKTOP }, + { 0x280, "nVidia Geforce 4 Ti 4800", DESKTOP }, + { 0x281, "nVidia Geforce 4 Ti 4200 8X", DESKTOP }, + { 0x282, "nVidia Geforce 4 Ti 4800SE", DESKTOP }, + { 0x286, "nVidia Geforce 4 4000 GO", MOBILE }, + { 0x288, "nVidia Quadro 4 980 XGL", DESKTOP }, + { 0x289, "nVidia Quadro 4 780 XGL", DESKTOP }, + { 0x28c, "nVidia Quadro 4 700 GoGL", MOBILE }, + { 0x300, "nVidia GeforceFX 5800", DESKTOP }, + { 0x301, "nVidia GeforceFX 5800 Ultra", DESKTOP }, + { 0x302, "nVidia GeforceFX 5800", DESKTOP }, + { 0x308, "nVidia QuadroFX 2000", DESKTOP }, + { 0x309, "nVidia QuadroFX 1000", DESKTOP }, + { 0x311, "nVidia GeforceFX 5600 Ultra", DESKTOP }, + { 0x312, "nVidia GeforceFX 5600", DESKTOP }, + { 0x314, "nVidia GeforceFX 5600XT", DESKTOP }, + { 0x316, "nVidia NV31", MOBILE }, + { 0x317, "nVidia NV31", MOBILE }, + { 0x318, "nVidia NV31GL", DESKTOP }, + { 0x319, "nVidia NV31GL", DESKTOP }, + { 0x31a, "nVidia GeforceFX Go 5600", MOBILE }, + { 0x31b, "nVidia GeforceFX Go 5650", MOBILE }, + { 0x31c, "nVidia QuadroFX Go700", MOBILE }, + { 0x31d, "NV31", MOBILE }, + { 0x31e, "NV31GL", MOBILE }, + { 0x31f, "NV31GL", MOBILE }, + { 0x321, "nVidia GeforceFX 5200 Ultra", DESKTOP }, + { 0x322, "nVidia GeforceFX 5200", DESKTOP }, + { 0x323, "nVidia GeforceFX 5200LE", DESKTOP }, + { 0x324, "nVidia GeforceFX Go 5200", MOBILE }, + { 0x325, "nVidia GeforceFX Go 5250", MOBILE }, + { 0x326, "nVidia GeforceFX 5500", DESKTOP }, + { 0x328, "nVidia GeForceFX Go5200 32M/64M", MOBILE }, + { 0x329, "nVidia GeForce FX 5200 (Mac)", MOBILE }, + { 0x32a, "nVidia Quadro NVS 280 PCI", DESKTOP }, + { 0x32b, "nVidia QuadroFX 500", DESKTOP }, + { 0x32c, "nVidia GeforceFX Go5300", MOBILE }, + { 0x32d, "nVidia GeforceFX Go5100", MOBILE }, + { 0x32f, "nVidia NV34GL", DESKTOP }, + { 0x330, "nVidia GeforceFX 5900 Ultra", DESKTOP }, + { 0x331, "nVidia GeforceFX 5900", DESKTOP }, + { 0x332, "nVidia GeforceFX 5900XT", DESKTOP }, + { 0x333, "nVidia GeforceFX 5950 Ultra", DESKTOP }, + { 0x334, "nVidia GeforceFX 5900ZT", DESKTOP }, + { 0x338, "nVidia QuadroFX 3000", DESKTOP }, + { 0x33f, "nVidia GeforceFX 700", DESKTOP }, + { 0x341, "nVidia GeforceFX 5700 Ultra", DESKTOP }, + { 0x342, "nVidia GeforceFX 5700", DESKTOP }, + { 0x343, "nVidia GeforceFX 5700LE", DESKTOP }, + { 0x344, "nVidia GeforceFX 5700VE", DESKTOP }, + { 0x345, "NV36", DESKTOP }, + { 0x347, "nVidia GeforceFX Go5700", MOBILE }, + { 0x348, "nVidia GeforceFX Go5700", MOBILE }, + { 0x349, "NV36", MOBILE }, + { 0x34b, "NV36", MOBILE }, + { 0x34c, "nVidia Quadro FX Go1000", MOBILE }, + { 0x34e, "nVidia QuadroFX 1100", DESKTOP }, + { 0x34f, "NV36GL", DESKTOP }, + { 0x2a0, "nVidia Xbox GPU", NFORCE }, + { 0x40, "nVidia Geforce 6800 Ultra", DESKTOP }, + { 0x41, "nVidia Geforce 6800", DESKTOP }, + { 0x42, "nVidia Geforce 6800LE", DESKTOP }, + { 0x43, "nVidia Geforce 6800XE", DESKTOP }, + { 0x44, "nVidia Geforce 6800XT", DESKTOP }, + { 0x45, "nVidia Geforce 6800GT", DESKTOP }, + { 0x46, "nVidia Geforce 6800GT", DESKTOP }, + { 0x47, "nVidia Geforce 6800GS", DESKTOP }, + { 0x48, "nVidia Geforce 6800XT", DESKTOP }, + { 0x49, "NV40GL", DESKTOP }, + { 0x4d, "nVidia QuadroFX 4400", DESKTOP }, + { 0x4e, "nVidia QuadroFX 4000", DESKTOP }, + { 0xc0, "nVidia Geforce 6800GS", DESKTOP }, + { 0xc1, "nVidia Geforce 6800", DESKTOP }, + { 0xc2, "nVidia Geforce 6800LE", DESKTOP }, + { 0xc3, "nVidia Geforce 6800XT", DESKTOP }, + { 0xc8, "nVidia Geforce Go 6800", MOBILE }, + { 0xc9, "nVidia Geforce Go 6800Ultra", MOBILE }, + { 0xcc, "nVidia QuadroFX Go 1400", MOBILE }, + { 0xcd, "nVidia QuadroFX 3350/4000SDI", DESKTOP }, + { 0xce, "nVidia QuadroFX 1400", DESKTOP }, + { 0xf0, "nVidia Geforce 6800/Ultra", DESKTOP }, + { 0xf1, "nVidia Geforce 6600/GT", DESKTOP }, + { 0xf2, "nVidia Geforce 6600", DESKTOP }, + { 0xf3, "nVidia Geforce 6200", DESKTOP }, + { 0xf4, "nVidia Geforce 6600LE", DESKTOP }, + { 0xf5, "nVidia Geforce 7800GS", DESKTOP }, + { 0xf6, "nVidia Geforce 6800GS/XT", DESKTOP }, + { 0xf8, "nVidia QuadroFX 3400", DESKTOP }, + { 0xf9, "nVidia Geforce 6800 Ultra", DESKTOP }, + { 0xfa, "nVidia GeforcePCX 5750", DESKTOP }, + { 0xfb, "nVidia GeforcePCX 5900", DESKTOP }, + { 0xfc, "nVidia GeforcePCX 5300 / Quadro FX330", DESKTOP }, + { 0xfd, "nVidia Quadro NVS280/FX330", DESKTOP }, + { 0xfe, "nVidia QuadroFX 1300", DESKTOP }, + { 0xff, "nVidia GeforcePCX 4300", DESKTOP }, + { 0x140, "nVidia Geforce 6600GT", DESKTOP }, + { 0x141, "nVidia Geforce 6600", DESKTOP }, + { 0x142, "nVidia Geforce 6600LE", DESKTOP }, + { 0x143, "NV43", DESKTOP }, + { 0x144, "nVidia Geforce Go 6600", MOBILE }, + { 0x145, "nVidia Geforce 6610XL", DESKTOP }, + { 0x146, "nVidia GeForce Go 6600TE/6200TE", MOBILE }, + { 0x147, "nVidia Geforce 6700XL", DESKTOP }, + { 0x148, "nVidia Geforce Go 6600", MOBILE }, + { 0x149, "nVidia Geforce Go 6600GT", MOBILE }, + { 0x14a, "NV43", DESKTOP }, + { 0x14b, "NV43", DESKTOP }, + { 0x14c, "nVidia QuadroFX 550", DESKTOP }, + { 0x14d, "nVidia QuadroFX 550", DESKTOP }, + { 0x14e, "nVidia QuadroFX 540", DESKTOP }, + { 0x14f, "nVidia Geforce 6200", DESKTOP }, + { 0x160, "nVidia NV44", DESKTOP }, + { 0x161, "nVidia Geforce 6200 TurboCache", DESKTOP }, + { 0x162, "nVidia Geforce 6200SE TurboCache", DESKTOP }, + { 0x163, "NV44", DESKTOP }, + { 0x164, "nVidia Geforce Go 6200", MOBILE }, + { 0x165, "nVidia Quadro NVS 285", DESKTOP }, + { 0x166, "nVidia Geforce Go 6400", MOBILE }, + { 0x167, "nVidia Geforce Go 6200", MOBILE }, + { 0x168, "nVidia Geforce Go 6400", MOBILE }, + { 0x169, "nVidia Geforce 6250", DESKTOP }, + { 0x16a, "nVidia Geforce 7100GS", DESKTOP }, + { 0x16b, "NV44GLM", DESKTOP }, + { 0x16c, "NV44GLM", DESKTOP }, + { 0x16d, "NV44GLM", DESKTOP }, + { 0x16e, "NV44GLM", DESKTOP }, + { 0x210, "NV48", DESKTOP }, + { 0x211, "nVidia Geforce 6800", DESKTOP }, + { 0x212, "nVidia Geforce 6800LE", DESKTOP }, + { 0x215, "nVidia Geforce 6800GT", DESKTOP }, + { 0x218, "nVidia Geforce 6800XT", DESKTOP }, + { 0x220, "Unknown NV44", DESKTOP }, + { 0x221, "nVidia Geforce 6200", DESKTOP }, + { 0x222, "nVidia Geforce 6200 A-LE", DESKTOP }, + { 0x228, "NV44M", MOBILE }, + { 0x240, "nVidia Geforce 6150", NFORCE }, + { 0x241, "nVidia Geforce 6150LE", NFORCE }, + { 0x242, "nVidia Geforce 6100", NFORCE }, + { 0x244, "nVidia Geforce Go 6150", NFORCE }, + { 0x245, "nVidia Quadro NVS 210S / Geforce 6150LE", NFORCE }, + { 0x247, "nVidia Geforce Go 6100", NFORCE }, + { 0x2dd, "nVidia Unknown NV4x", DESKTOP }, + { 0x2de, "nVidia Unknown NV4x", DESKTOP }, + { 0x90, "nVidia G70", DESKTOP }, + { 0x91, "nVidia Geforce 7800GTX", DESKTOP }, + { 0x92, "nVidia Geforce 7800GT", DESKTOP }, + { 0x93, "nVidia Geforce 6800GS", DESKTOP }, + { 0x94, "nVidia G70", DESKTOP }, + { 0x98, "nVidia G70", DESKTOP }, + { 0x99, "nVidia Geforce Go 7800GTX", MOBILE }, + { 0x9c, "nVidia G70", DESKTOP }, + { 0x9d, "nVidia QuadroFX 4500", DESKTOP }, + { 0x9e, "nVidia G70GL", DESKTOP }, + { 0x290, "nVidia Geforce 7900GTX", DESKTOP }, + { 0x291, "nVidia Geforce 7900GT", DESKTOP }, + { 0x292, "nVidia Geforce 7900GS", DESKTOP }, + { 0x293, "nVidia Geforce 7950GX2", DESKTOP }, + { 0x294, "nVidia Geforce 7950GX2", DESKTOP }, + { 0x295, "nVidia Geforce 7950GT", DESKTOP }, + { 0x297, "nVidia Geforce Go 7950GTX", MOBILE }, + { 0x298, "nVidia Geforce Go 7900GS", MOBILE }, + { 0x299, "nVidia Geforce Go 7900GTX", MOBILE }, + { 0x29a, "nVidia QuadroFX 2500M", MOBILE }, + { 0x29b, "nVidia QuadroFX 1500M", MOBILE }, + { 0x29c, "nVidia QuadroFX 5500", DESKTOP }, + { 0x29d, "nVidia QuadroFX 3500", DESKTOP }, + { 0x29e, "nVidia QuadroFX 1500", DESKTOP }, + { 0x29f, "nVidia QuadroFX 4500 X2", DESKTOP }, + { 0x390, "nVidia Geforce 7650GS", DESKTOP }, + { 0x391, "nVidia Geforce 7600GT", DESKTOP }, + { 0x392, "nVidia Geforce 7600GS", DESKTOP }, + { 0x393, "nVidia Geforce 7300GT", DESKTOP }, + { 0x394, "nVidia Geforce 7600LE", DESKTOP }, + { 0x395, "nVidia Geforce 7300GT", DESKTOP }, + { 0x397, "nVidia Geforce Go 7700", MOBILE }, + { 0x398, "nVidia Geforce Go 7600", MOBILE }, + { 0x399, "nVidia Geforce Go 7600GT", MOBILE }, + { 0x39a, "nVidia Quadro NVS 300M", MOBILE }, + { 0x39b, "nVidia Geforce Go 7900SE", MOBILE }, + { 0x39c, "nVidia QuadroFX 550M", MOBILE }, + { 0x39e, "nVidia QuadroFX 560", DESKTOP }, + { 0x2e0, "nVidia Geforce 7600GT", DESKTOP }, + { 0x2e1, "nVidia Geforce 7600GS", DESKTOP }, + { 0x2e2, "nVidia Geforce 7300GT", DESKTOP }, + { 0x2e4, "nVidia Geforce 7950GT", DESKTOP }, + { 0x1d1, "nVidia Geforce 7300LE", DESKTOP }, + { 0x1d3, "nVidia Geforce 7300SE", DESKTOP }, + { 0x1d7, "nVidia Geforce Go 7300", MOBILE }, + { 0x1d8, "nVidia Geforce Go 7400", MOBILE }, + { 0x1d9, "nVidia Geforce Go 7400GS", MOBILE }, + { 0x1da, "nVidia Quadro NVS 110M", MOBILE }, + { 0x1db, "nVidia Quadro NVS 120M", MOBILE }, + { 0x1dc, "nVidia QuadroFX 350M", MOBILE }, + { 0x1dd, "nVidia Geforce 7500LE", MOBILE }, + { 0x1de, "nVidia QuadroFX 350", DESKTOP }, + { 0x1df, "nVidia Geforce 7300GS", DESKTOP }, + { 0x190, "nVidia Geforce 8800", DESKTOP }, + { 0x191, "nVidia Geforce 8800GTX", DESKTOP }, + { 0x192, "nVidia Geforce 8800", DESKTOP }, + { 0x193, "nVidia Geforce 8800GTS", DESKTOP }, + { 0x194, "nVidia Geforce 8800Ultra", DESKTOP }, + { 0x197, "nVidia Geforce 8800", DESKTOP }, + { 0x19a, "nVidia G80-875", DESKTOP }, + { 0x19d, "nVidia QuadroFX 5600", DESKTOP }, + { 0x19e, "nVidia QuadroFX 4600", DESKTOP }, + { 0x400, "nVidia Geforce 8600GTS", DESKTOP }, + { 0x401, "nVidia Geforce 8600GT", DESKTOP }, + { 0x402, "nVidia Geforce 8600GT", DESKTOP }, + { 0x403, "nVidia Geforce 8600GS", DESKTOP }, + { 0x404, "nVidia Geforce 8400GS", DESKTOP }, + { 0x405, "nVidia Geforce 9500M GS", MOBILE }, + { 0x406, "nVidia Geforce NB9P-GE", MOBILE }, + { 0x407, "nVidia Geforce 8600M GT", MOBILE }, + { 0x408, "nVidia Geforce 8600M GTS", MOBILE }, + { 0x409, "nVidia Geforce 8700M GT", MOBILE }, + { 0x40a, "nVidia Quadro NVS 370M", MOBILE }, + { 0x40b, "nVidia Quadro NVS 320M", MOBILE }, + { 0x40c, "nVidia QuadroFX 570M", MOBILE }, + { 0x40d, "nVidia QuadroFX 1600M", MOBILE }, + { 0x40e, "nVidia QuadroFX 570", DESKTOP }, + { 0x40f, "nVidia QuadroFX 1700", DESKTOP }, + { 0x420, "nVidia Geforce 8400SE", DESKTOP }, + { 0x421, "nVidia Geforce 8500GT", DESKTOP }, + { 0x422, "nVidia Geforce 8400GS", DESKTOP }, + { 0x423, "nVidia Geforce 8300GS", DESKTOP }, + { 0x424, "nVidia Geforce 8400GS", DESKTOP }, + { 0x425, "nVidia Geforce 8600M GS", MOBILE }, + { 0x426, "nVidia Geforce 8400M GT", MOBILE }, + { 0x427, "nVidia Geforce 8400M GS", MOBILE }, + { 0x428, "nVidia Geforce 8400M G", MOBILE }, + { 0x429, "nVidia Quadro NVS 140M", MOBILE }, + { 0x42a, "nVidia Quadro NVS 130M", MOBILE }, + { 0x42b, "nVidia Quadro NVS 135M", MOBILE }, + { 0x42d, "nVidia Quadro FX 360M", MOBILE }, + { 0x42e, "nVidia Geforce 9300M G", MOBILE }, + { 0x42f, "nVidia Quadro NVS 290", DESKTOP }, + { 0x3d0, "nVidia Geforce 6100 nForce 430", NFORCE}, + { 0x3d1, "nVidia Geforce 6100 nForce 405", NFORCE}, + { 0x3d2, "nVidia Geforce 6100 nForce 400", NFORCE}, + { 0x3d5, "nVidia Geforce 6100 nForce 420", NFORCE}, + { 0x53a, "nVidia Geforce 7050PV nForce 630a", NFORCE}, + { 0x53b, "nVidia Geforce 7050PV nForce 630a", NFORCE}, + { 0x53e, "nVidia Geforce 7025 nForce 630a", NFORCE}, + { 0x600, "nVidia Geforce 8800GTS 512", DESKTOP }, + { 0x609, "nVidia Geforce 8800M GTS", MOBILE }, + { 0x611, "nVidia Geforce 8800GT", DESKTOP }, + { 0x61a, "nVidia QuadroFX 3700", DESKTOP }, + { 0x6e0, "nVidia G98", DESKTOP }, + { 0x6e1, "nVidia G98", DESKTOP }, + { 0x6e2, "nVidia Geforce 8400", DESKTOP }, + { 0x6e3, "nVidia G98", DESKTOP }, + { 0x6e4, "nVidia Geforce 8400GS", DESKTOP }, + { 0x6e5, "nVidia G98", DESKTOP }, + { 0x6e6, "nVidia G98", DESKTOP }, + { 0x6e7, "nVidia G98", DESKTOP }, + { 0x6e8, "nVidia G98", DESKTOP }, + { 0x6e9, "nVidia G98", DESKTOP }, + { 0x6ea, "nVidia G98", DESKTOP }, + { 0x6eb, "nVidia G98", DESKTOP }, + { 0x6ec, "nVidia G98", DESKTOP }, + { 0x6ed, "nVidia G98", DESKTOP }, + { 0x6ee, "nVidia G98", DESKTOP }, + { 0x6ef, "nVidia G98", DESKTOP }, + { 0x6f0, "nVidia G98", DESKTOP }, + { 0x6f1, "nVidia G98", DESKTOP }, + { 0x6f2, "nVidia G98", DESKTOP }, + { 0x6f3, "nVidia G98", DESKTOP }, + { 0x6f4, "nVidia G98", DESKTOP }, + { 0x6f5, "nVidia G98", DESKTOP }, + { 0x6f6, "nVidia G98", DESKTOP }, + { 0x6f7, "nVidia G98", DESKTOP }, + { 0x6f8, "nVidia G98", DESKTOP }, + { 0x6f9, "nVidia G98", DESKTOP }, + { 0x6fa, "nVidia G98", DESKTOP }, + { 0x6fb, "nVidia G98", DESKTOP }, + { 0x6fc, "nVidia G98", DESKTOP }, + { 0x6fd, "nVidia G98", DESKTOP }, + { 0x6fe, "nVidia G98", DESKTOP }, + { 0x6ff, "nVidia G98", DESKTOP }, + { 0, NULL, UNKNOWN } +}; + +const char *get_card_name(int device_id, gpu_type *gpu) +{ + struct pci_ids *nv_ids = (struct pci_ids*)ids; + + while(nv_ids->id != 0) + { + if(nv_ids->id == device_id) + { + *gpu = nv_ids->gpu; + return nv_ids->name; + } + + nv_ids++; + } + + /* if !found */ + *gpu = UNKNOWN; + return "Unknown Nvidia card"; +} + +/* Internal gpu architecture function which sets +/ a device to a specific architecture. This architecture +/ doesn't have to be the real architecture. It is mainly +/ used to choose codepaths inside nvclock. +*/ +int get_gpu_arch(int device_id) +{ + int arch; + switch(device_id & 0xff0) + { + case 0x20: + arch = NV5; + break; + case 0x100: + case 0x110: + case 0x150: + case 0x1a0: + arch = NV10; + break; + case 0x170: + case 0x180: + case 0x1f0: + arch = NV17; + break; + case 0x200: + arch = NV20; + break; + case 0x250: + case 0x280: + case 0x320: /* We don't treat the FX5200/FX5500 as FX cards */ + arch = NV25; + break; + case 0x300: + arch = NV30; + break; + case 0x330: + arch = NV35; /* Similar to NV30 but fanspeed stuff works differently */ + break; + /* Give a seperate arch to FX5600/FX5700 cards as they need different code than other FX cards */ + case 0x310: + case 0x340: + arch = NV31; + break; + case 0x40: + case 0x120: + case 0x130: + case 0x210: + case 0x230: + arch = NV40; + break; + case 0xc0: + arch = NV41; + break; + case 0x140: + arch = NV43; /* Similar to NV40 but with different fanspeed code */ + break; + case 0x160: + case 0x220: + arch = NV44; + break; + case 0x1d0: + arch = NV46; + break; + case 0x90: + arch = NV47; + break; + case 0x290: + arch = NV49; /* 7900 */ + break; + case 0x390: + arch = NV4B; /* 7600 */ + break; + case 0x190: + arch = NV50; /* 8800 'NV50 / G80' */ + break; + case 0x400: /* 8600 'G84' */ + arch = G84; + break; + case 0x420: /* 8500 'G86' */ + arch = G86; + break; + case 0x600: /* G92 */ + case 0x610: /* G92 */ + case 0x6e0: /* G98 */ + case 0x6f0: /* G98 */ + arch = G86; + break; + case 0x240: + case 0x3d0: /* not sure if this is a C51 too */ + case 0x530: /* not sure if the 70xx is C51 too */ + arch = C51; + break; + case 0x2e0: + case 0xf0: + /* The code above doesn't work for pci-express cards as multiple architectures share one id-range */ + switch(device_id) + { + case 0xf0: /* 6800 */ + case 0xf9: /* 6800Ultra */ + arch = NV40; + break; + case 0xf6: /* 6800GS/XT */ + arch = NV41; + break; + case 0xf1: /* 6600/6600GT */ + case 0xf2: /* 6600GT */ + case 0xf3: /* 6200 */ + case 0xf4: /* 6600LE */ + arch = NV43; + break; + case 0xf5: /* 7800GS */ + arch = NV47; + break; + case 0xfa: /* PCX5700 */ + arch = NV31; + break; + case 0xf8: /* QuadroFX 3400 */ + case 0xfb: /* PCX5900 */ + arch = NV35; + break; + case 0xfc: /* PCX5300 */ + case 0xfd: /* Quadro NVS280/FX330, FX5200 based? */ + case 0xff: /* PCX4300 */ + arch = NV25; + break; + case 0xfe: /* Quadro 1300, has the same id as a FX3000 */ + arch = NV35; + break; + case 0x2e0: /* Geforce 7600GT AGP (at least Leadtek uses this id) */ + case 0x2e1: /* Geforce 7600GS AGP (at least BFG uses this id) */ + case 0x2e2: /* Geforce 7300GT AGP (at least a Galaxy 7300GT uses this id) */ + arch = NV4B; + break; + case 0x2e4: /* Geforce 7950 GT AGP */ + arch = NV49; + break; + } + break; + default: + arch = UNKNOWN; + } + return arch; +} + +/* Receive the real gpu architecture */ +static short get_gpu_architecture() +{ + return (nv_read_pmc(NV_PMC_BOOT_0) >> 20) & 0xff; +} + +/* Receive the gpu revision */ +static short get_gpu_revision() +{ + return nv_read_pmc(NV_PMC_BOOT_0) & NV_PMC_BOOT_0_REVISION_MASK; +} + +/* Retrieve the 'real' PCI id from the card */ +static short get_gpu_pci_id() +{ + return nv_read_pbus16(PCI_DEVICE_ID); +} + +static int set_gpu_pci_id(short id) +{ + if(nv_card->arch & (NV10 | NV20)) + { + /* The first two bits of the pci id can be changed. They are stored in bit 13-12 of PEXTDEV_BOOT0 */ + int pextdev_boot0 = nv_card->PEXTDEV[0x0/4] & ~(0x3 << 12); + /* Only the first 2 bits can be changed on these GPUs */ + if(id > 3) + return 0; + + nv_card->PEXTDEV[0x0/4] = pextdev_boot0 | ((id & 0x3) << 12); + nv_card->device_id = get_gpu_pci_id(); + nv_card->card_name = (char*)get_card_name(nv_card->device_id, &nv_card->gpu); + return 1; + } + /* Don't allow modding on cards using bridges (0xf*)! */ + else if((nv_card->arch & (NV17 | NV25 | NV3X | NV4X)) && ((nv_card->device_id & 0xfff0) != 0xf0)) + { + /* The first four bits of the pci id can be changed. The first two bits are stored in bit 13-12 of PEXTDEV_BOOT0, bit 3 and 4 are stored in bit 21-20 */ + int pextdev_boot0 = nv_card->PEXTDEV[0x0/4] & ~((0x3 << 20) | (0x3 << 12)); + /* The first 4 bits can be changed */ + if(id > 16) + return 0; + + /* On various NV4x cards the quadro capability bit in PBUS_DEBUG1 is locked. It can be unlocked by setting the first bit in 0xc020/0xc028 */ + nv_card->PMC[0xc020/4] = 1; + nv_card->PMC[0xc028/4] = 1; + + nv_card->PEXTDEV[0x0/4] = pextdev_boot0 | (((id>>2) & 0x3) << 20) | ((id & 0x3) << 12); + nv_card->device_id = get_gpu_pci_id(); + nv_card->card_name = (char*)get_card_name(nv_card->device_id, &nv_card->gpu); + return 1; + } + return 0; +} + +/* Function to read a single byte from pci configuration space */ +static void read_byte(int offset, unsigned char *data) +{ + /* The original plan was to read the PCI configuration directly from registers 0x1800 and upwards + / from the card itself. Although this is a fully correct way, it doesn't work for some cards using + / a PCI-Express -> AGP bridge. If I would read the registers from the card they would include PCI-Express + / as one of the capabilities. Reading using the "normal" way results in AGP as one of the capabilities. + / To correctly show that a card uses AGP we need to read the modded config space. + */ + *data = pciReadLong(nv_card->devbusfn,offset) & 0xff; +} + +/* Check the videocard for a certain PCI capability like AGP/PCI-Express/PowerManagement. +/ If a certain capability is supported return the position of the cap pointer. +*/ +static int pci_find_capability(unsigned char cap) +{ + unsigned char pos, id; + + read_byte(PCI_CAPABILITY_LIST, &pos); + + while(pos >= 0x40) + { + pos &= ~3; + read_byte(pos + PCI_CAP_LIST_ID, &id); + if(id == 0xff) + break; + if(id == cap) + return pos; /* Return the position of the cap pointer */ + + read_byte(pos + PCI_CAP_LIST_NEXT, &pos); + } + return 0; +} + +/* Check the videocard for a certain PCI capability like AGP/PCI-Express/PowerManagement. +/ If a certain capability is supported return the position of the cap pointer. +*/ +static int nv_pci_find_capability(unsigned char cap) +{ + unsigned char pos, id; + + pos = nv_read_pbus8(PCI_CAPABILITY_LIST); + + while(pos >= 0x40) + { + pos &= ~3; + id = nv_read_pbus8(pos + PCI_CAP_LIST_ID); + if(id == 0xff) + break; + if(id == cap) + return pos; /* Return the position of the cap pointer */ + + pos = nv_read_pbus8(pos + PCI_CAP_LIST_NEXT); + } + return 0; +} + +static char* get_bus_type() +{ + /* The pci header contains lots of information about a device like + / what type of device it is, who the vendor is and so on. It also + / contains a list of capabilities. Things like AGP, power management, + / PCI-X and PCI-Express are considered capabilities. We could check + / these capabilities to find out if the card is AGP or PCI-Express. + / + / Reading the bus type from the pci header would be a nice way but + / unfortunately there are some issues. One way to do the reading + / is to read the information directly from the card (from PMC). + / This doesn't work as some PCI-Express boards (6600GT) actually use + / PCI-Express GPUs connected to some bridge chip on AGP boards (same device id!). + / If you read directly from the card it will advertise PCI-Express instead of AGP. + / There is also another way to read the pci header for instance on Linux + / using from files in /proc/bus/pci but non-root users can only read + / a small part of the file. Most of the time the info we need isn't in + / the readable part. Further there are also some early PCI-Express boards (GeforcePCX) + / that contain bridge chips to turn AGP GPUs into PCI-Express ones. + / + / Currently we will return PCI-Express on GeforcePCX board under the valid + / assumption that there are no AGP boards with the same device id. Further + / it seems that 'low' device ids are for PCI-Express->AGP while the higher ones + / are for AGP->PCI-Express, so for the lower ones (6200/6600/6800) we will return AGP + / and for the higher ones PCI-Express. A nicer way would be to read all this stuff from + / the pci header but as explained that can't be done at the moment. + */ + switch(nv_card->device_id) + { + case 0xf0: /* 6800 */ + case 0xf1: /* 6600GT */ + case 0xf2: /* 6600 */ + case 0xf3: /* 6200 */ + case 0xf5: /* 6800GS/XT */ + case 0xf6: /* 7800GS */ + case 0x2e0: /* 7600GT */ + case 0x2e1: /* 7600GS */ + case 0x2e2: /* 7300GT */ + return "AGP (BR02)"; /* We return something different from AGP for now as we don't want to show the AGP tab */ + case 0xf8: /* Quadro FX3400 */ + case 0xf9: /* Geforce 6800 series */ + case 0xfa: /* PCX5500 */ + case 0xfb: /* PCX5900 */ + case 0xfc: /* Quadro FX330*/ + case 0xfd: /* PCX5500 */ + case 0xfe: /* Quadro 1300 */ + case 0xff: /* PCX4300 */ + return "PCI-Express (BR02)"; + } + + if(nv_pci_find_capability(PCI_CAP_ID_EXP)) + return "PCI-Express"; + else if(nv_pci_find_capability(PCI_CAP_ID_AGP)) + return "AGP"; + else + return "PCI"; +} + +/* Needs better bus checks .. return a string ?*/ +static short get_agp_bus_rate() +{ + int agp_capptr, agp_rate, agp_status; + + agp_capptr = nv_pci_find_capability(PCI_CAP_ID_AGP); + agp_status = nv_read_pbus(agp_capptr + PCI_AGP_STATUS); + agp_rate = nv_read_pbus(agp_capptr + PCI_AGP_COMMAND) & PCI_AGP_STATUS_RATE_MASK; + + /* If true, the user has AGP8x support */ + if(agp_status & PCI_AGP_STATUS_RATE_8X_SUPPORT) + { + agp_rate <<= PCI_AGP_STATUS_RATE_8X_SHIFT; + } + return agp_rate; +} + +static char* get_agp_fw_status() +{ + int agp_capptr = nv_pci_find_capability(PCI_CAP_ID_AGP); + unsigned int agp_status = nv_read_pbus(agp_capptr + PCI_AGP_STATUS); + unsigned int agp_command = nv_read_pbus(agp_capptr + PCI_AGP_COMMAND); + + /* Check if Fast Writes is supported by the hostbridge */ + if(agp_status & PCI_AGP_STATUS_FW) + return (agp_command & PCI_AGP_COMMAND_FW) ? "Enabled" : "Disabled"; + else + return "Unsupported"; +} + +static char* get_agp_sba_status() +{ + int agp_capptr = nv_pci_find_capability(PCI_CAP_ID_AGP); + unsigned int agp_status = nv_read_pbus(agp_capptr + PCI_AGP_STATUS); + unsigned int agp_command = nv_read_pbus(agp_capptr + PCI_AGP_COMMAND); + + /* Check if Sideband Addressing is supported by the hostbridge */ + if(agp_status & PCI_AGP_STATUS_SBA) + return (agp_command & PCI_AGP_COMMAND_SBA) ? "Enabled" : "Disabled"; + else + return "Unsupported"; +} + +static char* get_agp_status() +{ + int agp_capptr = nv_pci_find_capability(PCI_CAP_ID_AGP); + unsigned int agp_command = nv_read_pbus(agp_capptr + PCI_AGP_COMMAND); + return (agp_command & PCI_AGP_COMMAND_AGP) ? "Enabled" : "Disabled"; +} + +static char* get_agp_supported_rates() +{ + int agp_capptr, agp_rates, agp_status, i; + static char *rate; + + agp_capptr = nv_pci_find_capability(PCI_CAP_ID_AGP); + agp_status = nv_read_pbus(agp_capptr + PCI_AGP_STATUS); + agp_rates = agp_status & PCI_AGP_STATUS_RATE_MASK; + + /* If true, the user has AGP8x support */ + if(agp_status & PCI_AGP_STATUS_RATE_8X_SUPPORT) + { + agp_rates <<= PCI_AGP_STATUS_RATE_8X_SHIFT; + } + + rate = (char*)calloc(1, sizeof(char)); + + for(i=1; i <= 8; i*=2) + { + if(agp_rates & i) + { + char *temp = (char*)malloc(4 * sizeof(char)); + sprintf(temp, "%dX ", i); + rate = (char*)realloc(rate, strlen(rate)+4); + rate = strcat(rate, temp); + free(temp); + } + } + + return rate; +} + +static short get_pcie_bus_rate() +{ + int pcie_rate, pcie_status_reg; + + pcie_status_reg = nv_pci_find_capability(PCI_CAP_ID_EXP); + if(pcie_status_reg != 0 ) + { + pcie_rate = (nv_read_pbus16(pcie_status_reg + PCIE_LINKSTATUS) & PCIE_LINK_SPEED_MASK) >> PCIE_LINK_SPEED_SHIFT; + return pcie_rate; + } + return 0; +} + +static short get_pcie_max_bus_rate() +{ + int pcie_rate, pcie_status_reg; + + pcie_status_reg = nv_pci_find_capability(PCI_CAP_ID_EXP); + if(pcie_status_reg != 0 ) + { + pcie_rate = (nv_read_pbus16(pcie_status_reg + PCIE_LINKCAP) & PCIE_LINK_SPEED_MASK) >> PCIE_LINK_SPEED_SHIFT; + + return pcie_rate; + } + return 0; +} + +static short get_memory_width() +{ + /* Nforce / Nforce2 */ + if((nv_card->device_id == 0x1a0) || (nv_card->device_id == 0x1f0)) + return 64; + /* GeforceFX cards (except for FX5200) need a different check */ + /* What to do with NV40 cards ? */ + else if(nv_card->arch & NV3X) + { + /* I got this info from the rivatuner forum. On the forum + * is a thread containing register dumps from lots of cards. + * It might not be 100% correct but it is better than a pci id check */ + switch(nv_card->PFB[0x200/4] & 0x7) + { + /* 64bit FX5600 */ + case 0x1: + return 64; + /* 128bit FX5800 */ + case 0x3: + return 128; + /* 128bit FX5600, FX5700 */ + case 0x5: + return 128; + /* 256bit FX5900 */ + case 0x7: + return 256; + } + } + else if(nv_card->arch == NV44) + { + return 64; /* For now return 64; (Turbocache cards) */ + } + else if(nv_card->arch & NV4X) + { + /* Memory bandwith detection for nv40 but not sure if it is correct, it is atleast better than nothing */ + switch(nv_card->PFB[0x200/4] & 0x7) + { + /* 128bit 6600GT */ + case 0x1: + return 128; + /* 256bit 6800 */ + case 0x3: + return 256; + default: + return 128; + } + } + else if(nv_card->arch & NV5X) + { + /* On Geforce 8800GTS/GTX and 8600GT/GTS cards the memory bandwith is proportional to the number of ROPs * 16. + * In case of the 8500 this isn't the case, there the size is just 128 where there are 4 ROPs. + * So for now use the number of ROPs as a meassure for the bus width. + */ + char rmask, rmask_default; + switch(nv_card->get_rop_units(&rmask, &rmask_default)) + { + case 24: /* 8800GTX */ + return 384; + case 20: /* 8800GTS */ + return 320; + case 8: /* 8600GT/GTS */ + case 4: /* 8500GT */ + return 128; + } + } + /* Generic algorithm for cards up to the Geforce4 */ + return (nv_card->PEXTDEV[0x0/4] & 0x17) ? 128 : 64; +} + +static char* get_memory_type() +{ + /* Nforce / Nforce2 */ + if((nv_card->device_id == 0x1a0) || (nv_card->device_id == 0x1f0)) + return ((pciReadLong(0x1, 0x7c) >> 12) & 0x1) ? "DDR" : "SDR"; + else if(nv_card->arch & (NV2X | NV3X)) + { + /* Based on statistics found on the rivatuner forum, the first two bytes of + * register 0x1218 of NV2X/NV3X boards, contains "0x0001 or 0x0101" in case of DDR memory and "0x0301" for DDR2. + */ + return (((nv_card->PMC[0x1218/4] >> 8) & 0x3) == 0x3) ? "DDR2" : "DDR"; + } + else if(nv_card->arch & (NV4X)) + { + /* On Geforce6/7 cards 0x100474 (PFB 0x474) can be used to distinguish between DDR and DDR3. + * Note these values are based on the bios and it was noted that for instance bits in this register differ. + * In case of DDR3 the first byte contains 0x4 while in case of DDR it contains 0x1. + */ + return (nv_card->PFB[0x474/4] & 0x4) ? "DDR3" : "DDR"; + } + else if(nv_card->arch & (NV5X)) + { + /* For now use 0x100218 (PFB 0x218) to distinguish between DDR2 and DDR3. The contents of this + * register differs between a 8500GT (DDR2) and 8600GTS/8800GTS (DDR3) according to the bios. + * FIXME: use a better register + */ + return (nv_card->PFB[0x218/4] & 0x1000000) ? "DDR3" : "DDR2"; + } + else + /* TNT, Geforce1/2/4MX */ + return (nv_card->PFB[0x200/4] & 0x01) ? "DDR" : "SDR"; +} + +static short get_memory_size() +{ + short memory_size; + + /* If the card is something TNT based the calculation of the memory is different. */ + if(nv_card->arch == NV5) + { + if(nv_card->PFB[0x0/4] & 0x100) + memory_size = ((nv_card->PFB[0x0/4] >> 12) & 0xf)*2+2; + else + { + switch(nv_card->PFB[0x0/4] & 0x3) + { + case 0: + memory_size = 32; + break; + case 1: + memory_size = 4; + break; + case 2: + memory_size = 8; + break; + case 3: + memory_size = 16; + break; + default: + memory_size = 16; + break; + } + } + } + /* Nforce 1 */ + else if(nv_card->device_id == 0x1a0) + { + int32_t temp = pciReadLong(0x1, 0x7c); + memory_size = ((temp >> 6) & 0x31) + 1; + } + /* Nforce2 */ + else if(nv_card->device_id == 0x1f0) + { + int32_t temp = pciReadLong(0x1, 0x84); + memory_size = ((temp >> 4) & 0x127) + 1; + } + /* Memory calculation for geforce cards or better.*/ + else + { + /* The code below is needed to show more than 256MB of memory + / but I'm not sure if 0xfff is safe for pre-geforcefx cards. + / There's no clean way right now to use 0xff for those old cards + / as currently the FX5200/FX5500 (which support 256MB) use the + / pre-geforcefx backend. + */ + memory_size = (nv_card->PFB[0x20c/4] >> 20) & 0xfff; + } + + return memory_size; +} + +/* Print various GPU registers for debugging purposes */ +static void get_debug_info() +{ + printf("--- %s GPU registers ---\n", nv_card->card_name); + printf("NV_PMC_BOOT_0 (0x0): %08x\n", nv_card->PMC[0]); + printf("NV_PBUS_DEBUG_0 (0x1080): %08x\n", nv_card->PMC[0x1080/4]); + printf("NV_PBUS_DEBUG_1 (0x1084): %08x\n", nv_card->PMC[0x1084/4]); + printf("NV_PBUS_DEBUG_2 (0x1088): %08x\n", nv_card->PMC[0x1088/4]); + printf("NV_PBUS_DEBUG_3 (0x108c): %08x\n", nv_card->PMC[0x108c/4]); + printf("NV_10F0 (0x10f0): %08x\n", nv_card->PMC[0x10f0/4]); + printf("NV_1540 (0x1540): %08x\n", nv_card->PMC[0x1540/4]); + printf("NV_15B0 (0x15b0): %08x\n", nv_card->PMC[0x15b0/4]); + printf("NV_15B4 (0x15b4): %08x\n", nv_card->PMC[0x15b4/4]); + printf("NV_15B8 (0x15b8): %08x\n", nv_card->PMC[0x15b8/4]); + printf("NV_15F0 (0x15f0): %08x\n", nv_card->PMC[0x15f0/4]); + printf("NV_15F4 (0x15f4): %08x\n", nv_card->PMC[0x15f4/4]); + printf("NV_15F8 (0x15f8): %08x\n", nv_card->PMC[0x15f8/4]); + printf("NV_PBUS_PCI_0 (0x1800): %08x\n", nv_read_pbus(PCI_VENDOR_ID)); + + if(nv_card->arch & (NV4X | NV5X)) + { + printf("NV_C010 (0xc010): %08x\n", nv_card->PMC[0xc010/4]); + printf("NV_C014 (0xc014): %08x\n", nv_card->PMC[0xc014/4]); + printf("NV_C018 (0xc018): %08x\n", nv_card->PMC[0xc018/4]); + printf("NV_C01C (0xc01c): %08x\n", nv_card->PMC[0xc01c/4]); + printf("NV_C020 (0xc020): %08x\n", nv_card->PMC[0xc020/4]); + printf("NV_C024 (0xc024): %08x\n", nv_card->PMC[0xc024/4]); + printf("NV_C028 (0xc028): %08x\n", nv_card->PMC[0xc028/4]); + printf("NV_C02C (0xc02c): %08x\n", nv_card->PMC[0xc02c/4]); + printf("NV_C040 (0xc040): %08x\n", nv_card->PMC[0xc040/4]); + printf("NV_4000 (0x4000): %08x\n", nv_card->PMC[0x4000/4]); + printf("NV_4004 (0x4004): %08x\n", nv_card->PMC[0x4004/4]); + printf("NV_4008 (0x4008): %08x\n", nv_card->PMC[0x4008/4]); + printf("NV_400C (0x400c): %08x\n", nv_card->PMC[0x400c/4]); + printf("NV_4010 (0x4010): %08x\n", nv_card->PMC[0x4010/4]); + printf("NV_4014 (0x4014): %08x\n", nv_card->PMC[0x4014/4]); + printf("NV_4018 (0x4018): %08x\n", nv_card->PMC[0x4018/4]); + printf("NV_401C (0x401c): %08x\n", nv_card->PMC[0x401c/4]); + printf("NV_4020 (0x4020): %08x\n", nv_card->PMC[0x4020/4]); + printf("NV_4024 (0x4024): %08x\n", nv_card->PMC[0x4024/4]); + printf("NV_4028 (0x4028): %08x\n", nv_card->PMC[0x4028/4]); + printf("NV_402C (0x402c): %08x\n", nv_card->PMC[0x402c/4]); + printf("NV_4030 (0x4030): %08x\n", nv_card->PMC[0x4030/4]); + printf("NV_4034 (0x4034): %08x\n", nv_card->PMC[0x4034/4]); + printf("NV_4038 (0x4038): %08x\n", nv_card->PMC[0x4038/4]); + printf("NV_403C (0x403c): %08x\n", nv_card->PMC[0x403c/4]); + printf("NV_4040 (0x4040): %08x\n", nv_card->PMC[0x4040/4]); + printf("NV_4044 (0x4044): %08x\n", nv_card->PMC[0x4044/4]); + printf("NV_4048 (0x4048): %08x\n", nv_card->PMC[0x4048/4]); + printf("NV_404C (0x404c): %08x\n", nv_card->PMC[0x404c/4]); + printf("NV_4050 (0x4050): %08x\n", nv_card->PMC[0x4050/4]); + printf("NV_4054 (0x4054): %08x\n", nv_card->PMC[0x4054/4]); + printf("NV_4058 (0x4058): %08x\n", nv_card->PMC[0x4058/4]); + printf("NV_405C (0x405c): %08x\n", nv_card->PMC[0x405c/4]); + printf("NV_4060 (0x4060): %08x\n", nv_card->PMC[0x4060/4]); + } + if(nv_card->arch & NV5X) + { + printf("NV_E100 (0xe100): %08x\n", nv_card->PMC[0xe100/4]); + printf("NV_E11C (0xe11c): %08x\n", nv_card->PMC[0xe11c/4]); + printf("NV_E120 (0xe120): %08x\n", nv_card->PMC[0xe120/4]); + printf("NV_20008 (0x20008): %08x\n", nv_card->PMC[0x20008/4]); + } + + printf("NV_PFB_CFG0 (0x100200): %08x\n", nv_card->PFB[0x200/4]); + printf("NV_PFB_CFG0 (0x100204): %08x\n", nv_card->PFB[0x204/4]); + printf("NV_PFB_CFG0 (0x100208): %08x\n", nv_card->PFB[0x208/4]); + printf("NV_PFB_CFG0 (0x10020c): %08x\n", nv_card->PFB[0x20c/4]); + printf("NV_PFB_218 (0x100218): %08x\n", nv_card->PFB[0x218/4]); + printf("NV_PFB_TIMING0 (0x100220): %08x\n", nv_card->PFB[0x220/4]); + printf("NV_PFB_TIMING1 (0x100224): %08x\n", nv_card->PFB[0x224/4]); + printf("NV_PFB_TIMING2 (0x100228): %08x\n", nv_card->PFB[0x228/4]); + printf("NV_PFB_474 (0x100474): %08x\n", nv_card->PFB[0x474/4]); + printf("NV_PEXTDEV_BOOT_0 (0x101000): %08x\n", nv_card->PEXTDEV[0x0/4]); + printf("NV_NVPLL_COEFF_A (0x680500): %08x\n", nv_card->PRAMDAC[0x500/4]); + printf("NV_MPLL_COEFF_A (0x680504): %08x\n", nv_card->PRAMDAC[0x504/4]); + printf("NV_VPLL_COEFF (0x680508): %08x\n", nv_card->PRAMDAC[0x508/4]); + printf("NV_PLL_COEFF_SELECT (0x68050c): %08x\n", nv_card->PRAMDAC[0x50c/4]); + printf("NV_NVPLL_COEFF_B (0x680570: %08x\n", nv_card->PRAMDAC[0x570/4]); + printf("NV_MPLL_COEFF_B (0x680574: %08x\n", nv_card->PRAMDAC[0x574/4]); + + /* The builtin tvout encoder is available on Geforce4MX/TI and all other GPUs upto NV3x/NV4x. + * The registers are somewhere else on Geforce8 cards. There is a difference between the encoders + * on the difference cards but I'm not sure which apart from more features like the addition of + * component on the Geforce6 */ + if(nv_card->arch & (NV17 | NV25 | NV3X | NV4X)) + { + int index=0; + printf("--- TVOut regs ---\n"); + printf("0xd200: 0x%08x\n", nv_card->PMC[0xd200/4]); /* bit27-24 flickering (?) */ + printf("0xd204: 0x%08x\n", nv_card->PMC[0xd204/4]); + printf("0xd208: 0x%08x\n", nv_card->PMC[0xd208/4]); /* Overscan */ + printf("0xd20c: 0x%08x\n", nv_card->PMC[0xd20c/4]); + printf("0xd210: 0x%08x\n", nv_card->PMC[0xd210/4]); /* bit 23-8 contain the horizontal resolution */ + printf("0xd214: 0x%08x\n", nv_card->PMC[0xd214/4]); /* bit 23-8 contain the vertical resolution */ + printf("0xd218: 0x%08x\n", nv_card->PMC[0xd218/4]); /* bit31 = sign bit; bit16 and up can be used for horizontal positioning */ + + printf("0xd21c: 0x%08x\n", nv_card->PMC[0xd21c/4]); /* bit31 = sign bit; bit16 and up can be used for vertical positioning */ + + printf("0xd228: 0x%08x\n", nv_card->PMC[0xd228/4]); /* is this some clock signal?? */ + printf("0xd22c: 0x%08x\n", nv_card->PMC[0xd22c/4]); + + printf("0xd230: 0x%08x\n", nv_card->PMC[0xd230/4]); + printf("0xd304: 0x%08x\n", nv_card->PMC[0xd304/4]); /* bit 25-16 hscaler (PAL 720, NTSC 720) */ + printf("0xd508: 0x%08x\n", nv_card->PMC[0xd508/4]); /* bit 25-26 vscalar (PAL 288, NTSC 240) */ + printf("0xd600: 0x%08x\n", nv_card->PMC[0xd600/4]); + printf("0xd604: 0x%08x\n", nv_card->PMC[0xd604/4]); + printf("0xd608: 0x%08x\n", nv_card->PMC[0xd608/4]); + + /* Register 0xd220/0xd224 form a index/data register pair + * - 0x7 = bit4:0 connector type; bit2 s-video, bit2-0 empty: composite? + * - 0xe = bit7:0 tv system; ntscm (japan) 0x2; palb/d/g 0x4; palm/n/ntsc 0xc; is this correct? + * - 0x22 = tv saturation + * - 0x25 = tv hue + * how many indices exist ? + */ + for(index=0; index < 0x80; index++) + { + nv_card->PMC[0xd220/4] = index; + printf("index 0x%x: %02x\n", index, nv_card->PMC[0xd224/4]); + } + } +} + + +void info_init(void) +{ + nv_card->get_bus_type = get_bus_type; + + /* Set the pci id again as the detected id might not be accurate in case of pci id modding. The OS doesn't allways update the id while it really changed! Only do it for cards without bridges (device_id != 0xf* and 0x2e* + * Don't use this for NV50 as the location of the pci config header has changed to an unknown position. + */ + if(((nv_card->device_id & 0xfff0) != 0xf0) && ((nv_card->device_id & 0xfff0) != 0x2e0) && !(nv_card->arch & NV5X)) + { + nv_card->device_id = get_gpu_pci_id(); + nv_card->card_name = (char*)get_card_name(nv_card->device_id, &nv_card->gpu); + } + + /* gpu arch/revision */ + nv_card->get_gpu_architecture = get_gpu_architecture; + nv_card->get_gpu_revision = get_gpu_revision; + + /* Allow modding on all Geforce cards except for ones using bridges */ + if((nv_card->arch & (NV1X | NV2X | NV3X | NV4X)) && ((nv_card->device_id & 0xfff0) != 0xf0)) + { + nv_card->caps |= GPU_ID_MODDING; + nv_card->set_gpu_pci_id = set_gpu_pci_id; + } + else + nv_card->set_gpu_pci_id = NULL; + + /* Check if card is a native AGP one and not using a bridge chip else we can't use the code below */ + if(strcmp(nv_card->get_bus_type(), "AGP") == 0) + { + nv_card->get_bus_rate = get_agp_bus_rate; + nv_card->get_agp_status = get_agp_status; + nv_card->get_agp_fw_status = get_agp_fw_status; + nv_card->get_agp_sba_status = get_agp_sba_status; + nv_card->get_agp_supported_rates = get_agp_supported_rates; + } + /* Check if card is a native PCI-Express one and not using a bridge chip else we can't use the code below */ + else if(strcmp(nv_card->get_bus_type(), "PCI-Express") == 0) + { + nv_card->get_bus_rate = get_pcie_bus_rate; + nv_card->get_pcie_max_bus_rate = get_pcie_max_bus_rate; + nv_card->get_agp_status = NULL; + nv_card->get_agp_fw_status = NULL; + nv_card->get_agp_sba_status = NULL; + nv_card->get_agp_supported_rates = NULL; + } + else + { + nv_card->get_bus_rate = NULL; + nv_card->get_agp_status = NULL; + nv_card->get_agp_fw_status = NULL; + nv_card->get_agp_sba_status = NULL; + nv_card->get_agp_supported_rates = NULL; + } + + nv_card->get_memory_size = get_memory_size; + nv_card->get_memory_type = get_memory_type; + nv_card->get_memory_width = get_memory_width; + + /* Debugging stuff */ + nv_card->get_debug_info = get_debug_info; +} diff --git a/nvclock/info.o b/nvclock/info.o new file mode 100644 index 0000000000000000000000000000000000000000..33068e51bb3bcaa62cdc0f2d5339205f6d90a0b7 GIT binary patch literal 27548 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 literal 0 HcmV?d00001 diff --git a/nvclock/libbackend.a b/nvclock/libbackend.a new file mode 100644 index 0000000000000000000000000000000000000000..dc4d6af2abd831e945588ab505ad156227d3648a GIT binary patch literal 115052 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⁣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

4U#`3`6kIbC4W}(-I8}p{;K2$ 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 ZU`-$ql~O9|9P&InI`K?w6bPif{|7){z)AoB literal 0 HcmV?d00001 diff --git a/nvclock/libc_wrapper.c b/nvclock/libc_wrapper.c new file mode 100644 index 0000000..4787205 --- /dev/null +++ b/nvclock/libc_wrapper.c @@ -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 +#include "xfree.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +/* 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; +} + diff --git a/nvclock/libc_wrapper.o b/nvclock/libc_wrapper.o new file mode 100644 index 0000000000000000000000000000000000000000..1722fa183b5aaccd91be09ef255140b64ccd3052 GIT binary patch literal 888 zcma)4yGjF55S^I#L?LP;SQtbhqPh5pZ?MrA1hGpkoAs)}#9el;s32Hah2T&41^$DD zZMZs<*qf> zDD>4+Q?H-xcDwl$rLxaEQz_`~3vW_tl-?jrH%cFk!eBk!eByd>dwV03E$VaQd6~Rg zR!g~HG2<_&g}IDh(Ybk*SF1B|g-C_hHRF^SO%UU$M1ig4tlH_vv1iL5Y5Oq&fbrNwbLX~n&UopoQDqawB=lm z%vcnVezTl_{x97;YtSDELq8Qo@dGvZy2Ut4^dNynXkf$a@=kE*_7GST9k5H1j5kak z8L@+2cFckK!8`lSG1=stnDi4a*qKk!dt36r-+v`|=NrIfi}9)C_w5Pqj=l|SlzoIZ zg!>c2cTcjThI>H0bEl|NwNiEuF9XN-b)a0jI$m432&-EAWC58IZ9?sa#Aqrt?HwOE Q*F-~nyo)b43mhQ&0xIHUsQ>@~ literal 0 HcmV?d00001 diff --git a/nvclock/lm99.c b/nvclock/lm99.c new file mode 100644 index 0000000..6ea0dc6 --- /dev/null +++ b/nvclock/lm99.c @@ -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 +#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; +} diff --git a/nvclock/lm99.o b/nvclock/lm99.o new file mode 100644 index 0000000000000000000000000000000000000000..9e4ebcd81546886d4f6b5f1225fb8059d05d12af GIT binary patch literal 1364 zcma)6&ubG=5T0$)HddikqKFmTL#nOXM_bd_2x@=DAT*^ag&u^kO*S={&4%O!QamUG z!Js{P@!;Q4dkl&cZ~hB{f=~~7F*%5FzTLNV`vY`fGV{&%z1ewhU-ofg;g+UpZNVFRBk0=j=ZR1w82S3E)9Gv^etJx2FVnx7 zX!UP4>+Pf*skc)yQg3V38@x2PJss9)I~c)ZoFVp3cgEl=pBaWQ^bFXheAF%cD4M_h(TRMstNta6lPH$L#PvRM$) ztVt4CLmDJjD-{xD+XWfhviK1sincUGa&dXIB8|*L9O?hsITqq%d(;BlqZQu;3#$4c z$0I672zL`rb3_et=tAs63I{mCUif+ed;KnP4*kD$W2`|7bqF4@I5VU8n*9WySlFk)J|g=t z-XpXl?9kuj_@4cx!c?&0aglLCI%`TZBdL~~irt==yK5TR*>!0W-}P*@YFYeU2J$%>d?uf28meh4BS~{*#J-00GJNLI3~& literal 0 HcmV?d00001 diff --git a/nvclock/nv30.c b/nvclock/nv30.c new file mode 100644 index 0000000..b527b56 --- /dev/null +++ b/nvclock/nv30.c @@ -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 +#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); + } +} diff --git a/nvclock/nv30.o b/nvclock/nv30.o new file mode 100644 index 0000000000000000000000000000000000000000..eceed43054787bab77c3ddde2c55b518922af4cc GIT binary patch literal 7160 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 literal 0 HcmV?d00001 diff --git a/nvclock/nv40.c b/nvclock/nv40.c new file mode 100644 index 0000000..e7bd747 --- /dev/null +++ b/nvclock/nv40.c @@ -0,0 +1,1099 @@ +/* NVClock 0.8 - Linux overclocker for NVIDIA cards + * + * site: http://nvclock.sourceforge.net + * + * Copyright(C) 2001-2008 Roderick Colenbrander + * + * Thanks to Erik Waling for doing Smartdimmer coding/testing. (his code isn't the one in NVClock) + * + * 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 + */ + +/* This source file uses some clock calculation code from nvidia's xfree86 driver. + To keep Nvidia happy I have added their copyright. The way they interpret it (see linux kernel riva_hw.h) + is that you need to add the disclaimer and copyright and when that's done + you can basicly do what you want. +*/ + +/***************************************************************************\ +|* *| +|* Copyright 1993-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 1993-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. *| +|* *| +\***************************************************************************/ + +#include +#include +#include "backend.h" + +/* +/ The original NV40 gpu was used as the base for 6800LE/6800NU/6800GT/6800Ultra +/ GPUs. The difference between all these models lie in the amount of enabled +/ pixel/vertex pipelines and clockspeeds. For instance the 6800LE ships with 8 +/ pixel pipelines while the 6800GT ships with 16 of those. Right after production +/ all GPUs are tested, if all pipelines work and they run at high clocks they +/ are called Ultra or if pipes are broken they are called 6800NU(12p) or 6800LE(8p). +/ Further in some cases 'good' GPUs can be rebranded too if there's a shortage of +/ 6800NU/6800LE GPUs. The key to this rebranding is register 0x1540 which contains +/ the enabled pixel/vertex pipelines. Depending on the GPU architecture a bit can +/ correspond to a single vertex shader or to a block containing two or four +/ pixel pipelines. +/ +/ We now define some words coming from Rivatuner as people are familiar with those. +/ A 'good' GPU for which pipelines are disabled just to get enough lowend models +/ is said to contain 'Software masked units'. In this case the videobios initializes +/ 0x1540 with a value that locks out some units. +/ GPUs which didn't pass the hardware quality testing contain 'Hardware masked units'. +/ In this case the bios initializes 0x1540 with a value that enables all pipelines. +/ A certain (read-only) register (0xc010) contains a mask of units to disable by default. +/ The bios loads this value into another register (0xc020) at startup. The value from +/ 0xc020 is then used by the drivers to disable units in 0x1540. For example by clearing this +/ register before the drivers are loaded, you can prevent masks from being disabled. + +/ 1540 units_cfg (rw) second byte contains vertex configuration and first byte pipeline +/ c010 default_mask (r) pixel pipelines start at +22, while vertex start at +16 (is this correct for all cards?) +/ c020 active_mask (rw) +/ c024/c028/c02c are being set to 0, why? (they correspond to c014/c018/c01c) +/ +/ Below are supported pipeline configurations on various types of cards. Not sure +/ if everything is fully correct though: +/ - NV40 0x3f0f 6800 cards +/ - NV41 0x1f07 6800 pci-e (is this correct?) +/ - NV43 0x0703 6200/6600 cards +/ - NV44 0x0703 6200(Go)/Turbocache cards +/ - NV46 0x0703 7300 (is this correct?) +/ - NV47/NV49 0xff3f 7800/7900 cards +/ - NV4B 0x1f0f 7600 +*/ + +static int nv40_get_default_mask(char *pmask, char *vmask) +{ + int mask; + switch(nv_card->arch) + { + case NV40: + mask = 0x3f0f; + break; + case NV41: + mask = 0x1f07; + break; + case NV43: + case NV44: + case NV46: + case C51: + mask = 0x703; + break; + case NV47: + case NV49: + mask = 0xff3f; + break; + case NV4B: + mask = 0x1f0b; + break; + } + + if(pmask) + *pmask = mask & 0xff; + if(vmask) + *vmask = (mask >> 8) & 0xff; + + return mask; +} + +/* Try to locate hardware maskes units. On success we return 1 and pmask/vmask +/ contain the masked units. When no hw masked units are found we return 0. +*/ +static int nv40_get_hw_masked_units(char *pmask, char *vmask) +{ + unsigned int mask = nv_card->PMC[0xc010/4]; /* Or should we show the currently locked pipes? */ + unsigned int masked_units; + + /* On 6200 cards for some reason 0xc010 can be empty while there are locked pipes, 0xc020 is then used instead. + / For now we use 0xc020 if that's the case. Note that during unlocking 0xc020 is cleared and that we then lose this locking info. + */ + if(mask == 0) + mask = nv_card->PMC[0xc020/4]; + /* For now we show units that are hw masked by default, not the currently masked units; the cli code wants to have this info + / Unfortunately bios dumping isn't possible on various mobile 6200go cards, so as a fallback use the currently masked pipes + / in favor of a segfault ;) + */ + /* What to do with NV47 which has 8 vertex units? */ + masked_units = (((mask & 0x3f0000) >> 8) | (mask >> 22)) & nv40_get_default_mask(0, 0); + + if(masked_units != 0) + { + *pmask = masked_units & 0xff; /* pixel */ + *vmask = (masked_units >> 8) & 0xff; /* vertex */ + return 1; + } + + return 0; +} + +/* Try to locate software maskes units. On success we return 1 and pmask/vmask +/ contain the masked units. When no sw masked units are found we return 0. +*/ +static int nv40_get_sw_masked_units(char *pmask, char *vmask) +{ + unsigned int mask = nv40_get_default_mask(0, 0); + unsigned int pipe_cfg; + + pipe_cfg = nv_card->PMC[0x1540/4] & nv40_get_default_mask(0, 0); + + if(!pipe_cfg) + return 0; + + /* Check if the card contains sw masked units by comparing + / the default pipe_cfg register value with the most optimal + / register value for the type of card. If they differ we have + / sw masked units. The check below first does a AND-mask to filter + / out bits which aren't needed. + */ + if((pipe_cfg & 0xffff) != mask) + { + /* First note the bits that are different + / E.g. pipe_cfg = 0x701, while mask = 0x703 + / 0x701 ^ 0x703 = 0x002 + */ + pipe_cfg = (pipe_cfg ^ mask) & mask; + *pmask = pipe_cfg & 0xff; + *vmask = (pipe_cfg >> 8) & 0xff; + return 1; + } + return 0; +} + +/* Receive the number of enabled pixel pipelines and also +/ store a mask with active pipelines. Further store the total +/ number of pixel units per pipeline in total. +*/ +static int nv40_get_pixel_pipelines(char *mask, int *total) +{ + unsigned char pipe_cfg = nv_card->PMC[0x1540/4] & 0xff; + int i, pipelines=0; + + /* The number of enabled pixel pipelines is stored in the first 4 (or more?) bits. + / In case of 6800 hardware a single bit corresponds to 4 pipelines and on NV44/NV46 + / hardware a bit corresponds to 2 pipelines + */ + for(i=0; i<8; i++) + if((pipe_cfg >> i) & 0x1) + pipelines++; + + *mask = pipe_cfg; + + /* NV44/NV46 use 2 pixel units per pipeline */ + if(nv_card->arch & (NV44 | NV46)) + *total = 2; + else + *total = 4; + + return pipelines; +} + +/* Receive the number of enabled vertex pipelines and also +/ store a mask with active pipelines. +*/ +static int nv40_get_vertex_pipelines(char *mask) +{ + unsigned char pipe_cfg = (nv_card->PMC[0x1540/4] >> 8) & 0xff; + int i, pipelines=0; + + /* The number of enabled vertex pipelines is stored in the second byte. + / A a single bit corresponds to 1 vertex pipeline. + */ + for(i=0; i<8; i++) + if((pipe_cfg >> i) & 0x1) + pipelines++; + + *mask = pipe_cfg; + + return pipelines; +} + +static void nv40_set_pixel_pipelines(unsigned char mask) +{ + int pipe_cfg = nv_card->PMC[0x1540/4]; + + /* Why do 0xc024/0xc028/0xc02c need to be reset? What do they contain? */ + nv_card->PMC[0xc020/4] = nv_card->PMC[0xc024/4] = nv_card->PMC[0xc028/4] = nv_card->PMC[0xc02c/4] = 0; + + nv_card->PMC[0x1540/4] = ~(~pipe_cfg | 0xff) | mask; +} + +static void nv40_set_vertex_pipelines(unsigned char mask) +{ + int pipe_cfg = nv_card->PMC[0x1540/4]; + + /* Why do 0xc024/0xc028/0xc02c need to be reset? What do they contain? */ + nv_card->PMC[0xc020/4] = nv_card->PMC[0xc024/4] = nv_card->PMC[0xc028/4] = nv_card->PMC[0xc02c/4] = 0; + + nv_card->PMC[0x1540/4] = ~(~pipe_cfg | 0xff00) | (mask<<8); +} + +/* Fanspeed code for Geforce6800 hardware */ +static float nv40_get_fanspeed() +{ + /* Bit 30-16 of register 0x10f0 control the voltage for the pwm signal generator + / which is connected to the fan. By changing the value in the register the duty cycle can be controlled + / so that the fan turns slower or faster. Bit 14-0 of 0x10f0 contain the pwm division + / ratio which decides the smallest fanspeed adjustment step. + / The value stored in the registers needs to be inverted, so a value of 10% means 90% and so on. + */ + int pwm_divider = nv_card->PMC[0x10f0/4] & 0x7fff; + float fanspeed = (float)(pwm_divider - ((nv_card->PMC[0x10f0/4] >> 16) & 0x7fff)) * 100.0/(float)pwm_divider; + return fanspeed; +} + +static void nv40_set_fanspeed(float speed) +{ + int value; + int pwm_divider = nv_card->PMC[0x10f0/4] & 0x7fff; + + /* For safety reasons we should never disable the fan by not putting it below 10%; further negative values don't exist ;) */ + if(speed < 10 || speed > 100) + return; + + value = 0x80000000 + ((((int)(100 - speed) * pwm_divider/100) & 0x7fff)<<16) + pwm_divider; + nv_card->PMC[0x10f0/4] = value; +} + +/* Fanspeed code for Geforce6600 hardware (does this work for 6200 cards too??)*/ +static float nv43_get_fanspeed() +{ + /* The first 12 or more bits of register 0x15f4 control the voltage for the pwm signal generator in case + / of Geforce 6200/6600(GT)/7600/7800GS hardware. By changing the value in the register the duty cycle of the pwm signal + / can be controlled so that the fan turns slower or faster. The first part of register 0x15f8 contains the pwm division ratio. + / The value stored in the registers needs to be inverted, so a value of 10% means 90% and so on. (pwm_divider means off, 0 means on) + */ + int pwm_divider = nv_card->PMC[0x15f8/4] & 0x3fff; + float fanspeed = (pwm_divider - (nv_card->PMC[0x15f4/4] & 0x3fff)) * 100.0/(float)pwm_divider; + return fanspeed; +} + +static void nv43_set_fanspeed(float speed) +{ + int value; + int pwm_divider = nv_card->PMC[0x15f8/4] & 0x3fff; + + /* For safety reasons we should never disable the fan by not putting it below 10%; further negative values don't exist ;) */ + if(speed < 10 || speed > 100) + return; + + value = 0x80000000 + (int)((100 - speed) * pwm_divider/100); + nv_card->PMC[0x15f4/4] = value; +} + +/* There's an internal temperature sensor on NV43 hardware and higher +/ Note that the sensor variable which is passed to this function is bogus +/ it is only there to share nv_card->get_gpu_temp between I2C and low-level. +*/ +static int nv43_get_gpu_temp(void *sensor) +{ + int temp; + int correction=0; + float offset; + float slope; + + /* For now duplicate the temperature offset code here. It is needed for Mobile users in most cases the bios can't be read on those GPUs. */ + if(!nv_card->bios) + { + switch(nv_card->arch) + { + case NV43: + offset = 32060.0/1000.0; + slope = 792.0/1000.0; + break; + case NV44: + case NV47: + offset = 27839.0/1000.0; + slope = 700.0/1000.0; + break; + case NV46: /* are these really the default ones? they come from a 7300GS bios */ + offset = -24775.0/100.0; + slope = 467.0/10000.0; + break; + case NV49: /* are these really the default ones? they come from a 7900GT/GTX bioses */ + offset = -25051.0/100.0; + slope = 458.0/10000.0; + break; + case NV4B: /* are these really the default ones? they come from a 7600GT bios */ + offset = -24088.0/100.0; + slope = 442.0/10000.0; + break; + } + } + else + { + /* The output value of the sensor needs to be 'calibrated' in order to get the correct temperature. These + / values are stored in the video bios and are different for each type of gpu. The value needs to be multiplied + / with a certain 'slope' and further the sensor has an offset and another correction constant. + */ + offset = (float)nv_card->bios->sensor_cfg.diode_offset_mult / (float)nv_card->bios->sensor_cfg.diode_offset_div; + slope = (float)nv_card->bios->sensor_cfg.slope_mult / (float)nv_card->bios->sensor_cfg.slope_div; + correction = nv_card->bios->sensor_cfg.temp_correction; + } + + /* Assume that the sensor is disabled when the temperature part (without offset) is 0 */ + if((nv_card->PMC[0x15b4/4] & 0xfff) == 0) + { + /* Initialize the sensor, for now program a threshold value of 120C. + */ + int max_temp = (int)(((float)120 - offset - correction) / slope); + + /* 7300/7600/7900 cards need bit31 to be set while older cards need a different bit */ + if(nv_card->arch & (NV46 | NV49 | NV4B)) + nv_card->PMC[0x15b0/4] = 0x80000000 | max_temp; + else + nv_card->PMC[0x15b0/4] = 0x10000000 | max_temp; + usleep(500); + } + + /* In case of Geforce 7300/7600/7900 cards more than one byte is used for the temperature */ + if(nv_card->arch & (NV46 | NV49 | NV4B)) + temp = nv_card->PMC[0x15b4/4] & 0x1fff; + else + temp = nv_card->PMC[0x15b4/4] & 0xff; + + return (int)(temp * slope + offset) + correction; +} + +/* Get current backpanel brightness level on laptops */ +static int nv44_mobile_get_smartdimmer() +{ + /* Convert level to a value between 1 and 100 */ + return 5*(((nv_card->PMC[0x15f0/4] >> 16) & 0x1f) - 1); +} + +/* Adjust backpanel brightness on laptops */ +static void nv44_mobile_set_smartdimmer(int level) +{ + if(level < 15 || level > 100) + return; + + /* Convert the level to correct Smartdimmer values; on Windows a value between 4 and 21 works fine although 0-31 should work. + / The code below creates a value between 4 and 21; + */ + level = level/5 + 1; + + /* Modify the smartdimmer part but keep the rest of the register the same */ + nv_card->PMC[0x15f0/4] = (level << 16) | (nv_card->PMC[0x15f0/4] & 0xffe0ffff); +} + +static int CalcSpeed_nv40(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_nv40(int base_freq, unsigned int pll, unsigned int pll2) +{ + int m1, m2, n1, n2, p; + + /* mpll at 0x4020 and 0x4024; nvpll at 0x4000 and 0x4004 */ + p = (pll >> 16) & 0x03; + m1 = pll2 & 0xFF; + n1 = (pll2 >> 8) & 0xFF; + + /* Bit 8 of the first pll register can be used to disable the second set of dividers/multipliers. */ + if(pll & 0x100) + { + m2 = 1; + n2 = 1; + } + /* NV46/NV49/NV4B cards seem to use a different calculation; I'm not sure how it works yet, so for now check the architecture. Further it looks like bit 15 can be used to identify it but I'm not sure yet. + */ + else if(pll & 0x1000) + { + m2 = 1; + n2 = 1; + } + else + { + m2 = (pll2 >> 16) & 0xFF; + n2 = (pll2 >> 24) & 0xFF; + } + + if(nv_card->debug) + printf("m1=%d m2=%d n1=%d n2=%d p=%d\n", m1, m2, n1, n2, p); + + return (float)CalcSpeed_nv40(base_freq, m1, m2, n1, n2, p)/1000; +} + +const unsigned int pll_entries=2; +/* TODO: add proper architecture specific defaults */ +const struct pll pll_lst[2] = { + {0x0000, 7, 0, {3000, 25000, 100000, 405000, 1, 255, 1, 255}, {35000, 100000, 400000, 1000000, 1, 31, 1, 31}}, + {0x4020, 7, 0, {3000, 25000, 100000, 405000, 1, 255, 1, 255}, {35000, 100000, 600000, 1400000, 1, 31, 1, 31}}, +}; + +static const struct pll* GetPllLimits(unsigned int reg) +{ + int i; + if(nv_card->bios && nv_card->bios->pll_entries) + { + for(i=0; ibios->pll_entries; i++) + { + if(nv_card->bios->pll_lst[i].reg == reg) + return &nv_card->bios->pll_lst[i]; + } + /* Return the default limits */ + return &nv_card->bios->pll_lst[0]; + } + else + { + for(i=0; iVCO1.minInputFreq; + minVCOFreq = pll_limits->VCO1.minFreq; + maxVCOFreq = pll_limits->VCO1.maxFreq; + minM = pll_limits->VCO1.minM; + maxM = pll_limits->VCO1.maxM; + minN = pll_limits->VCO1.minN; + maxN = pll_limits->VCO1.maxN; + maxP = 6; + + /* The optimal frequency for the PLL to work at is somewhere in the center of its range. + / Choose a post divider in such a way to achieve this. + / The G8x nv driver does something similar but they they derive a minP and maxP. That + / doesn't seem required as you get so many matching clocks that you don't enter a second + / iteration for P. (The minP / maxP values in the nv driver only differ at most 1, so it is for + / some rare corner cases. + */ + for(P=0, VCOFreq=maxVCOFreq/2; clockIn<=VCOFreq && P <= maxP; P++) + { + VCOFreq /= 2; + } + + /* 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(M=minM; M<=maxM; M++) + { + /* The VCO has a minimum input frequency */ + if((refClk/M) < minVCOInputFreq) + break; + + for(N=minN; N<=maxN; N++) + { + /* Calculate the frequency generated by VCO1 */ + clock = (int)(refClk * N / (float)M); + + /* Verify if the clock lies within the output limits of VCO1 */ + if(clock < minVCOFreq) + continue; + else if (clock > maxVCOFreq) /* It is no use to continue as the clock will only become higher */ + break; + + clock >>= P; + delta = abs((int)(clockIn - clock)); + /* When the difference is 0 or less than .5% accept the speed */ + if(((delta == 0) || ((float)delta/(float)clockIn <= 0.005))) + { + *bestM = M; + *bestN = N; + *bestP = P; + return; + } + + /* When the new difference is smaller than the old one, use this one */ + if(delta < bestDelta) + { + bestDelta = delta; + *bestM = M; + *bestN = N; + *bestP = P; + } + } + } +} + +static void ClockSelectDoubleVCO_nv40(int clockIn, const struct pll *pll_limits, int *bestM, int *bestM2, int *bestN, int *bestN2, int *bestP) +{ + int clock1, clock2, M, M2, N, N2, P; + int delta, bestDelta, minM, minM2, maxM, maxM2, minN, minN2, maxN, maxN2, maxP; + int minVCOInputFreq, minVCO2InputFreq, maxVCO2InputFreq, minVCOFreq, minVCO2Freq, maxVCOFreq, maxVCO2Freq; + int maxClock, VCO2Freq; + int refClk = 27000; + bestDelta = clockIn; + *bestM=*bestM2=*bestN=*bestN2=*bestP=0; + + minVCOInputFreq = pll_limits->VCO1.minInputFreq; + minVCOFreq = pll_limits->VCO1.minFreq; + maxVCOFreq = pll_limits->VCO1.maxFreq; + minM = pll_limits->VCO1.minM; + maxM = pll_limits->VCO1.maxM; + minN = pll_limits->VCO1.minN; + maxN = pll_limits->VCO1.maxN; + + minVCO2InputFreq = pll_limits->VCO2.minInputFreq; + maxVCO2InputFreq = pll_limits->VCO2.maxInputFreq; + minVCO2Freq = pll_limits->VCO2.minFreq; + maxVCO2Freq = pll_limits->VCO2.maxFreq; + minM2 = pll_limits->VCO2.minM; + maxM2 = pll_limits->VCO2.maxM; + minN2 = pll_limits->VCO2.minN; + maxN2 = pll_limits->VCO2.maxN; + maxP = 6; /* This should be somewhere in the bios too */ + + maxClock = maxVCO2Freq; + /* If the requested clock is behind the bios limits, try it anyway */ + if(clockIn > maxVCO2Freq) + maxClock = clockIn + clockIn/200; /* Add a .5% margin */ + + /* The optimal frequency for the PLL to work at is somewhere in the center of its range. + / Choose a post divider in such a way to achieve this. + / The G8x nv driver does something similar but they they derive a minP and maxP. That + / doesn't seem required as you get so many matching clocks that you don't enter a second + / iteration for P. (The minP / maxP values in the nv driver only differ at most 1, so it is for + / some rare corner cases. + */ + for(P=0, VCO2Freq=maxClock/2; clockIn<=VCO2Freq && P <= maxP; P++) + { + VCO2Freq /= 2; + } + + /* The PLLs on Geforce6/7 hardware can operate in a single stage made with only 1 VCO + / and a cascade mode of two VCOs. This second mode is in general used for relatively high + / frequencies. The loop below calculates the divider and multiplier ratios for the cascade + / mode. The code takes into account limits defined in the video bios. + */ + for(M=minM; M<=maxM; M++) + { + /* The VCO has a minimum input frequency */ + if((refClk/M) < minVCOInputFreq) + break; + + for(N=minN; N<=maxN; N++) + { + /* Calculate the frequency generated by VCO1 */ + clock1 = (int)(refClk * N / (float)M); + /* Verify if the clock lies within the output limits of VCO1 */ + if( (clock1 < minVCOFreq) ) + continue; + else if(clock1 > maxVCOFreq) /* For future N, the clock will only increase so stop; xorg nv continues but that is useless */ + break; + + for(M2=minM2; M2<=maxM2; M2++) + { + /* The clock fed to the second VCO needs to lie within a certain input range */ + if(clock1 / M2 < minVCO2InputFreq) + break; + else if(clock1 / M2 > maxVCO2InputFreq) + continue; + + N2 = (int)((float)((clockIn << P) * M * M2) / (float)(refClk * N)+.5); + if( (N2 < minN2) || (N2 > maxN2) ) + continue; + + /* The clock before being fed to the post-divider needs to lie within a certain range. + / Further there are some limits on N2/M2. + */ + clock2 = (int)((float)(N*N2)/(M*M2) * refClk); + if( (clock2 < minVCO2Freq) || (clock2 > maxClock))// || ((N2 / M2) < 4) || ((N2 / M2) > 10) ) + continue; + + /* The post-divider delays the 'high' clock to create a low clock if requested. + / This post-divider exists because the VCOs can only generate frequencies within + / a limited frequency range. This range has been tuned to lie around half of its max + / input frequency. It tries to calculate all clocks (including lower ones) around this + / 'center' frequency. + */ + clock2 >>= P; + delta = abs((int)(clockIn - clock2)); + + /* When the difference is 0 or less than .5% accept the speed */ + if(((delta == 0) || ((float)delta/(float)clockIn <= 0.005))) + { + *bestM = M; + *bestM2 = M2; + *bestN = N; + *bestN2 = N2; + *bestP = P; + return; + } + + /* When the new difference is smaller than the old one, use this one */ + if(delta < bestDelta) + { + bestDelta = delta; + *bestM = M; + *bestM2 = M2; + *bestN = N; + *bestN2 = N2; + *bestP = P; + } + } + } + } +} + +static void ClockSelect_nv40(int clockIn, unsigned int reg, unsigned int *pllOut, unsigned int *pllBOut) +{ + int PLL=0, PLL2=0; + int bestM=0, bestM2=1, bestN=0, bestN2=1, bestP=0; + const struct pll *pll_limits = GetPllLimits(reg); + int pllIn = nv_card->PMC[reg/4]; + + printf("Warning using experimental NV4x lowlevel clock adjustment, if you encounter strange issues, issue a bugreport.\n"); + + /* Use a single VCO if the clock isn't high enough to require a second. + / This is what apparently the Nvidia drivers are doing. For instance on my 7600GS + / the max frequency for the first VCO in case of the GPU clock is 300MHz. Indeed below + / 300MHz it moves to a single VCO. + / We could likely also check if the limits of the second divider and multiplier are 1. */ + if(clockIn < pll_limits->VCO1.maxFreq) + { + ClockSelectSingleVCO_nv40(clockIn, pll_limits, &bestM, &bestN, &bestP); + + /* Bit31 enables the first VCO */ + PLL = 0x80000000; + + /* Select only a single VCO; bit12 is apparently used when there is a single VCO (G7x) + / while bit8 is used when there are two VCOs, so it is apparently some disable bit. + / + / On some cards at least on G7x ones, a single VCO is used for the memory + / In this case the min and max reference dividers (M) are equal. + */ + if(pll_limits->VCO2.minM == pll_limits->VCO2.maxM) + PLL |= 0x1000; + else + PLL |= 0x100; + + /* Set the reference divider (M) and the feedback divider (N) */ + PLL2 = (bestN<<8) | bestM; + } + else + { + ClockSelectDoubleVCO_nv40(clockIn, pll_limits, &bestM, &bestM2, &bestN, &bestN2, &bestP); + + /* Bit31 enables the first VCO, bit30 the second */ + PLL = 0xc0000000; + /* Set the reference dividers (M, M2) and the feedback dividers (N, N2) */ + PLL2 = (bestN2<<24) | (bestM2<<16) | (bestN<<8) | bestM; + } + + /* Set the post divider */ + PLL |= (bestP<<16); + + if(reg == 0x4020) /* MPLL */ + { + unsigned int default_pll = nv_card->bios ? nv_card->bios->mpll : 0; + + /* This data comes from the init script tables of bios of the respective cards */ + if(!default_pll) + { + switch(nv_card->arch) + { + case NV40: + case NV41: /* I'm not sure if this is correct */ + case NV43: + case NV44: + default_pll = 0x2000001c; + break; + case NV46: + default_pll = 0x20000000; + break; + case NV47: + default_pll = 0x2400001c; + break; + case NV49: + case NV4B: + default_pll = 0x24800000; + break; + default: + printf("Unknown default pll for %#x\n", nv_card->arch); + } + } + + /* According to a vbtracetool log, this is done. Why a second post divider for the MPLL? */ + PLL |= (bestP + pll_limits->var1e) << 20; + /* Or with the 'empty' PLL but make sure that it doesn't adjust the post dividers or the enable bits */ + PLL |= (default_pll & 0x3f88ffff); + } + else /* NVPLL */ + { + unsigned int default_pll = nv_card->bios ? nv_card->bios->nvpll : 0; + if(!default_pll) + { + switch(nv_card->arch) + { + case NV40: + case NV41: /* I'm not sure if this is correct */ + case NV43: + default_pll = 0x0000001c; + break; + case NV44: + default_pll = 0xc000001c; + break; + case NV46: + default_pll = 0xc0000000; + break; + case NV47: + default_pll = 0x0001001f; + break; + case NV49: + case NV4B: + default_pll = 0x00010000; + break; + case C51: + default: + printf("Unknown default pll for %#x\n", nv_card->arch); + default_pll = 0xc0000000; /* this should only get reached for C51 */ + } + } + + /* Or with the 'empty' PLL but make sure that it doesn't adjust the post dividers or the enable bits */ + PLL |= (default_pll & 0x3f88ffff); + } + + /* TODO: at some stage we should perhaps also play nice with 0xc040. + / Before programming a PLL, we should disable it in there. Then + / we should put the PLL in program mode (?) by setting bit28. After that + / we should program the PLLs (first the dividers/multipliers and then the config), + / then re-enable the PLL in 0xc040 and then unset bit28 of the pll config register. + / At least this is what happens in a vbtracetool log. + */ + *pllOut = PLL; + *pllBOut = PLL2; + + if(nv_card->debug) + { + printf("register=%#x, clockIn=%d, calculated=%d\n", reg, clockIn, CalcSpeed_nv40(27000, bestM, bestM2, bestN, bestN2, bestP)); + printf("PLL=%#08x, PLL2=%08x\n", *pllOut, *pllBOut); + } +} + +static float nv40_get_gpu_speed() +{ + int pll = nv_card->PMC[0x4000/4]; + int pll2 = nv_card->PMC[0x4004/4]; + if(nv_card->debug == 1) + { + printf("NVPLL_COEFF=%08x\n", pll); + printf("NVPLL2_COEFF=%08x\n", pll2); + } + + return (float)GetClock_nv40(nv_card->base_freq, pll, pll2); +} + +static void nv40_set_gpu_speed(unsigned int nvclk) +{ + unsigned int PLL=0, PLL2=0; + nvclk *= 1000; + + ClockSelect_nv40(nvclk, 0x4000, &PLL, &PLL2); + + /* 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->PMC[0x4000/4] = PLL; + nv_card->PMC[0x4004/4] = PLL2; + } +} + +static float nv40_get_memory_speed() +{ + int pll = nv_card->PMC[0x4020/4]; + int pll2 = nv_card->PMC[0x4024/4]; + if(nv_card->debug == 1) + { + printf("MPLL_COEFF=%08x\n", pll); + printf("MPLL2_COEFF=%08x\n", pll2); + } + + return (float)GetClock_nv40(nv_card->base_freq, pll, pll2); +} + +static void nv40_set_memory_speed(unsigned int memclk) +{ + unsigned int PLL=0, PLL2=0; + memclk *= 1000; + + ClockSelect_nv40(memclk, 0x4020, &PLL, &PLL2); + + /* 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); + } + + /* It seems that different NV4X GPUs contain multiple memory clocks. + / A 6800 card has 4 of them, a 6600GT 2 of them and a NV44 (6200) 1. + / Very likely this is related to the width of the memory bus, which + / is 256bit on the 6800, 128bit on the 6600GT (NV43) and 64bit on the NV44. + / + / The code below handles the setting of the extra clockspeeds. + */ + switch(nv_card->arch) + { + case NV40: + case NV41: + case NV47: + nv_card->PMC[0x402c/4] = PLL; + nv_card->PMC[0x4030/4] = PLL2; + nv_card->PMC[0x4044/4] = PLL; + nv_card->PMC[0x4048/4] = PLL2; + case NV43: + case NV49: + case NV4B: + nv_card->PMC[0x4038/4] = PLL; + nv_card->PMC[0x403c/4] = PLL2; + case NV44: + case NV46: + nv_card->PMC[0x4020/4] = PLL; + nv_card->PMC[0x4024/4] = PLL2; + } + } +} + +static void nv40_reset_gpu_speed() +{ + /* Set the gpu speed */ + nv_card->PMC[0x4000/4] = nv_card->nvpll; + nv_card->PMC[0x4004/4] = nv_card->nvpll2; +} + +static void nv40_reset_memory_speed() +{ + /* Set the memory speed */ + nv_card->PMC[0x4024/4] = nv_card->mpll2; + + switch(nv_card->arch) + { + case NV40: + case NV41: + case NV47: + nv_card->PMC[0x402c/4] = nv_card->mpll; + nv_card->PMC[0x4030/4] = nv_card->mpll2; + nv_card->PMC[0x4044/4] = nv_card->mpll; + nv_card->PMC[0x4048/4] = nv_card->mpll2; + case NV43: + case NV49: + case NV4B: + nv_card->PMC[0x4038/4] = nv_card->mpll; + nv_card->PMC[0x403c/4] = nv_card->mpll2; + case NV44: + case NV46: + nv_card->PMC[0x4020/4] = nv_card->mpll; + nv_card->PMC[0x4024/4] = nv_card->mpll2; + } +} + +static void nv40_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 = nv40_get_gpu_speed; + nv_card->get_memory_speed = nv40_get_memory_speed; + nv_card->set_memory_speed = nv40_set_memory_speed; + nv_card->set_gpu_speed = nv40_set_gpu_speed; + nv_card->reset_gpu_speed = nv40_reset_gpu_speed; + nv_card->reset_memory_speed = nv40_reset_memory_speed; + } +} + +void nv40_init(void) +{ + nv_card->base_freq = 27000; + + nv_card->set_state = nv40_set_state; + nv_card->set_state(STATE_LOWLEVEL); /* Set the clock function pointers */ + + nv_card->get_default_mask = nv40_get_default_mask; + nv_card->get_hw_masked_units = nv40_get_hw_masked_units; + nv_card->get_sw_masked_units = nv40_get_sw_masked_units; + nv_card->get_pixel_pipelines = nv40_get_pixel_pipelines; + nv_card->get_vertex_pipelines = nv40_get_vertex_pipelines; + + /* 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(); + } + + /* For now enable modding on NV40 cards and NV43 revisions prior to A4; other cards are locked */ + if((nv_card->arch & NV40) || ((nv_card->arch & NV43) && (nv_card->get_gpu_revision() < 0xA4))) + { + nv_card->caps |= PIPELINE_MODDING; + nv_card->set_pixel_pipelines = nv40_set_pixel_pipelines; + nv_card->set_vertex_pipelines = nv40_set_vertex_pipelines; + } + + /* If the smartdimmer register contains a value (default 21) then smartdimmer is supported on the laptop; This should work on various 6200Go/7600Go cards */ + if((nv_card->PMC[0x15f0/4] & 0xff) && nv_card->gpu == MOBILE) + { + nv_card->caps |= SMARTDIMMER; + nv_card->get_smartdimmer = nv44_mobile_get_smartdimmer; + nv_card->set_smartdimmer = nv44_mobile_set_smartdimmer; + } + + /* Temperature monitoring; all cards after the NV40 feature an internal temperature sensor. + / Only it is disabled on most 6200/6600(GT) cards but we can re-enable it ;) + */ + if((nv_card->arch & (NV43 | NV44 | NV46 | NV47 | NV49 | NV4B)) && !(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))nv43_get_gpu_temp; + } + + /* Fanspeed 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. + / Should the NV44 use the NV43 codepath? + */ + if(((nv_card->arch & (NV40 | NV49)) && (nv_card->PMC[0x10f0/4] & 0x80000000)) && !(nv_card->caps & I2C_FANSPEED_MONITORING)) + { + nv_card->caps |= GPU_FANSPEED_MONITORING; + nv_card->get_fanspeed = nv40_get_fanspeed; + nv_card->set_fanspeed = nv40_set_fanspeed; + } + else if(((nv_card->arch & (NV41 | NV43 | NV44 | NV47 | NV4B)) && (nv_card->PMC[0x15f4/4] & 0x80000000)) && !(nv_card->caps & I2C_FANSPEED_MONITORING)) + { + nv_card->caps |= GPU_FANSPEED_MONITORING; + nv_card->get_fanspeed = nv43_get_fanspeed; + nv_card->set_fanspeed = nv43_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) + { + /* Most Geforce6 bioses just have one active entry but some Geforce6 6800(Ultra) bioses have 2 entries + / in that case the first one contains the highest clocks (3d?). Further there are 6600GT cards with + / also two entries for which the second entry contains the 3d clock. + */ + if((nv_card->bios->perf_entries == 1) || (nv_card->bios->perf_lst[0].nvclk > nv_card->bios->perf_lst[1].nvclk)) + { + nv_card->memclk_3d = (short)nv_card->bios->perf_lst[0].memclk; + nv_card->nvclk_3d = (short)nv_card->bios->perf_lst[0].nvclk; + } + else + { + /* 6600GT cards have 2d/3d clocks again; the second entries are the 3d ones. + / We use the 2d entries for the minimum clocks and the 3d ones for the maximum ones. + */ + nv_card->memclk_3d = (short)nv_card->bios->perf_lst[1].memclk; + nv_card->nvclk_3d = (short)nv_card->bios->perf_lst[1].nvclk; + } + nv_card->memclk_min = (short)(nv_card->bios->perf_lst[0].memclk * .75); + nv_card->memclk_max = nv_card->memclk_3d * 1.25; + nv_card->nvclk_min = (short)(nv_card->bios->perf_lst[0].nvclk * .75); + nv_card->nvclk_max = nv_card->nvclk_3d * 1.25; + + /* FIXME: Divide the memory clocks by two on Geforc7600/7900 cards because we program the 'real' clocks for those instead of the effective DDR ones which are twice as high */ + if(nv_card->arch & (NV46 | NV49 | NV4B)) + { + nv_card->memclk_min /= 2; + nv_card->memclk_max /= 2; + } + } + else + { + float memclk = GetClock_nv40(nv_card->base_freq, nv_card->mpll, nv_card->mpll2); + float nvclk = GetClock_nv40(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); + } +} diff --git a/nvclock/nv40.o b/nvclock/nv40.o new file mode 100644 index 0000000000000000000000000000000000000000..38237fd6f3a7afd16ae6fc9760ef3bda31b50c85 GIT binary patch literal 12912 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 literal 0 HcmV?d00001 diff --git a/nvclock/nv50.c b/nvclock/nv50.c new file mode 100644 index 0000000..1c3bf2f --- /dev/null +++ b/nvclock/nv50.c @@ -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 +#include +#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); + } +} diff --git a/nvclock/nv50.o b/nvclock/nv50.o new file mode 100644 index 0000000000000000000000000000000000000000..ccf855f5a75e80c1ec70ae3b4d48a7dbe7ea483b GIT binary patch literal 5084 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 A-~a#s literal 0 HcmV?d00001 diff --git a/nvclock/nvclock.h b/nvclock/nvclock.h new file mode 100644 index 0000000..086ea18 --- /dev/null +++ b/nvclock/nvclock.h @@ -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 */ diff --git a/nvclock/nvcontrol.c b/nvclock/nvcontrol.c new file mode 100644 index 0000000..6c0c936 --- /dev/null +++ b/nvclock/nvcontrol.c @@ -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 +#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); + } +} diff --git a/nvclock/nvreg.h b/nvclock/nvreg.h new file mode 100644 index 0000000..01776a7 --- /dev/null +++ b/nvclock/nvreg.h @@ -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) +*/ diff --git a/nvclock/overclock.c b/nvclock/overclock.c new file mode 100644 index 0000000..5ed1268 --- /dev/null +++ b/nvclock/overclock.c @@ -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 +#include +#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; + } +} diff --git a/nvclock/overclock.o b/nvclock/overclock.o new file mode 100644 index 0000000000000000000000000000000000000000..25d0402e0ab2a5fae67fc27e2f7531a9e74c3405 GIT binary patch literal 4888 zcmb7HVQgE~6~520Q`36H?ZQ@dEw#F2N(Hf!Hb6#O2uW`PTj;VDXVpd;LSmPcII*1P zsx3;E&h5nGkt%2)I*A`pCg`N0X=q!cYCqzX%`GZZfFkM!+NjE^phR8JOlwM5_MQ9A zOY8&`cBSXiO@)FIu}ru{!1&kB_@sM8 zdPk(^(&r=6ed%nf`)0vOdGpVnkRdavaAu@2oeJMH18Gwh8t)s4_rEYM#NTrNqVY6F z$0sXI=_#IpPea1OlNfh1n^MdhA1yHC1&}k9wAaGvNPBZ?thShlu{MhIc#D|6)- zIdVLaktw~VNMx)q4CkNy&hY4p>ZQ`&8J+jiJO9FT$S~b@w?P#!y}Wp1g*aW4szkXd4{QqkOwnR?Vsh zUBZQ(LNaXzEFn_uJJ$E`v{6^&w)46ROSeb#^?NYHW-cP8=so8$9JZ{lUpd8A96WW2 zt#UbeIvltKi-{Z=jD?jY*5D3p@oIW$7G19Cw`b9)rWa(!`d!|}BR@~6Qx$Q09g9X4 zX{VE5+4rv0uv~|##NXIMQ9R?&*UXvI&e@$;xtq}Xl}Gi1>!HHD4GU$Psyb7|N2^f9 zmkdyqop25O4L3^2sJ3J2iH{aSZM|hC*%u2@B_LX+rU@mheEZu}H`(jMjH^&unHGV* zgb*?<0WC$=nBECm=$xfxpeN?E6`;Si_mb?b1U+L@v-f4}Am>1alD#iM<`Cu@fS?rK zN9Zit2v8|B;xD5+r@!0MSI+5whwdwQgjwY&PS0qxOTQAZ`@F2-^{|8cEdX|Z0$m=C ztmMU3TSSGs;N*_{8}G&6&b#qm$f(bscw)}b5tH)_<8NzZq@>ASiqix3)|0&yCu*#v zz)+j`juKeOrP(motP``J&Vn*nP$E)|T!Z@Q_E9ntzZQ=ECU)HOI$saWm zp~NQzeNHX(Zia%AtKL0Ny*vI^1wO+lnP}lH#jU;$`ibCYso367Usu;u^Aovk+m_<8 z%Kb~l!}A5zft_UnKjrkZL3ufdFREv2@ao{9Hh#Z_nfpa;P0hW&lG?v5R%&! zcqlGjJQRj(e=G5IJr0@OY=1VlFLLrqOIh=J9yu6jKx_F~`Kx2-&Ch{p@yjzS?QUc{@AbJ7PG zql~?b&olm!@g2q#<2d6D#(cz^{H&m- z`1uS-`}mUOx8iKlJnMioe+6STV*_J|F~)e9@i&ae8UMt1n(;i4{9h!F`TxUm2M#pN zx1132H9%TdG0X2Jj&VOD#3xJ-$zk^l>yI)1f$^JlSI;nip80Q?zsmds^Ea8FWS(ID59Y(nKVW{D`55yXjQM!QX}z}*LbeL% zKvDVu%kN_Oy)2h3Z(s~E>Vz1707&Bxv-~;67a8ARJjFQ3ILvsF5OKK7{2Jp7qYD>~ zS}&uIu^dSG{2}wLi~*MKX1<3I>wK2^3(VhO{#WMzWd0%Ze>4A#aTzWYTE{BJ^^BE_ VwTumn5kkxxXZ|$famLpf{|7rZP+$N6 literal 0 HcmV?d00001 diff --git a/nvclock/tete.c b/nvclock/tete.c new file mode 100644 index 0000000..f39bdc0 --- /dev/null +++ b/nvclock/tete.c @@ -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 +#include +#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()); +} diff --git a/nvclock/utils.c b/nvclock/utils.c new file mode 100644 index 0000000..a3581ca --- /dev/null +++ b/nvclock/utils.c @@ -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 + +/* 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<)=-+~|U6O#GxKm|%)Yja`^%toKacKqZ2{SOqI%KI49V z);u|rj+^ZS`Lron=yW^Fh#H-4M^dRU9PZ_x`F#HKd-A0a*hnx1LDeRLdl2l{SdbY{ zxkc1LxUPVVKUKHM+T(-zc&>9|w`<0iOEqG7pWC@U#ZEJN=T6gBOzEYj^ zIcfWozNJYey`bbM#uk(;AhX>y?Mo+>oUv#;S^QzV(bJNoWOXGcQ4OWnKMHxK9h#?h zU!`mM;u6`(o~-ere%{FH%aZYd*8U+LcN= zC;eIV`AZk)SnrwULkGOA2Vu05;em04j7AtI$Y^36Eg&tP1Kz_3*Pdfunv7xVXK8+; zIV$iD;bUQ2_=-65c61!Asq?B49*x#C%Pn$KgLbec)+jgbuZe}!29Mk`7~rwKW9PMWoX+X*o$#Wz-91s&{tG@<{O z4)q|vl?M5Sh+D&|5653zAEyoB+)y8tx>_H;SLA~pW0dmA1=D;YhN%cNo%DEU6(AhY zU~Hpl?ExG*9}F=p9k4jXJ1TsZd|PRv57$%%V!wlZ&0Bup zpMO2@;x~X>T`a7!n;l!rU^go)=B2B>Zlf1hQOEYKh0T4`ujBZ!K>L6YdY0g!k+>XW z!D!^Fs&W67Si&FBfhBLl0@@f} zh4YlCSFHkj32oF&+yVHbf +#include +#include +#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) +{ +} + diff --git a/nvclock/w83781d.o b/nvclock/w83781d.o new file mode 100644 index 0000000000000000000000000000000000000000..6215ea3157d01a23cf673dc3d4baa70ccafecee6 GIT binary patch literal 1588 zcmb7E-%Aux6h7;Yxz&RvQc;Ert+bX7u5P+W50!39(C-q1lx22j*2H#Zn7!L7AqM-> z5Jpcy|3WXmge^V!Be32IUkZ8?eE3)@Kk)$?h6GL`$E!nbUuv-Y$>$*t6VNm#W#fK zuGdpL6W^?znJsSBv+Cm3U_OLVnZ@%Bhhb3)(GHb(d!CZrI9j4Y)=oG^mVz zY*V6u4-0#$IfZ6dHGI$i6W%VaM_mnuy(wJ{CcGZpY-4s+q8^w1Z>fHJ$(~#KW`BMX z-=Ip)mGd=~!q-mnz9Q>BESq?1> zjOZytSAzqw&hA*G5Bf)Sqo46;#z0mTrhuHv1+40MBmV`y7Or}cm>Wn# z;}A|!8*$Rzqy-;BN9az`hy~T5k*)#tNA=MoqH}WRYsK@MMw=KSpWp)Bb_srzg`Lp* z*C6iRfw(vwC&~}FscZP5#>Gg#5C1jJc!p4qCwk1KpgvCT;*njPz5~U!;G}r^O}%k( z71zr+pW`I|1fq#e(!RuBAU;A)l@fqyO-)*6)}!8bn1vlj4zqh4(1>+c n;m7XcIDqKGW(JAlr6_P-B#uqLB1HWX$KM=}VC!VhBU1byUQ9I~ literal 0 HcmV?d00001 diff --git a/nvclock/w83l785r.c b/nvclock/w83l785r.c new file mode 100644 index 0000000..45645e7 --- /dev/null +++ b/nvclock/w83l785r.c @@ -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 +#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<3YM{OqMz9zmv`M5r6!?)u+c`V;>Wmt7;Lh)!^E>z4-#s&T9*PZD z4F&^nGe8O)cmVAwDq~MVKAZ##v?`y&oB31Xj#6i{EY{gf4M%Q15f;ZN2SM@-C!4XtOs6xUy5bYp7F z6JPCEi+R>3#kJmufxd)^r!IiG+_8;jWohSYbkD3TMR#-6;uF+s8WH|Y9Mbc5rOEfb zynt%jdc9pfuiM(@C~K&6l(%3AZK(!eq!#LHYpbmJ^^#Zi`K)%KTqr9Flv@2ILP??3 z-zyar3wEKhKxF`g;Gjo#b|K0E74Py{KAI&X`JZPlCea6cf+f>kQ;f_(FtO z5Yg1a@P56EIH&x7mZH4oB%?lf9wFC8Otu*y3xnx6r}L4%EF9E3Z9N8;IXD zz7ywFc!*)fKR~1#be;K+5slcuN6deY$c^elzoV=VFIPZt?N2-1k}SDo80dCXTq~_@ zk(}vNz + */ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/i2c/xf86i2c.c,v 1.6 1999/06/12 15:37:08 dawes Exp $ */ + +#include "xfree.h" +#include + +#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 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 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 to free it. If you set , the function + * xf86DestroyI2CDevRec will be called for all devices linked to the bus + * first, passing down the 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; +} diff --git a/nvclock/xf86i2c.h b/nvclock/xf86i2c.h new file mode 100644 index 0000000..1ea8a56 --- /dev/null +++ b/nvclock/xf86i2c.h @@ -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 */ diff --git a/nvclock/xf86i2c.o b/nvclock/xf86i2c.o new file mode 100644 index 0000000000000000000000000000000000000000..aae1f8a3e4c13c0be9aa83dc295916da5981cc9c GIT binary patch literal 6136 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 literal 0 HcmV?d00001 diff --git a/nvclock/xfree.h b/nvclock/xfree.h new file mode 100644 index 0000000..fca2777 --- /dev/null +++ b/nvclock/xfree.h @@ -0,0 +1,76 @@ +/* NVTV xfree -- Dirk Thierbach + * + * Header: All definitions from xfree that are needed. + * + */ + +#ifndef _XFREE_H +#define _XFREE_H 1 + +#include +#include + +#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 diff --git a/wmtempnv/Makefile b/wmtempnv/Makefile index 34340a1..68021b3 100644 --- a/wmtempnv/Makefile +++ b/wmtempnv/Makefile @@ -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 $@ $< diff --git a/wmtempnv/wmtempnv_mask.xbm b/wmtempnv/wmtempnv_mask.xbm index 80ff536..e595a6e 100644 --- a/wmtempnv/wmtempnv_mask.xbm +++ b/wmtempnv/wmtempnv_mask.xbm @@ -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,