/* NVClock 0.8 - Linux overclocker for NVIDIA cards
*
* site: http://nvclock.sourceforge.net
*
* Copyright(C) 2001-2006 Roderick Colenbrander
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
* I2C support needed for hardware monitoring, this code is partly based on code from nvtv
* and the opensource xfree86 nv driver.
*/
/* NVTV TV common routines -- Dirk Thierbach <dthierbach@gmx.de>
*
* This file is part of nvtv, a tool for tv-output on NVidia cards.
*
* nvtv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* nvtv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
* $Id: i2c.c,v 1.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 <unistd.h>
#include <stdarg.h>
#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;
}
}
syntax highlighted by Code2HTML, v. 0.9.1