xref: /haiku/src/add-ons/media/media-add-ons/radeon/I2CPort.cpp (revision 3cb015b1ee509d69c643506e8ff573808c86dcfc)
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