xref: /haiku/src/add-ons/accelerants/nvidia/engine/nv_i2c.c (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
1 /*
2  * i2c interface.
3  * Bus should be run at max. 100kHz: see original Philips I2C specification
4  *
5  * Rudolf Cornelissen 12/2002-10/2005
6  */
7 
8 #define MODULE_BIT 0x00004000
9 
10 #include "nv_std.h"
11 
12 char i2c_flag_error (char ErrNo)
13 //error code list:
14 //0 - OK status
15 //1 - SCL locked low by device (bus is still busy)
16 //2 - SDA locked low by device (bus is still busy)
17 //3 - No Acknowledge from device (no handshake)
18 //4 - SDA not released for master to generate STOP bit
19 {
20 	static char I2CError = 0;
21 
22 	if (!I2CError) I2CError = ErrNo;
23 	if (ErrNo == -1) I2CError = 0;
24 	return I2CError;
25 }
26 
27 static void i2c_select_bus_set(bool set)
28 {
29 	/* I/O pins set selection is only valid on dualhead cards */
30 	if (!si->ps.secondary_head) return;
31 
32 	/* select GPU I/O pins set to connect to I2C 'registers' */
33 	if (set)
34 	{
35 		NV_REG32(NV32_FUNCSEL) &= ~0x00000010;
36 		NV_REG32(NV32_2FUNCSEL) |= 0x00000010;
37 	}
38 	else
39 	{
40 		NV_REG32(NV32_2FUNCSEL) &= ~0x00000010;
41 		NV_REG32(NV32_FUNCSEL) |= 0x00000010;
42 	}
43 }
44 
45 static void OutSCL(uint8 BusNR, bool Bit)
46 {
47 	uint8 data;
48 
49 	if (BusNR & 0x01)
50 	{
51 		data = (CRTCR(WR_I2CBUS_1) & 0xf0) | 0x01;
52 		if (Bit)
53 			CRTCW(WR_I2CBUS_1, (data | 0x20));
54 		else
55 			CRTCW(WR_I2CBUS_1, (data & ~0x20));
56 	}
57 	else
58 	{
59 		data = (CRTCR(WR_I2CBUS_0) & 0xf0) | 0x01;
60 		if (Bit)
61 			CRTCW(WR_I2CBUS_0, (data | 0x20));
62 		else
63 			CRTCW(WR_I2CBUS_0, (data & ~0x20));
64 	}
65 }
66 
67 static void OutSDA(uint8 BusNR, bool Bit)
68 {
69 	uint8 data;
70 
71 	if (BusNR & 0x01)
72 	{
73 		data = (CRTCR(WR_I2CBUS_1) & 0xf0) | 0x01;
74 		if (Bit)
75 			CRTCW(WR_I2CBUS_1, (data | 0x10));
76 		else
77 			CRTCW(WR_I2CBUS_1, (data & ~0x10));
78 	}
79 	else
80 	{
81 		data = (CRTCR(WR_I2CBUS_0) & 0xf0) | 0x01;
82 		if (Bit)
83 			CRTCW(WR_I2CBUS_0, (data | 0x10));
84 		else
85 			CRTCW(WR_I2CBUS_0, (data & ~0x10));
86 	}
87 }
88 
89 static bool InSCL(uint8 BusNR)
90 {
91 	if (BusNR & 0x01)
92 	{
93 		if ((CRTCR(RD_I2CBUS_1) & 0x04)) return true;
94 	}
95 	else
96 	{
97 		if ((CRTCR(RD_I2CBUS_0) & 0x04)) return true;
98 	}
99 
100 	return false;
101 }
102 
103 static bool InSDA(uint8 BusNR)
104 {
105 	if (BusNR & 0x01)
106 	{
107 		if ((CRTCR(RD_I2CBUS_1) & 0x08)) return true;
108 	}
109 	else
110 	{
111 		if ((CRTCR(RD_I2CBUS_0) & 0x08)) return true;
112 	}
113 
114 	return false;
115 }
116 
117 static void TXBit (uint8 BusNR, bool Bit)
118 {
119 	/* send out databit */
120 	if (Bit)
121 	{
122 		OutSDA(BusNR, true);
123 		snooze(3);
124 		if (!InSDA(BusNR)) i2c_flag_error (2);
125 	}
126 	else
127 	{
128 		OutSDA(BusNR, false);
129 	}
130 	/* generate clock pulse */
131 	snooze(6);
132 	OutSCL(BusNR, true);
133 	snooze(3);
134 	if (!InSCL(BusNR)) i2c_flag_error (1);
135 	snooze(6);
136 	OutSCL(BusNR, false);
137 	snooze(6);
138 }
139 
140 static uint8 RXBit (uint8 BusNR)
141 {
142 	uint8 Bit = 0;
143 
144 	/* set SDA so input is possible */
145 	OutSDA(BusNR, true);
146 	/* generate clock pulse */
147 	snooze(6);
148 	OutSCL(BusNR, true);
149 	snooze(3);
150 	if (!InSCL(BusNR)) i2c_flag_error (1);
151 	snooze(3);
152 	/* read databit */
153 	if (InSDA(BusNR)) Bit = 1;
154 	/* finish clockpulse */
155 	OutSCL(BusNR, false);
156 	snooze(6);
157 
158 	return Bit;
159 }
160 
161 void i2c_bstart (uint8 BusNR)
162 {
163 	/* select GPU I/O pins set */
164 	i2c_select_bus_set(BusNR & 0x02);
165 
166 	/* enable access to primary head */
167 	set_crtc_owner(0);
168 
169 	/* make sure SDA is high */
170 	OutSDA(BusNR, true);
171 	snooze(3);
172 	OutSCL(BusNR, true);
173 	snooze(3);
174 	if (!InSCL(BusNR)) i2c_flag_error (1);
175 	snooze(6);
176 	/* clear SDA while SCL set (bus-start condition) */
177 	OutSDA(BusNR, false);
178 	snooze(6);
179 	OutSCL(BusNR, false);
180 	snooze(6);
181 
182 	LOG(4,("I2C: START condition generated on bus %d; status is %d\n",
183 		BusNR, i2c_flag_error (0)));
184 }
185 
186 void i2c_bstop (uint8 BusNR)
187 {
188 	/* select GPU I/O pins set */
189 	i2c_select_bus_set(BusNR & 0x02);
190 
191 	/* enable access to primary head */
192 	set_crtc_owner(0);
193 
194 	/* make sure SDA is low */
195 	OutSDA(BusNR, false);
196 	snooze(3);
197 	OutSCL(BusNR, true);
198 	snooze(3);
199 	if (!InSCL(BusNR)) i2c_flag_error (1);
200 	snooze(6);
201 	/* set SDA while SCL set (bus-stop condition) */
202 	OutSDA(BusNR, true);
203 	snooze(3);
204 	if (!InSDA(BusNR)) i2c_flag_error (4);
205 	snooze(3);
206 
207 	LOG(4,("I2C: STOP condition generated on bus %d; status is %d\n",
208 		BusNR, i2c_flag_error (0)));
209 }
210 
211 uint8 i2c_readbyte(uint8 BusNR, bool Ack)
212 {
213 	uint8 cnt, bit, byte = 0;
214 
215 	/* select GPU I/O pins set */
216 	i2c_select_bus_set(BusNR & 0x02);
217 
218 	/* enable access to primary head */
219 	set_crtc_owner(0);
220 
221 	/* read data */
222 	for (cnt = 8; cnt > 0; cnt--)
223 	{
224 		byte <<= 1;
225 		bit = RXBit (BusNR);
226 		byte += bit;
227 	}
228 	/* send acknowledge */
229 	TXBit (BusNR, Ack);
230 
231 	LOG(4,("I2C: read byte ($%02x) from bus #%d; status is %d\n",
232 		byte, BusNR, i2c_flag_error(0)));
233 
234 	return byte;
235 }
236 
237 bool i2c_writebyte (uint8 BusNR, uint8 byte)
238 {
239 	uint8 cnt;
240 	bool bit;
241 	uint8 tmp = byte;
242 
243 	/* select GPU I/O pins set */
244 	i2c_select_bus_set(BusNR & 0x02);
245 
246 	/* enable access to primary head */
247 	set_crtc_owner(0);
248 
249 	/* write data */
250 	for (cnt = 8; cnt > 0; cnt--)
251 	{
252 		bit = (tmp & 0x80);
253 		TXBit (BusNR, bit);
254 		tmp <<= 1;
255 	}
256 	/* read acknowledge */
257 	bit = RXBit (BusNR);
258 	if (bit) i2c_flag_error (3);
259 
260 	LOG(4,("I2C: written byte ($%02x) to bus #%d; status is %d\n",
261 		byte, BusNR, i2c_flag_error(0)));
262 
263 	return bit;
264 }
265 
266 void i2c_readbuffer (uint8 BusNR, uint8* buf, uint8 size)
267 {
268 	uint8 cnt;
269 
270 	for (cnt = 0; cnt < size; cnt++)
271 	{
272 		buf[cnt] = i2c_readbyte(BusNR, buf[cnt]);
273 	}
274 }
275 
276 void i2c_writebuffer (uint8 BusNR, uint8* buf, uint8 size)
277 {
278 	uint8 cnt;
279 
280 	for (cnt = 0; cnt < size; cnt++)
281 	{
282 		i2c_writebyte(BusNR, buf[cnt]);
283 	}
284 }
285 
286 status_t i2c_init(void)
287 {
288 	uint8 bus, buses;
289 	bool *i2c_bus = &(si->ps.i2c_bus0);
290 	status_t result = B_ERROR;
291 
292 	LOG(4,("I2C: searching for wired I2C buses...\n"));
293 
294 	/* enable access to primary head */
295 	set_crtc_owner(0);
296 
297 	/* preset no board wired buses */
298 	si->ps.i2c_bus0 = false;
299 	si->ps.i2c_bus1 = false;
300 	si->ps.i2c_bus2 = false;
301 	si->ps.i2c_bus3 = false;
302 
303 	/* set number of buses to test for */
304 	buses = 2;
305 	if (si->ps.secondary_head) buses = 4;
306 
307 	/* find existing buses */
308 	for (bus = 0; bus < buses; bus++)
309 	{
310 		/* reset status */
311 		i2c_flag_error (-1);
312 		snooze(6);
313 		/* init and/or stop I2C bus */
314 		i2c_bstop(bus);
315 		/* check for hardware coupling of SCL and SDA -out and -in lines */
316 		snooze(6);
317 		OutSCL(bus, false);
318 		OutSDA(bus, true);
319 		snooze(3);
320 		if (InSCL(bus) || !InSDA(bus)) continue;
321 		snooze(3);
322 		OutSCL(bus, true);
323 		OutSDA(bus, false);
324 		snooze(3);
325 		if (!InSCL(bus) || InSDA(bus)) continue;
326 		i2c_bus[bus] = true;
327 		snooze(3);
328 		/* re-init bus */
329 		i2c_bstop(bus);
330 	}
331 
332 	for (bus = 0; bus < buses; bus++)
333 	{
334 		if (i2c_bus[bus])
335 		{
336 			LOG(4,("I2C: bus #%d wiring check: passed\n", bus));
337 			result = B_OK;
338 		}
339 		else
340 			LOG(4,("I2C: bus #%d wiring check: failed\n", bus));
341 	}
342 
343 	return result;
344 }
345