/* 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; }
/* 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; } }
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; }