1/* Copyright (c) 2005 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 *
21 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
22 * contributors may be used to endorse or promote products derived from this
23 * software without specific prior written permission.
24 * */
25
26/*
27 * This file contains routines to write to and read from the I2C bus using
28 * the GPIO pins of the CS5530.
29 * */
30
31/* STATIC VARIABLES TO STORE WHAT GPIO PINS TO USE */
32
33int gpio_clock = 0;
34int gpio_data = 0;
35
36static int g_initialized = 0;
37
38#define	I2CWRITE		0x00    /* Write address         */
39#define	I2CREAD			0x01    /* Read address          */
40
41#define	I2CACK			0x00    /* Ack value             */
42#define	I2CNACK			0x01    /* Not - ack value       */
43
44#define		CS5530_ID	(0x80000000 | (0x00<<16) | (0x12<<11) | (0<<8) | 0x00)
45#define		CS5530_GPIO	(0x80000000 | (0x00<<16) | (0x12<<11) | (0<<8) | 0x90)
46#define		SDA		0x0800
47#define		SCL		0x0400
48#define		SDADIR	0x0008
49#define		SCLDIR	0x0004
50
51int I2C_init(void);
52void I2C_cleanup(void);
53
54int I2C_Read(unsigned char address, unsigned int reg, unsigned long *p_value,
55             unsigned int bytes);
56int I2C_Write(unsigned char address, unsigned int reg, unsigned long value,
57              unsigned int bytes);
58int I2CAL_init(void);
59void I2CAL_cleanup(void);
60
61void I2CAL_output_clock(int state);
62void I2CAL_output_data(int state);
63unsigned char I2CAL_input_data(void);
64
65void I2CAL_set_data_for_input(void);
66void I2CAL_set_data_for_output(void);
67
68void SendI2CStart(void);
69void SendI2CData(unsigned char inData);
70
71unsigned char ReceiveI2CAck(void);
72void SendI2CStop(void);
73void SendI2CNack(void);
74void SendI2CAck(void);
75unsigned char ReceiveI2CData(void);
76
77/* ### ADD ### ANY LOCAL ROUTINE DEFINITIONS SPECIFIC TO GPIO */
78
79/*---------------------------------------------------------------------------
80 * gfx_i2c_reset
81 *
82 * This routine resets the I2C bus.
83 *---------------------------------------------------------------------------
84 */
85
86#if GFX_I2C_DYNAMIC
87int
88gpio_i2c_reset(unsigned char busnum, short adr, char freq)
89#else
90int
91gfx_i2c_reset(unsigned char busnum, short adr, char freq)
92#endif
93{
94    /* ### ADD ### Any code needed to reset the state of the GPIOs. */
95    return GFX_STATUS_OK;
96}
97
98/*---------------------------------------------------------------------------
99 * gfx_i2c_select_gpio
100 *
101 * This routine selects which GPIO pins to use.
102 *---------------------------------------------------------------------------
103 */
104#if GFX_I2C_DYNAMIC
105int
106gpio_i2c_select_gpio(int clock, int data)
107#else
108int
109gfx_i2c_select_gpio(int clock, int data)
110#endif
111{
112    gpio_clock = clock;
113    gpio_data = data;
114    return (0);
115}
116
117/*---------------------------------------------------------------------------
118 * gfx_i2c_write
119 *
120 * This routine writes data to the specified I2C address.
121 *---------------------------------------------------------------------------
122 */
123#if GFX_I2C_DYNAMIC
124int
125gpio_i2c_write(unsigned char busnum, unsigned char address, unsigned char reg,
126               unsigned char bytes, unsigned char *value)
127#else
128int
129gfx_i2c_write(unsigned char busnum, unsigned char address, unsigned char reg,
130              unsigned char bytes, unsigned char *value)
131#endif
132{
133    /* ### ADD ### CODE TO WRITE BYTE TO I2B BUS */
134
135    int restart_count = 0;
136
137    while (restart_count++ < 5) {
138        /* set the access pointer register. */
139        /* The address is shifted left by one to make room for Read/Write
140         * bit */
141        SendI2CStart();
142        SendI2CData((char) ((address << 1) | I2CWRITE));
143        if (!ReceiveI2CAck()) {
144            SendI2CStop();
145            gfx_delay_milliseconds(10);
146            continue;
147        }
148        SendI2CData((unsigned char) reg);
149        if (!ReceiveI2CAck()) {
150            SendI2CStop();
151            gfx_delay_milliseconds(10);
152            continue;
153        }
154
155        /* write the first byte */
156        SendI2CData(*value);
157        if (!ReceiveI2CAck()) {
158            SendI2CStop();
159            gfx_delay_milliseconds(10);
160            continue;
161        }
162
163        /* write the second byte. */
164        if (bytes == 2) {
165            SendI2CData(*(value + 1));
166            if (!ReceiveI2CAck()) {
167                SendI2CStop();
168                gfx_delay_milliseconds(10);
169                continue;
170            }
171        }
172
173        /* done. */
174        SendI2CStop();
175
176        return 0;
177    }
178
179    return (0);
180
181}
182
183/*---------------------------------------------------------------------------
184 * gfx_i2c_read
185 *
186 * This routine reads data from the specified I2C address.
187 *---------------------------------------------------------------------------
188 */
189#if GFX_I2C_DYNAMIC
190int
191gpio_i2c_read(unsigned char busnum, unsigned char address, unsigned char reg,
192              unsigned char bytes, unsigned char *p_value)
193#else
194int
195gfx_i2c_read(unsigned char busnum, unsigned char address, unsigned char reg,
196             unsigned char bytes, unsigned char *p_value)
197#endif
198{
199    /* ### ADD ### CODE TO WRITE BYTE TO I2B BUS */
200    /* For now return clock and data pins */
201
202    int restart_count = 0;
203
204    if (!p_value)
205        return (1);
206
207    while (restart_count++ < 5) {
208        /* set the access pointer register. */
209        /* The address is shifted left by one to make room for Read/Write
210         * bit  */
211        SendI2CStart();
212        SendI2CData((char) ((address << 1) | I2CWRITE));
213        if (!ReceiveI2CAck()) {
214            SendI2CStop();
215            gfx_delay_milliseconds(10);
216            continue;
217        }
218        SendI2CData((unsigned char) (reg & 0xFF));
219        SendI2CNack();
220
221        /* read the first data byte. */
222        SendI2CStart();
223        SendI2CData((char) ((address << 1) | I2CREAD));
224        if (!ReceiveI2CAck()) {
225            SendI2CStop();
226            gfx_delay_milliseconds(10);
227            continue;
228        }
229        *p_value = ReceiveI2CData();
230
231        /* read the second byte. */
232        if (bytes == 2) {
233            SendI2CAck();
234            *(p_value + 1) = ReceiveI2CData();
235        }
236
237        /* done. */
238        SendI2CNack();
239        SendI2CStop();
240
241        return 0;
242    }
243
244    return (1);
245}
246
247/* Added i2c/gpio code to test fs451 chip. */
248
249/*
250 * ----------------------------------------------------------------------
251 *
252 *	void SendI2CStart(void)
253 *
254 *	Sends an I2C start signal on the bus.
255 *
256 *----------------------------------------------------------------------
257 */
258void
259SendI2CStart(void)
260{
261    I2CAL_output_data(1);
262    I2CAL_output_clock(1);
263    I2CAL_output_data(0);
264    I2CAL_output_clock(0);
265}
266
267/*
268 *----------------------------------------------------------------------
269 *
270 *	void SendI2CStop(void)
271 *
272 *	Sends an I2C stop signal on the bus.
273 *
274 *----------------------------------------------------------------------
275 */
276void
277SendI2CStop(void)
278{
279    I2CAL_output_data(0);
280    I2CAL_output_clock(1);
281    I2CAL_output_data(1);
282}
283
284/*
285 *----------------------------------------------------------------------
286 *
287 *	void SendI2CAck(void)
288 *
289 *	Sends the Ack signal on the I2C bus.
290 *
291 *----------------------------------------------------------------------
292 */
293void
294SendI2CAck(void)
295{
296    I2CAL_output_data(0);
297    I2CAL_output_clock(1);
298    I2CAL_output_clock(0);
299}
300
301/*
302 *----------------------------------------------------------------------
303 *
304 *	void SendI2CNack(void)
305 *
306 *	Sends the Nt-Ack signal on the I2C bus.
307 *
308 *----------------------------------------------------------------------
309 */
310void
311SendI2CNack(void)
312{
313    I2CAL_output_data(1);
314    I2CAL_output_clock(1);
315    I2CAL_output_clock(0);
316}
317
318/*
319 *----------------------------------------------------------------------
320 *
321 *	UInt8 SendI2CData( UInt8 inData )
322 *
323 *	Sends a byte of data on the I2C bus and returns the TRUE if the slave
324 *	ACK'dthe data.
325 *
326 *	Input:	inData			- the byte of data to send
327 *	Output:	(return)		- TRUE (1) if ACK was received, FALSE (0) if not
328 *
329 *----------------------------------------------------------------------
330 */
331void
332SendI2CData(unsigned char inData)
333{
334    unsigned char bit;
335
336    /* Send all 8 bits of data byte, MSB to LSB */
337    for (bit = 0x80; bit != 0; bit >>= 1) {
338        if (inData & bit)
339            I2CAL_output_data(1);
340        else
341            I2CAL_output_data(0);
342
343        I2CAL_output_clock(1);
344        I2CAL_output_clock(0);
345    }
346}
347
348/*
349 *----------------------------------------------------------------------
350 *
351 *	UInt8 ReceiveI2CAck(  )
352 *
353 *	Receives the Ack (or Nack) from the slave.
354 *
355 *	Output:	(return) - TRUE (1) if ACK was received, FALSE (0) if not
356 *
357 *----------------------------------------------------------------------
358 */
359unsigned char
360ReceiveI2CAck(void)
361{
362    unsigned char bit;
363
364    /* Test for Ack/Nack */
365    I2CAL_set_data_for_input();
366    I2CAL_output_data(1);
367    I2CAL_output_clock(1);
368    bit = I2CAL_input_data();
369    I2CAL_output_clock(0);
370    I2CAL_set_data_for_output();
371    return !bit;
372}
373
374/*
375 *----------------------------------------------------------------------
376 *
377 *	unsigned char ReceiveI2CData(void)
378 *
379 *	Receives a byte of data from the I2C bus.
380 *
381 *	Output:	(return) - The data byte recehved from the bus
382 *
383 *----------------------------------------------------------------------
384 */
385unsigned char
386ReceiveI2CData(void)
387{
388    unsigned char data = 0;
389    unsigned char x;
390
391    /* make sure the data line is released */
392    I2CAL_set_data_for_input();
393    I2CAL_output_data(1);
394
395    /* shift in the data */
396    for (x = 0; x < 8; x++) {
397        /* shift the data left   */
398        I2CAL_output_clock(1);
399        data <<= 1;
400        data |= I2CAL_input_data();
401        I2CAL_output_clock(0);
402    }
403
404    I2CAL_set_data_for_output();
405    I2CAL_output_data(1);
406    return data;
407}
408
409/*
410 *----------------------------------------------------------------------
411 *
412 *	void I2C_init(void)
413 *
414 *	This routine initializes the I2C interface. Clients of the I2C.c
415 *	will call this routine before calling any other routine in the I2C.c
416 *
417 *----------------------------------------------------------------------
418 */
419
420#if GFX_I2C_DYNAMIC
421int
422gpio_i2c_init(void)
423#else
424int
425gfx_i2c_init(void)
426#endif
427{
428    int errc;
429
430    /* init I2CAL */
431    errc = I2CAL_init();
432    if (errc)
433        return errc;
434
435    /* set the clock and data lines to the proper states */
436    I2CAL_output_clock(1);
437    I2CAL_output_data(1);
438    I2CAL_set_data_for_output();
439
440    SendI2CStart();
441    SendI2CStop();
442    SendI2CStop();
443
444    g_initialized = 1;
445
446    return 0;
447}
448
449/*
450 *----------------------------------------------------------------------
451 *
452 *	void I2C_cleanup(void)
453 *
454 *	This routine disables the I2C interface. Clients of the I2C.c will not
455 *	call any other I2C routine after calling this routine.
456 *
457 *----------------------------------------------------------------------
458 */
459
460#if GFX_I2C_DYNAMIC
461void
462gpio_i2c_cleanup(void)
463#else
464void
465gfx_i2c_cleanup(void)
466#endif
467{
468    if (g_initialized) {
469
470        /* set the clock and data lines to a harmless state */
471        I2CAL_output_clock(1);
472        I2CAL_output_data(1);
473
474        g_initialized = 0;
475    }
476
477    I2CAL_cleanup();
478}
479
480int
481I2CAL_init(void)
482{
483    unsigned long l_reg;
484    unsigned short reg;
485
486    /* initialize the i2c port. */
487    l_reg = gfx_pci_config_read(CS5530_GPIO);
488
489    if (l_reg != 0x01001078)
490        return 1;
491
492    l_reg = gfx_pci_config_read(CS5530_GPIO);
493    reg = (unsigned short) l_reg;
494
495    /* both outputs, both high. */
496    reg |= (SDADIR | SCLDIR | SDA | SCL);
497    l_reg = reg;
498    gfx_pci_config_write(CS5530_GPIO, l_reg);
499
500    g_initialized = 1;
501
502    return 0;
503}
504
505void
506I2CAL_cleanup(void)
507{
508    if (g_initialized) {
509
510        g_initialized = 0;
511    }
512}
513
514/*
515 *----------------------------------------------------------------------------
516 *
517 *	set the I2C clock line state
518 *
519 *----------------------------------------------------------------------------
520 */
521void
522I2CAL_output_clock(int inState)
523{
524    unsigned short reg;
525    unsigned long value;
526
527    value = gfx_pci_config_read(CS5530_GPIO);
528    reg = (unsigned short) value;
529
530    if (inState) {              /* write a 1. */
531        reg |= SCL;
532    }
533    else {                      /* write a 0. */
534        reg &= ~SCL;
535    }
536
537    value = reg;
538    gfx_pci_config_write(CS5530_GPIO, value);
539
540    /* hold it for a minimum of 4.7us */
541    gfx_delay_microseconds(5);
542}
543
544/*
545 *----------------------------------------------------------------------------
546 *
547 *	set the I2C data line state
548 *
549 *----------------------------------------------------------------------------
550 */
551void
552I2CAL_output_data(int inState)
553{
554    unsigned short reg;
555    unsigned long value;
556
557    value = gfx_pci_config_read(CS5530_GPIO);
558    reg = (unsigned short) value;
559
560    if (inState) {              /* write a 1. */
561        reg |= SDA;
562    }
563    else {
564        /* write a 0. */
565        reg &= ~SDA;
566    }
567    value = reg;
568    gfx_pci_config_write(CS5530_GPIO, value);
569
570    /* 250 ns setup time */
571    gfx_delay_microseconds(1);
572}
573
574/*
575 *----------------------------------------------------------------------------
576 *
577 *	read the state of the data line
578 *
579 *----------------------------------------------------------------------------
580 */
581unsigned char
582I2CAL_input_data(void)
583{
584    unsigned short reg;
585    unsigned long value;
586
587    value = gfx_pci_config_read(CS5530_GPIO);
588    reg = (unsigned short) value;
589
590    if (reg & SDA)
591        return 1;
592    else
593        return 0;
594}
595
596/*
597 *----------------------------------------------------------------------------
598 *
599 *	set the I2C data for input mode
600 *
601 *----------------------------------------------------------------------------
602 */
603void
604I2CAL_set_data_for_input(void)
605{
606    unsigned short reg;
607    unsigned long value;
608
609    value = gfx_pci_config_read(CS5530_GPIO);
610    reg = (unsigned short) value;
611
612    reg &= ~SDADIR;
613
614    value = reg;
615
616    gfx_pci_config_write(CS5530_GPIO, value);
617}
618
619/*
620 *----------------------------------------------------------------------------
621 *
622 *	set the I2C data for output mode
623 *
624 *----------------------------------------------------------------------------
625 */
626void
627I2CAL_set_data_for_output(void)
628{
629    unsigned short reg;
630    unsigned long value;
631
632    value = gfx_pci_config_read(CS5530_GPIO);
633    reg = (unsigned short) value;
634    reg |= SDADIR;
635    value = reg;
636
637    gfx_pci_config_write(CS5530_GPIO, value);
638
639}
640
641/* END OF FILE */
642