/* NVClock 0.8 - Linux overclocker for NVIDIA cards * * site: http://nvclock.sourceforge.net * * Copyright(C) 2001-2006 Roderick Colenbrander * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * I2C support needed for hardware monitoring, this code is partly based on code from nvtv * and the opensource xfree86 nv driver. */ /* NVTV TV common routines -- Dirk Thierbach * * 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.11 2006/07/31 14:44:32 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 "nvclock.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) Bool I2CAddress(I2CDevPtr d, I2CSlaveAddr addr); 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.ptr; /* 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.ptr; 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; } static 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.ptr = (int*)bus; /* abuse a pointer for now */ 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); for(bus = 0; bus < num_busses; bus++) { for(dev = busses[bus]->FirstDev; dev; dev = dev->NextDev) { 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_init(void) { /* For hardware monitoring support we need access to the i2c bus. Each card / has 2 or 3 of these busses. Currently we will create these busses here in set_card / and store them in the card list. It should also be possible to create just 3 busses and store / those in the nv_card struct and have each card use them and limit the number of used busses by / a variable but the issue is that this is not very nice and perhaps even incorrect. For example / different cards might need different i2c bus timings and further we mess up the xfree86 i2c structures / if we mix dev/bus structures from different cards. / / Once the busses have been created they will be probed for sensor chips. If a sensor is found / a i2c device pointer will be stored. Using this pointer we can access the chip using standard / xf86 i2c functions. */ 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); } 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; } }