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
14CI2CPort::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
64CI2CPort::~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
73status_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
88CRadeon & CI2CPort::Radeon() const
89{
90	return fRadeon;
91}
92
93bool CI2CPort::Probe(int address)
94{
95	char buffer[1];
96
97	return Read(address, buffer, sizeof(buffer));
98}
99
100bool 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
107bool 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
114bool 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
122int 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
134void 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
145int 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
190int 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
234int 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
250void 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