#include <Debug.h>
#include "I2CPort.h"
CI2CPort::CI2CPort(CRadeon & radeon, int rate)
: fRadeon(radeon),
fNfactor(0),
fMfactor(0),
fTimeLimit(0),
si(NULL)
{
PRINT(("CI2CPort::CI2CPort()\n"));
if( fRadeon.InitCheck() == B_OK ) {
int refFreq, refDiv, minFreq, maxFreq, xclock;
double n;
fRadeon.GetPLLParameters(refFreq, refDiv, minFreq, maxFreq, xclock);
si = fRadeon.GetSharedInfo();
if ( si->asic == rt_rv200 ) {
n = (xclock * 40000.0) / (1.0 * rate);
} else {
n = (xclock * 10000.0) / (4.0 * rate);
}
for (fNfactor = 1; fNfactor < 255; fNfactor++) {
if (fNfactor * (fNfactor - 1) > n)
break;
}
fMfactor = fNfactor - 1;
fTimeLimit = 2 * fNfactor;
fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 0x00ff0000,
C_RADEON_I2C_SEL | C_RADEON_I2C_EN);
fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 0x000000ff,
C_RADEON_I2C_DONE | C_RADEON_I2C_NACK |
C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST |
C_RADEON_I2C_DRIVE_EN | C_RADEON_I2C_DRIVE_SEL);
#if DEBUG
PRINT(("CI2CPort::CI2CPort() - I2C devices found at ports: "));
for (int address = 0x80; address <= 0xff; address += 0x02) {
if (Probe(address))
PRINT(("0x%02x ", address));
}
PRINT(("\n"));
#endif
}
}
CI2CPort::~CI2CPort()
{
PRINT(("CI2CPort::~CI2CPort()\n"));
if( fRadeon.InitCheck() == B_OK ) {
fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 0);
}
}
status_t CI2CPort::InitCheck() const
{
if (fRadeon.InitCheck() != B_OK)
return B_ERROR;
if ( si == NULL )
return B_ERROR;
if ( si->has_no_i2c ) {
PRINT(("This Chips I2C is BLACKLISTED!"));
return B_ERROR;
}
return B_OK;
}
CRadeon & CI2CPort::Radeon() const
{
return fRadeon;
}
bool CI2CPort::Probe(int address)
{
char buffer[1];
return Read(address, buffer, sizeof(buffer));
}
bool CI2CPort::Write(int address, const char * buffer, int length)
{
if (Send(address, buffer, length, true, true) == length)
return true;
return false;
}
bool CI2CPort::Read(int address, char * buffer, int length)
{
if (Receive(address, buffer, length, true, true) == length)
return true;
return false;
}
bool CI2CPort::Write(int address, const char * buffer, int length, char * result, int reslen)
{
if (Send(address, buffer, length, true, false) == length)
if (Receive(address, result, reslen, true, true) == reslen)
return true;
return false;
}
int CI2CPort::Register(int address, int index)
{
char value = index;
if (Send(address, &value, sizeof(value), true, false) == sizeof(value)) {
if (Receive(address, &value, sizeof(value), true, true) == sizeof(value))
return value & 0xff;
}
PRINT(("CI2CPort::Register() - error\n"));
return -1;
}
void CI2CPort::SetRegister(int address, int index, int value)
{
char buffer[2];
buffer[0] = index;
buffer[1] = value;
if (Send(address, buffer, sizeof(buffer), true, true) != sizeof(buffer))
PRINT(("CI2CPort::SetRegister() - error\n"));
}
int CI2CPort::Send(int address, const char * buffer, int length, bool start, bool stop)
{
fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
C_RADEON_I2C_DONE | C_RADEON_I2C_NACK |
C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST);
fRadeon.SetRegister(C_RADEON_I2C_DATA, address & ~(1));
for (int offset = 0; offset < length; offset++)
fRadeon.SetRegister(C_RADEON_I2C_DATA, buffer[offset]);
if (si->asic >= rt_r200) {
fRadeon.SetRegister(C_RADEON_I2C_CNTL_1,
(fTimeLimit << 24) | length |
C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 0x010);
} else {
fRadeon.SetRegister(C_RADEON_I2C_CNTL_1,
(fTimeLimit << 24) | length |
C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 0x100);
}
fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
(fNfactor << 24) | (fMfactor << 16) |
C_RADEON_I2C_GO | C_RADEON_I2C_DRIVE_EN |
(start ? C_RADEON_I2C_START : 0) | (stop ? C_RADEON_I2C_STOP : 0));
for (int wait = 0; wait < 100; wait++) {
if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0)
break;
}
if (WaitAck() != C_RADEON_I2C_DONE) {
Stop();
return 0;
}
return length;
}
int CI2CPort::Receive(int address, char * buffer, int length, bool start, bool stop)
{
fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
C_RADEON_I2C_DONE | C_RADEON_I2C_NACK |
C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST);
fRadeon.SetRegister(C_RADEON_I2C_DATA, address | 0x00000001);
if (si->asic >= rt_r200) {
fRadeon.SetRegister(C_RADEON_I2C_CNTL_1,
(fTimeLimit << 24) | C_RADEON_I2C_EN | C_RADEON_I2C_SEL |
length | 0x010);
} else {
fRadeon.SetRegister(C_RADEON_I2C_CNTL_1,
(fTimeLimit << 24) | C_RADEON_I2C_EN | C_RADEON_I2C_SEL |
length | 0x100);
}
fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
(fNfactor << 24) | (fMfactor << 16) | C_RADEON_I2C_GO |
(start ? C_RADEON_I2C_START : 0) | (stop ? C_RADEON_I2C_STOP : 0) |
C_RADEON_I2C_DRIVE_EN | C_RADEON_I2C_RECEIVE);
for (int wait = 0; wait < 100; wait++) {
if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0)
break;
}
if (WaitAck() != C_RADEON_I2C_DONE)
return 0;
snooze(1000);
for (int offset = 0; offset < length; offset++) {
buffer[offset] = fRadeon.Register(C_RADEON_I2C_DATA) & 0xff;
}
return length;
}
int CI2CPort::WaitAck()
{
for (int wait = 0; wait < 100; wait++) {
int control = fRadeon.Register(C_RADEON_I2C_CNTL_0);
if ((control & C_RADEON_I2C_HALT) != 0)
return C_RADEON_I2C_HALT;
if ((control & C_RADEON_I2C_NACK) != 0)
return C_RADEON_I2C_NACK;
if ((control & C_RADEON_I2C_DONE) != 0)
return C_RADEON_I2C_DONE;
snooze(1000);
}
PRINT(("CI2CPort::WaitAck() - Time out!\n"));
return C_RADEON_I2C_HALT;
}
void CI2CPort::Stop()
{
fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | C_RADEON_I2C_HALT, 0);
fRadeon.SetRegister(C_RADEON_I2C_CNTL_0_PLUS1,
C_RADEON_I2C_ABORT | C_RADEON_I2C_GO, C_RADEON_I2C_ABORT | C_RADEON_I2C_GO);
for (int wait = 0; wait < 100; wait++) {
if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0)
snooze(1000);
}
PRINT(("CI2CPort::Stop() - Time out!\n"));
}