Beispiel #1
0
/* 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; i<header->num_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
}
int PTnVmon::probe_devices()
{
	IOPCIDevice* PTCard=NULL;
	int i=0;
	UInt16 vendor_id;
	IOLog("PTKawainVi: started\n");
	OSData* idKey;
	OSDictionary * iopci = serviceMatching("IOPCIDevice");
	OSString* str;
#if __LP64__
	mach_vm_address_t addr;
	//mach_vm_size_t size;
#else
	vm_address_t addr;
	//vm_size_t size;
#endif
	nvclock.num_cards=0;
	if (iopci) {
		OSIterator * iterator = getMatchingServices(iopci);
		if (iterator) {
			while (PTCard = OSDynamicCast(IOPCIDevice, iterator->getNextObject())) {
				vendor_id=0;
				str=OSDynamicCast(OSString, PTCard->getProperty("IOName"));
				idKey=OSDynamicCast(OSData, PTCard->getProperty("vendor-id"));
				if (idKey)
					vendor_id=*(UInt32*)idKey->getBytesNoCopy();
				if ((str->isEqualTo("display"))&&(vendor_id==0x10de)){
					PTCard->setMemoryEnable(true);
					nvio = PTCard->mapDeviceMemoryWithIndex(0);		
					addr = (vm_address_t)nvio->getVirtualAddress();
					idKey=OSDynamicCast(OSData, PTCard->getProperty("device-id"));
					if (idKey)
						nvclock.card[i].device_id=*(UInt32*)idKey->getBytesNoCopy();
					IOLog("Vendor ID: %x, Device ID: %x\n", vendor_id, nvclock.card[i].device_id);
					nvclock.card[i].arch = get_gpu_arch(nvclock.card[i].device_id);
					IOLog("Architecture: %x\n", nvclock.card[i].arch);
					nvclock.card[i].number = i;
					nvclock.card[i].card_name = (char*)get_card_name(nvclock.card[i].device_id, &nvclock.card[i].gpu);
					IOLog("%s\n", nvclock.card[i].card_name);
					nvclock.card[i].state = 0;
					
					nvclock.card[i].reg_address = addr;
					map_mem_card( &nvclock.card[i], addr );
					
					
					
					
					i++;
					
				}
			}
		}
	}
	
	nvclock.num_cards = i;
	
	if (!i) {
		IOLog("PTKawainVi: No nVidia graphics adapters found\n");
	}	
	
	return nvclock.num_cards;
}
Beispiel #3
0
/* 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; entry<header->num_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;
	}
}
Beispiel #4
0
bool NVClockX::start(IOService * provider)
{
	HWSensorsDebugLog("Starting...");
	
	if (!super::start(provider)) 
        return false;
    
    if ((videoCard = (IOPCIDevice*)provider)) 
    {
        if (videoCard->setMemoryEnable(true)) 
        {
            if ((nvio = videoCard->mapDeviceMemoryWithIndex(0))) 
            {
                IOVirtualAddress addr = nvio->getVirtualAddress();
                
                if (OSData * data = OSDynamicCast(OSData, videoCard->getProperty("device-id"))) 
                {
                    nvclock.card[nvclock.num_cards].device_id=*(UInt32*)data->getBytesNoCopy();
                    
                    
                    nvclock.card[nvclock.num_cards].arch = get_gpu_arch(nvclock.card[nvclock.num_cards].device_id);
                    nvclock.card[nvclock.num_cards].number = nvclock.num_cards;
                    nvclock.card[nvclock.num_cards].card_name = (char*)get_card_name(nvclock.card[nvclock.num_cards].device_id, &nvclock.card[nvclock.num_cards].gpu);
                    nvclock.card[nvclock.num_cards].state = 0;
                    
                    //nvclock.card[nvclock.num_cards].reg_address = addr;
                    
                    //map_mem_card(&nvclock.card[nvclock.num_cards], addr);
                    // Map the registers of the nVidia chip 
                    // normally pmc is till 0x2000 but extended it for nv40 
                    nvclock.card[nvclock.num_cards].PEXTDEV = (volatile unsigned int*)(addr + 0x101000);
                    nvclock.card[nvclock.num_cards].PFB     = (volatile unsigned int*)(addr + 0x100000);
                    nvclock.card[nvclock.num_cards].PMC     = (volatile unsigned int*)(addr + 0x000000);
                    nvclock.card[nvclock.num_cards].PCIO    = (volatile unsigned char*)(addr + 0x601000);
                    nvclock.card[nvclock.num_cards].PDISPLAY= (volatile unsigned int*)(addr + NV_PDISPLAY_OFFSET);
                    nvclock.card[nvclock.num_cards].PRAMDAC = (volatile unsigned int*)(addr + 0x680000);
                    nvclock.card[nvclock.num_cards].PRAMIN  = (volatile unsigned int*)(addr + NV_PRAMIN_OFFSET);
                    nvclock.card[nvclock.num_cards].PROM    = (volatile unsigned char*)(addr + 0x300000);
                    
                    // On Geforce 8xxx cards it appears that the pci config header has been moved 
                    if(nvclock.card[nvclock.num_cards].arch & NV5X)
                        nvclock.card[nvclock.num_cards].PBUS = (volatile unsigned int*)(addr + 0x88000);
                    else
                        nvclock.card[nvclock.num_cards].PBUS = nvclock.card[nvclock.num_cards].PMC + 0x1800/4;
                    
                    nvclock.card[nvclock.num_cards].mem_mapped = 1;
                    
                    HWSensorsInfoLog("%s device-id=0x%x arch=0x%x", 
                                     nvclock.card[nvclock.num_cards].card_name, 
                                     nvclock.card[nvclock.num_cards].device_id, 
                                     nvclock.card[nvclock.num_cards].arch);
                    
                    nvclock.num_cards++;
                }
                else HWSensorsWarningLog("device-id property not found");                
            }
            else {
                HWSensorsWarningLog("failed to map device's memory");
                return false;
            }
        }
    }else {
        HWSensorsWarningLog("failed to assign PCI device");
        return false;
    }
    
	char key[7];
	
	nvclock.dpy = NULL;
	
	for (int index = 0; index < nvclock.num_cards; index++) {
		/* set the card object to the requested card */
		if (!set_card(index)){
			char buffer[256];
			HWSensorsWarningLog("%s", get_error(buffer, 256));
			return false;
		}

        OSData *bios = OSDynamicCast(OSData, videoCard->getProperty("vbios"));

        nvclock.card[index].bios = read_bios(bios ? bios->getBytesNoCopy() : NULL);
        
		/* Check if the card is supported, if not print a message. */
		if(nvclock.card[index].gpu == UNKNOWN){
			HWSensorsWarningLog("it seems your card isn't officialy supported yet");
			HWSensorsWarningLog("please tell the author the pci_id of the card for further investigation");
			HWSensorsWarningLog("continuing anyway");
		}
        
        SInt8 cardIndex = getVacantGPUIndex();
        
        if (cardIndex < 0) {
            HWSensorsWarningLog("failed to obtain vacant GPU index");
            return false;
        }
        
		if(nv_card->caps & (GPU_TEMP_MONITORING)) {
            HWSensorsInfoLog("registering temperature sensors");
            
            if(nv_card->caps & BOARD_TEMP_MONITORING) {
                snprintf(key, 5, KEY_FORMAT_GPU_DIODE_TEMPERATURE, cardIndex);
                addSensor(key, TYPE_SP78, 2, kNVCLockDiodeTemperatureSensor, index);
                
				snprintf(key, 5, KEY_FORMAT_GPU_HEATSINK_TEMPERATURE, cardIndex);
				addSensor(key, TYPE_SP78, 2, kNVCLockBoardTemperatureSensor, index);
			}
            else {
                snprintf(key, 5, KEY_FORMAT_GPU_PROXIMITY_TEMPERATURE, cardIndex);
                addSensor(key, TYPE_SP78, 2, kNVCLockBoardTemperatureSensor, index);
            }
		}
		
		if (nv_card->caps & I2C_FANSPEED_MONITORING || nv_card->caps & GPU_FANSPEED_MONITORING){
            HWSensorsInfoLog("registering tachometer sensors");
            
            char title[6]; 
            
            snprintf (title, 6, "GPU %X", cardIndex);
            
			addTachometer(index, title);
		}
		
        HWSensorsInfoLog("registering frequency sensors");
        
        snprintf(key, 5, KEY_FAKESMC_FORMAT_GPU_FREQUENCY, index);
        addSensor(key, TYPE_UI32, TYPE_UI32_SIZE, kNVCLockCoreFrequencySensor, index);
        
        snprintf(key, 5, KEY_FAKESMC_FORMAT_GPU_MEMORY_FREQUENCY, index);
        addSensor(key, TYPE_UI32, TYPE_UI32_SIZE, kNVCLockMemoryFrequencySensor, index);
        
        snprintf(key, 5, KEY_FAKESMC_FORMAT_GPU_SHADER_FREQUENCY, index);
        addSensor(key, TYPE_UI32, TYPE_UI32_SIZE, kNVCLockMemoryFrequencySensor, index);
		
		/*OSNumber* fanKey = OSDynamicCast(OSNumber, getProperty("FanSpeedPercentage"));
         
         if((fanKey!=NULL)&(nv_card->set_fanspeed!=NULL)) {
         HWSensorsInfoLog("Changing fan speed to %d", fanKey->unsigned8BitValue());
         nv_card->set_fanspeed(fanKey->unsigned8BitValue());
         }
         
         OSNumber* speedKey=OSDynamicCast(OSNumber, getProperty("GPUSpeed"));
         
         if ((speedKey!=NULL)&(nv_card->caps&GPU_OVERCLOCKING)) {
         HWSensorsInfoLog("Default speed %d", (UInt16)nv_card->get_gpu_speed());
         //HWSensorsInfoLog("%d", speedKey->unsigned16BitValue());
         nv_card->set_gpu_speed(speedKey->unsigned16BitValue());
         HWSensorsInfoLog("Overclocked to %d", (UInt16)nv_card->get_gpu_speed());
         }*/
	}
    
    registerService();
	
	return true;
}