Commit | Line | Data |
---|---|---|
81dee67e SM |
1 | /******************************************************************* |
2 | * | |
3 | * Copyright (c) 2007 by Silicon Motion, Inc. (SMI) | |
4 | * | |
5 | * All rights are reserved. Reproduction or in part is prohibited | |
6 | * without the written consent of the copyright owner. | |
7 | * | |
8 | * swi2c.c --- SM750/SM718 DDK | |
9 | * This file contains the source code for I2C using software | |
10 | * implementation. | |
11 | * | |
12 | *******************************************************************/ | |
13 | #include "ddk750_help.h" | |
14 | #include "ddk750_reg.h" | |
15 | #include "ddk750_swi2c.h" | |
16 | #include "ddk750_power.h" | |
17 | ||
18 | ||
19 | /******************************************************************* | |
20 | * I2C Software Master Driver: | |
21 | * =========================== | |
22 | * Each i2c cycle is split into 4 sections. Each of these section marks | |
23 | * a point in time where the SCL or SDA may be changed. | |
24 | * | |
25 | * 1 Cycle == | Section I. | Section 2. | Section 3. | Section 4. | | |
26 | * +-------------+-------------+-------------+-------------+ | |
27 | * | SCL set LOW |SCL no change| SCL set HIGH|SCL no change| | |
28 | * | |
29 | * ____________ _____________ | |
30 | * SCL == XXXX _____________ ____________ / | |
31 | * | |
32 | * I.e. the SCL may only be changed in section 1. and section 3. while | |
33 | * the SDA may only be changed in section 2. and section 4. The table | |
34 | * below gives the changes for these 2 lines in the varios sections. | |
35 | * | |
36 | * Section changes Table: | |
37 | * ====================== | |
38 | * blank = no change, L = set bit LOW, H = set bit HIGH | |
39 | * | |
40 | * | 1.| 2.| 3.| 4.| | |
41 | * ---------------+---+---+---+---+ | |
42 | * Tx Start SDA | | H | | L | | |
43 | * SCL | L | | H | | | |
44 | * ---------------+---+---+---+---+ | |
45 | * Tx Stop SDA | | L | | H | | |
46 | * SCL | L | | H | | | |
47 | * ---------------+---+---+---+---+ | |
48 | * Tx bit H SDA | | H | | | | |
49 | * SCL | L | | H | | | |
50 | * ---------------+---+---+---+---+ | |
51 | * Tx bit L SDA | | L | | | | |
52 | * SCL | L | | H | | | |
53 | * ---------------+---+---+---+---+ | |
54 | * | |
55 | ******************************************************************/ | |
56 | ||
57 | /* GPIO pins used for this I2C. It ranges from 0 to 63. */ | |
58 | static unsigned char g_i2cClockGPIO = DEFAULT_I2C_SCL; | |
59 | static unsigned char g_i2cDataGPIO = DEFAULT_I2C_SDA; | |
60 | ||
61 | /* | |
62 | * Below is the variable declaration for the GPIO pin register usage | |
63 | * for the i2c Clock and i2c Data. | |
64 | * | |
65 | * Note: | |
66 | * Notice that the GPIO usage for the i2c clock and i2c Data are | |
67 | * separated. This is to make this code flexible enough when | |
68 | * two separate GPIO pins for the clock and data are located | |
69 | * in two different GPIO register set (worst case). | |
70 | */ | |
71 | ||
72 | /* i2c Clock GPIO Register usage */ | |
73 | static unsigned long g_i2cClkGPIOMuxReg = GPIO_MUX; | |
74 | static unsigned long g_i2cClkGPIODataReg = GPIO_DATA; | |
75 | static unsigned long g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION; | |
76 | ||
77 | /* i2c Data GPIO Register usage */ | |
78 | static unsigned long g_i2cDataGPIOMuxReg = GPIO_MUX; | |
79 | static unsigned long g_i2cDataGPIODataReg = GPIO_DATA; | |
80 | static unsigned long g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION; | |
81 | ||
81dee67e SM |
82 | /* |
83 | * This function puts a delay between command | |
84 | */ | |
85 | static void swI2CWait(void) | |
86 | { | |
87 | /* find a bug: | |
88 | * peekIO method works well before suspend/resume | |
89 | * but after suspend, peekIO(0x3ce,0x61) & 0x10 | |
90 | * always be non-zero,which makes the while loop | |
91 | * never finish. | |
92 | * use non-ultimate for loop below is safe | |
93 | * */ | |
94 | #if 0 | |
95 | /* Change wait algorithm to use PCI bus clock, | |
96 | it's more reliable than counter loop .. | |
97 | write 0x61 to 0x3ce and read from 0x3cf | |
98 | */ | |
f31b55ac | 99 | while(peekIO(0x3ce, 0x61) & 0x10); |
81dee67e | 100 | #else |
7ef803a9 | 101 | int i, Temp; |
81dee67e | 102 | |
6d43b0f4 | 103 | for(i = 0; i < 600; i++) |
7ef803a9 IA |
104 | { |
105 | Temp = i; | |
106 | Temp += i; | |
107 | } | |
81dee67e SM |
108 | #endif |
109 | } | |
110 | ||
111 | /* | |
112 | * This function set/reset the SCL GPIO pin | |
113 | * | |
114 | * Parameters: | |
115 | * value - Bit value to set to the SCL or SDA (0 = low, 1 = high) | |
116 | * | |
117 | * Notes: | |
118 | * When setting SCL to high, just set the GPIO as input where the pull up | |
119 | * resistor will pull the signal up. Do not use software to pull up the | |
120 | * signal because the i2c will fail when other device try to drive the | |
121 | * signal due to SM50x will drive the signal to always high. | |
122 | */ | |
123 | void swI2CSCL(unsigned char value) | |
124 | { | |
7ef803a9 IA |
125 | unsigned long ulGPIOData; |
126 | unsigned long ulGPIODirection; | |
127 | ||
128 | ulGPIODirection = PEEK32(g_i2cClkGPIODataDirReg); | |
129 | if (value) /* High */ | |
130 | { | |
131 | /* Set direction as input. This will automatically pull the signal up. */ | |
132 | ulGPIODirection &= ~(1 << g_i2cClockGPIO); | |
133 | POKE32(g_i2cClkGPIODataDirReg, ulGPIODirection); | |
134 | } | |
135 | else /* Low */ | |
136 | { | |
137 | /* Set the signal down */ | |
138 | ulGPIOData = PEEK32(g_i2cClkGPIODataReg); | |
139 | ulGPIOData &= ~(1 << g_i2cClockGPIO); | |
140 | POKE32(g_i2cClkGPIODataReg, ulGPIOData); | |
141 | ||
142 | /* Set direction as output */ | |
143 | ulGPIODirection |= (1 << g_i2cClockGPIO); | |
144 | POKE32(g_i2cClkGPIODataDirReg, ulGPIODirection); | |
145 | } | |
81dee67e SM |
146 | } |
147 | ||
148 | /* | |
149 | * This function set/reset the SDA GPIO pin | |
150 | * | |
151 | * Parameters: | |
152 | * value - Bit value to set to the SCL or SDA (0 = low, 1 = high) | |
153 | * | |
154 | * Notes: | |
155 | * When setting SCL to high, just set the GPIO as input where the pull up | |
156 | * resistor will pull the signal up. Do not use software to pull up the | |
157 | * signal because the i2c will fail when other device try to drive the | |
158 | * signal due to SM50x will drive the signal to always high. | |
159 | */ | |
160 | void swI2CSDA(unsigned char value) | |
161 | { | |
7ef803a9 IA |
162 | unsigned long ulGPIOData; |
163 | unsigned long ulGPIODirection; | |
164 | ||
165 | ulGPIODirection = PEEK32(g_i2cDataGPIODataDirReg); | |
166 | if (value) /* High */ | |
167 | { | |
168 | /* Set direction as input. This will automatically pull the signal up. */ | |
169 | ulGPIODirection &= ~(1 << g_i2cDataGPIO); | |
170 | POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection); | |
171 | } | |
172 | else /* Low */ | |
173 | { | |
174 | /* Set the signal down */ | |
175 | ulGPIOData = PEEK32(g_i2cDataGPIODataReg); | |
176 | ulGPIOData &= ~(1 << g_i2cDataGPIO); | |
177 | POKE32(g_i2cDataGPIODataReg, ulGPIOData); | |
178 | ||
179 | /* Set direction as output */ | |
180 | ulGPIODirection |= (1 << g_i2cDataGPIO); | |
181 | POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection); | |
182 | } | |
81dee67e SM |
183 | } |
184 | ||
185 | /* | |
186 | * This function read the data from the SDA GPIO pin | |
187 | * | |
188 | * Return Value: | |
189 | * The SDA data bit sent by the Slave | |
190 | */ | |
191 | static unsigned char swI2CReadSDA(void) | |
192 | { | |
7ef803a9 IA |
193 | unsigned long ulGPIODirection; |
194 | unsigned long ulGPIOData; | |
195 | ||
196 | /* Make sure that the direction is input (High) */ | |
197 | ulGPIODirection = PEEK32(g_i2cDataGPIODataDirReg); | |
198 | if ((ulGPIODirection & (1 << g_i2cDataGPIO)) != (~(1 << g_i2cDataGPIO))) | |
199 | { | |
200 | ulGPIODirection &= ~(1 << g_i2cDataGPIO); | |
201 | POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection); | |
202 | } | |
203 | ||
204 | /* Now read the SDA line */ | |
205 | ulGPIOData = PEEK32(g_i2cDataGPIODataReg); | |
206 | if (ulGPIOData & (1 << g_i2cDataGPIO)) | |
207 | return 1; | |
208 | else | |
209 | return 0; | |
81dee67e SM |
210 | } |
211 | ||
81dee67e SM |
212 | /* |
213 | * This function sends ACK signal | |
214 | */ | |
215 | static void swI2CAck(void) | |
216 | { | |
7ef803a9 | 217 | return; /* Single byte read is ok without it. */ |
81dee67e SM |
218 | } |
219 | ||
220 | /* | |
221 | * This function sends the start command to the slave device | |
222 | */ | |
6a9df430 | 223 | static void swI2CStart(void) |
81dee67e | 224 | { |
7ef803a9 IA |
225 | /* Start I2C */ |
226 | swI2CSDA(1); | |
227 | swI2CSCL(1); | |
228 | swI2CSDA(0); | |
81dee67e SM |
229 | } |
230 | ||
231 | /* | |
232 | * This function sends the stop command to the slave device | |
233 | */ | |
6a9df430 | 234 | static void swI2CStop(void) |
81dee67e | 235 | { |
7ef803a9 IA |
236 | /* Stop the I2C */ |
237 | swI2CSCL(1); | |
238 | swI2CSDA(0); | |
239 | swI2CSDA(1); | |
81dee67e SM |
240 | } |
241 | ||
242 | /* | |
243 | * This function writes one byte to the slave device | |
244 | * | |
245 | * Parameters: | |
246 | * data - Data to be write to the slave device | |
247 | * | |
248 | * Return Value: | |
249 | * 0 - Success | |
250 | * -1 - Fail to write byte | |
251 | */ | |
6a9df430 | 252 | static long swI2CWriteByte(unsigned char data) |
81dee67e | 253 | { |
7ef803a9 IA |
254 | unsigned char value = data; |
255 | int i; | |
256 | ||
257 | /* Sending the data bit by bit */ | |
6d43b0f4 | 258 | for (i = 0; i < 8; i++) |
7ef803a9 IA |
259 | { |
260 | /* Set SCL to low */ | |
261 | swI2CSCL(0); | |
262 | ||
263 | /* Send data bit */ | |
264 | if ((value & 0x80) != 0) | |
265 | swI2CSDA(1); | |
266 | else | |
267 | swI2CSDA(0); | |
268 | ||
269 | swI2CWait(); | |
270 | ||
271 | /* Toggle clk line to one */ | |
272 | swI2CSCL(1); | |
273 | swI2CWait(); | |
274 | ||
275 | /* Shift byte to be sent */ | |
276 | value = value << 1; | |
277 | } | |
278 | ||
279 | /* Set the SCL Low and SDA High (prepare to get input) */ | |
280 | swI2CSCL(0); | |
281 | swI2CSDA(1); | |
282 | ||
283 | /* Set the SCL High for ack */ | |
284 | swI2CWait(); | |
285 | swI2CSCL(1); | |
286 | swI2CWait(); | |
287 | ||
288 | /* Read SDA, until SDA==0 */ | |
6d43b0f4 | 289 | for(i = 0; i < 0xff; i++) |
7ef803a9 IA |
290 | { |
291 | if (!swI2CReadSDA()) | |
292 | break; | |
293 | ||
294 | swI2CSCL(0); | |
295 | swI2CWait(); | |
296 | swI2CSCL(1); | |
297 | swI2CWait(); | |
298 | } | |
299 | ||
300 | /* Set the SCL Low and SDA High */ | |
301 | swI2CSCL(0); | |
302 | swI2CSDA(1); | |
303 | ||
6d43b0f4 | 304 | if (i < 0xff) |
7ef803a9 IA |
305 | return 0; |
306 | else | |
307 | return -1; | |
81dee67e SM |
308 | } |
309 | ||
310 | /* | |
311 | * This function reads one byte from the slave device | |
312 | * | |
313 | * Parameters: | |
314 | * ack - Flag to indicate either to send the acknowledge | |
315 | * message to the slave device or not | |
316 | * | |
317 | * Return Value: | |
318 | * One byte data read from the Slave device | |
319 | */ | |
6a9df430 | 320 | static unsigned char swI2CReadByte(unsigned char ack) |
81dee67e | 321 | { |
7ef803a9 IA |
322 | int i; |
323 | unsigned char data = 0; | |
81dee67e | 324 | |
6d43b0f4 | 325 | for(i = 7; i >= 0; i--) |
7ef803a9 IA |
326 | { |
327 | /* Set the SCL to Low and SDA to High (Input) */ | |
328 | swI2CSCL(0); | |
329 | swI2CSDA(1); | |
330 | swI2CWait(); | |
81dee67e | 331 | |
7ef803a9 IA |
332 | /* Set the SCL High */ |
333 | swI2CSCL(1); | |
334 | swI2CWait(); | |
81dee67e | 335 | |
7ef803a9 IA |
336 | /* Read data bits from SDA */ |
337 | data |= (swI2CReadSDA() << i); | |
338 | } | |
81dee67e | 339 | |
7ef803a9 IA |
340 | if (ack) |
341 | swI2CAck(); | |
81dee67e | 342 | |
7ef803a9 IA |
343 | /* Set the SCL Low and SDA High */ |
344 | swI2CSCL(0); | |
345 | swI2CSDA(1); | |
81dee67e | 346 | |
7ef803a9 | 347 | return data; |
81dee67e | 348 | } |
81dee67e SM |
349 | |
350 | /* | |
351 | * This function initializes GPIO port for SW I2C communication. | |
352 | * | |
353 | * Parameters: | |
354 | * i2cClkGPIO - The GPIO pin to be used as i2c SCL | |
355 | * i2cDataGPIO - The GPIO pin to be used as i2c SDA | |
356 | * | |
357 | * Return Value: | |
358 | * -1 - Fail to initialize the i2c | |
359 | * 0 - Success | |
360 | */ | |
6a9df430 RRD |
361 | static long swI2CInit_SM750LE(unsigned char i2cClkGPIO, |
362 | unsigned char i2cDataGPIO) | |
81dee67e | 363 | { |
7ef803a9 | 364 | int i; |
81dee67e | 365 | |
7ef803a9 IA |
366 | /* Initialize the GPIO pin for the i2c Clock Register */ |
367 | g_i2cClkGPIODataReg = GPIO_DATA_SM750LE; | |
368 | g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION_SM750LE; | |
81dee67e | 369 | |
7ef803a9 IA |
370 | /* Initialize the Clock GPIO Offset */ |
371 | g_i2cClockGPIO = i2cClkGPIO; | |
81dee67e | 372 | |
7ef803a9 IA |
373 | /* Initialize the GPIO pin for the i2c Data Register */ |
374 | g_i2cDataGPIODataReg = GPIO_DATA_SM750LE; | |
375 | g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION_SM750LE; | |
81dee67e | 376 | |
7ef803a9 IA |
377 | /* Initialize the Data GPIO Offset */ |
378 | g_i2cDataGPIO = i2cDataGPIO; | |
81dee67e | 379 | |
7ef803a9 | 380 | /* Note that SM750LE don't have GPIO MUX and power is always on */ |
81dee67e | 381 | |
7ef803a9 | 382 | /* Clear the i2c lines. */ |
6d43b0f4 | 383 | for(i = 0; i < 9; i++) |
7ef803a9 | 384 | swI2CStop(); |
81dee67e | 385 | |
7ef803a9 | 386 | return 0; |
81dee67e SM |
387 | } |
388 | ||
389 | /* | |
390 | * This function initializes the i2c attributes and bus | |
391 | * | |
392 | * Parameters: | |
393 | * i2cClkGPIO - The GPIO pin to be used as i2c SCL | |
394 | * i2cDataGPIO - The GPIO pin to be used as i2c SDA | |
395 | * | |
396 | * Return Value: | |
397 | * -1 - Fail to initialize the i2c | |
398 | * 0 - Success | |
399 | */ | |
400 | long swI2CInit( | |
7ef803a9 IA |
401 | unsigned char i2cClkGPIO, |
402 | unsigned char i2cDataGPIO | |
81dee67e SM |
403 | ) |
404 | { | |
7ef803a9 | 405 | int i; |
81dee67e | 406 | |
7ef803a9 IA |
407 | /* Return 0 if the GPIO pins to be used is out of range. The range is only from [0..63] */ |
408 | if ((i2cClkGPIO > 31) || (i2cDataGPIO > 31)) | |
409 | return -1; | |
81dee67e | 410 | |
7ef803a9 IA |
411 | if (getChipType() == SM750LE) |
412 | return swI2CInit_SM750LE(i2cClkGPIO, i2cDataGPIO); | |
81dee67e | 413 | |
7ef803a9 IA |
414 | /* Initialize the GPIO pin for the i2c Clock Register */ |
415 | g_i2cClkGPIOMuxReg = GPIO_MUX; | |
416 | g_i2cClkGPIODataReg = GPIO_DATA; | |
417 | g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION; | |
81dee67e | 418 | |
7ef803a9 IA |
419 | /* Initialize the Clock GPIO Offset */ |
420 | g_i2cClockGPIO = i2cClkGPIO; | |
81dee67e | 421 | |
7ef803a9 IA |
422 | /* Initialize the GPIO pin for the i2c Data Register */ |
423 | g_i2cDataGPIOMuxReg = GPIO_MUX; | |
424 | g_i2cDataGPIODataReg = GPIO_DATA; | |
425 | g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION; | |
81dee67e | 426 | |
7ef803a9 IA |
427 | /* Initialize the Data GPIO Offset */ |
428 | g_i2cDataGPIO = i2cDataGPIO; | |
81dee67e | 429 | |
7ef803a9 IA |
430 | /* Enable the GPIO pins for the i2c Clock and Data (GPIO MUX) */ |
431 | POKE32(g_i2cClkGPIOMuxReg, | |
432 | PEEK32(g_i2cClkGPIOMuxReg) & ~(1 << g_i2cClockGPIO)); | |
433 | POKE32(g_i2cDataGPIOMuxReg, | |
434 | PEEK32(g_i2cDataGPIOMuxReg) & ~(1 << g_i2cDataGPIO)); | |
81dee67e | 435 | |
7ef803a9 IA |
436 | /* Enable GPIO power */ |
437 | enableGPIO(1); | |
81dee67e | 438 | |
7ef803a9 | 439 | /* Clear the i2c lines. */ |
6d43b0f4 | 440 | for(i = 0; i < 9; i++) |
7ef803a9 | 441 | swI2CStop(); |
81dee67e | 442 | |
7ef803a9 | 443 | return 0; |
81dee67e SM |
444 | } |
445 | ||
446 | /* | |
447 | * This function reads the slave device's register | |
448 | * | |
449 | * Parameters: | |
450 | * deviceAddress - i2c Slave device address which register | |
451 | * to be read from | |
452 | * registerIndex - Slave device's register to be read | |
453 | * | |
454 | * Return Value: | |
455 | * Register value | |
456 | */ | |
457 | unsigned char swI2CReadReg( | |
7ef803a9 IA |
458 | unsigned char deviceAddress, |
459 | unsigned char registerIndex | |
81dee67e SM |
460 | ) |
461 | { | |
7ef803a9 | 462 | unsigned char data; |
81dee67e | 463 | |
7ef803a9 IA |
464 | /* Send the Start signal */ |
465 | swI2CStart(); | |
81dee67e | 466 | |
7ef803a9 IA |
467 | /* Send the device address */ |
468 | swI2CWriteByte(deviceAddress); | |
81dee67e | 469 | |
7ef803a9 IA |
470 | /* Send the register index */ |
471 | swI2CWriteByte(registerIndex); | |
81dee67e | 472 | |
7ef803a9 IA |
473 | /* Get the bus again and get the data from the device read address */ |
474 | swI2CStart(); | |
475 | swI2CWriteByte(deviceAddress + 1); | |
476 | data = swI2CReadByte(1); | |
81dee67e | 477 | |
7ef803a9 IA |
478 | /* Stop swI2C and release the bus */ |
479 | swI2CStop(); | |
81dee67e | 480 | |
7ef803a9 | 481 | return data; |
81dee67e SM |
482 | } |
483 | ||
484 | /* | |
485 | * This function writes a value to the slave device's register | |
486 | * | |
487 | * Parameters: | |
488 | * deviceAddress - i2c Slave device address which register | |
489 | * to be written | |
490 | * registerIndex - Slave device's register to be written | |
491 | * data - Data to be written to the register | |
492 | * | |
493 | * Result: | |
494 | * 0 - Success | |
495 | * -1 - Fail | |
496 | */ | |
497 | long swI2CWriteReg( | |
7ef803a9 IA |
498 | unsigned char deviceAddress, |
499 | unsigned char registerIndex, | |
500 | unsigned char data | |
81dee67e SM |
501 | ) |
502 | { | |
7ef803a9 | 503 | long returnValue = 0; |
81dee67e | 504 | |
7ef803a9 IA |
505 | /* Send the Start signal */ |
506 | swI2CStart(); | |
81dee67e | 507 | |
7ef803a9 IA |
508 | /* Send the device address and read the data. All should return success |
509 | in order for the writing processed to be successful | |
510 | */ | |
511 | if ((swI2CWriteByte(deviceAddress) != 0) || | |
512 | (swI2CWriteByte(registerIndex) != 0) || | |
513 | (swI2CWriteByte(data) != 0)) | |
514 | { | |
515 | returnValue = -1; | |
516 | } | |
81dee67e | 517 | |
7ef803a9 IA |
518 | /* Stop i2c and release the bus */ |
519 | swI2CStop(); | |
81dee67e | 520 | |
7ef803a9 | 521 | return returnValue; |
81dee67e | 522 | } |