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