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
12char 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
27static 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
45static 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
67static 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
89static 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
103static 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
117static 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
140static 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
161void 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
186void 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
211uint8 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
237bool 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
266void 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
276void 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
286status_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