1 /****************************************************************************** 2 / 3 / File: I2C.cpp 4 / 5 / Description: ATI Radeon I2C Serial Bus interface. 6 / 7 / Copyright 2001, Carlos Hasan 8 / 9 *******************************************************************************/ 10 11 #include <Debug.h> 12 #include "I2CPort.h" 13 14 CI2CPort::CI2CPort(CRadeon & radeon, int rate) 15 : fRadeon(radeon), 16 fNfactor(0), 17 fMfactor(0), 18 fTimeLimit(0), 19 si(NULL) 20 { 21 22 PRINT(("CI2CPort::CI2CPort()\n")); 23 24 if( fRadeon.InitCheck() == B_OK ) { 25 int refFreq, refDiv, minFreq, maxFreq, xclock; 26 double n; 27 28 fRadeon.GetPLLParameters(refFreq, refDiv, minFreq, maxFreq, xclock); 29 si = fRadeon.GetSharedInfo(); 30 31 if ( si->asic == rt_rv200 ) { 32 n = (xclock * 40000.0) / (1.0 * rate); 33 } else { 34 n = (xclock * 10000.0) / (4.0 * rate); 35 } 36 37 for (fNfactor = 1; fNfactor < 255; fNfactor++) { 38 if (fNfactor * (fNfactor - 1) > n) 39 break; 40 } 41 fMfactor = fNfactor - 1; 42 fTimeLimit = 2 * fNfactor; 43 44 // enable I2C bus 45 fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 0x00ff0000, 46 C_RADEON_I2C_SEL | C_RADEON_I2C_EN); 47 48 fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 0x000000ff, 49 C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | 50 C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST | 51 C_RADEON_I2C_DRIVE_EN | C_RADEON_I2C_DRIVE_SEL); 52 53 #if DEBUG 54 PRINT(("CI2CPort::CI2CPort() - I2C devices found at ports: ")); 55 for (int address = 0x80; address <= 0xff; address += 0x02) { 56 if (Probe(address)) 57 PRINT(("0x%02x ", address)); 58 } 59 PRINT(("\n")); 60 #endif 61 } 62 } 63 64 CI2CPort::~CI2CPort() 65 { 66 PRINT(("CI2CPort::~CI2CPort()\n")); 67 if( fRadeon.InitCheck() == B_OK ) { 68 // disable I2C bus 69 fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 0); 70 } 71 } 72 73 status_t CI2CPort::InitCheck() const 74 { 75 if (fRadeon.InitCheck() != B_OK) 76 return B_ERROR; 77 78 if ( si == NULL ) 79 return B_ERROR; 80 81 if ( si->has_no_i2c ) { 82 PRINT(("This Chips I2C is BLACKLISTED!")); 83 return B_ERROR; 84 } 85 return B_OK; 86 } 87 88 CRadeon & CI2CPort::Radeon() const 89 { 90 return fRadeon; 91 } 92 93 bool CI2CPort::Probe(int address) 94 { 95 char buffer[1]; 96 97 return Read(address, buffer, sizeof(buffer)); 98 } 99 100 bool CI2CPort::Write(int address, const char * buffer, int length) 101 { 102 if (Send(address, buffer, length, true, true) == length) 103 return true; 104 return false; 105 } 106 107 bool CI2CPort::Read(int address, char * buffer, int length) 108 { 109 if (Receive(address, buffer, length, true, true) == length) 110 return true; 111 return false; 112 } 113 114 bool CI2CPort::Write(int address, const char * buffer, int length, char * result, int reslen) 115 { 116 if (Send(address, buffer, length, true, false) == length) 117 if (Receive(address, result, reslen, true, true) == reslen) 118 return true; 119 return false; 120 } 121 122 int CI2CPort::Register(int address, int index) 123 { 124 char value = index; 125 126 if (Send(address, &value, sizeof(value), true, false) == sizeof(value)) { 127 if (Receive(address, &value, sizeof(value), true, true) == sizeof(value)) 128 return value & 0xff; 129 } 130 PRINT(("CI2CPort::Register() - error\n")); 131 return -1; 132 } 133 134 void CI2CPort::SetRegister(int address, int index, int value) 135 { 136 char buffer[2]; 137 138 buffer[0] = index; 139 buffer[1] = value; 140 141 if (Send(address, buffer, sizeof(buffer), true, true) != sizeof(buffer)) 142 PRINT(("CI2CPort::SetRegister() - error\n")); 143 } 144 145 int CI2CPort::Send(int address, const char * buffer, int length, bool start, bool stop) 146 { 147 //fRadeon.WaitForFifo(4 + length); 148 149 // clear status bit 150 fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 151 C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | 152 C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST); 153 154 // write address 155 fRadeon.SetRegister(C_RADEON_I2C_DATA, address & ~(1)); 156 157 // write data 158 for (int offset = 0; offset < length; offset++) 159 fRadeon.SetRegister(C_RADEON_I2C_DATA, buffer[offset]); 160 161 // 162 if (si->asic >= rt_r200) { 163 fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 164 (fTimeLimit << 24) | length | 165 C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 0x010); 166 } else { 167 fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 168 (fTimeLimit << 24) | length | 169 C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 0x100); 170 } 171 172 fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 173 (fNfactor << 24) | (fMfactor << 16) | 174 C_RADEON_I2C_GO | C_RADEON_I2C_DRIVE_EN | 175 (start ? C_RADEON_I2C_START : 0) | (stop ? C_RADEON_I2C_STOP : 0)); 176 177 for (int wait = 0; wait < 100; wait++) { 178 if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0) 179 break; 180 } 181 182 if (WaitAck() != C_RADEON_I2C_DONE) { 183 Stop(); 184 return 0; 185 } 186 187 return length; 188 } 189 190 int CI2CPort::Receive(int address, char * buffer, int length, bool start, bool stop) 191 { 192 //fRadeon.WaitForFifo(length + 4); 193 194 fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 195 C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | 196 C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST); 197 198 fRadeon.SetRegister(C_RADEON_I2C_DATA, address | 0x00000001); 199 200 if (si->asic >= rt_r200) { 201 fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 202 (fTimeLimit << 24) | C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 203 length | 0x010); 204 } else { 205 fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 206 (fTimeLimit << 24) | C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 207 length | 0x100); 208 } 209 210 fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 211 (fNfactor << 24) | (fMfactor << 16) | C_RADEON_I2C_GO | 212 (start ? C_RADEON_I2C_START : 0) | (stop ? C_RADEON_I2C_STOP : 0) | 213 C_RADEON_I2C_DRIVE_EN | C_RADEON_I2C_RECEIVE); 214 215 216 for (int wait = 0; wait < 100; wait++) { 217 if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0) 218 break; 219 } 220 221 if (WaitAck() != C_RADEON_I2C_DONE) 222 return 0; 223 224 snooze(1000); 225 226 for (int offset = 0; offset < length; offset++) { 227 //fRadeon.WaitForFifo(1); 228 buffer[offset] = fRadeon.Register(C_RADEON_I2C_DATA) & 0xff; 229 } 230 231 return length; 232 } 233 234 int CI2CPort::WaitAck() 235 { 236 for (int wait = 0; wait < 100; wait++) { 237 int control = fRadeon.Register(C_RADEON_I2C_CNTL_0); 238 if ((control & C_RADEON_I2C_HALT) != 0) 239 return C_RADEON_I2C_HALT; 240 if ((control & C_RADEON_I2C_NACK) != 0) 241 return C_RADEON_I2C_NACK; 242 if ((control & C_RADEON_I2C_DONE) != 0) 243 return C_RADEON_I2C_DONE; 244 snooze(1000); 245 } 246 PRINT(("CI2CPort::WaitAck() - Time out!\n")); 247 return C_RADEON_I2C_HALT; 248 } 249 250 void CI2CPort::Stop() 251 { 252 // reset status flags 253 fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 254 C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | C_RADEON_I2C_HALT, 0); 255 256 // issue abort call 257 fRadeon.SetRegister(C_RADEON_I2C_CNTL_0_PLUS1, 258 C_RADEON_I2C_ABORT | C_RADEON_I2C_GO, C_RADEON_I2C_ABORT | C_RADEON_I2C_GO); 259 260 // wait GO bit to go low 261 for (int wait = 0; wait < 100; wait++) { 262 if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0) 263 snooze(1000); 264 } 265 PRINT(("CI2CPort::Stop() - Time out!\n")); 266 } 267