summaryrefslogtreecommitdiff
path: root/OptionRomPkg/Library/CirrusLogicI2cLib/CirrusLogic5430I2cLib.c
blob: 2a693cef5a407791259f80d8061cc935d5cefc16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
/** @file
  I2C Bus Libary implementation upon CirrusLogic.

  Copyright (c) 2008, Intel Corporation
  All rights reserved. This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include <PiDxe.h>

#include <Library/DxeI2cLib.h>
#include <Library/TimerLib.h>
#include <Library/DebugLib.h>

#define SEQ_ADDRESS_REGISTER    0x3c4
#define SEQ_DATA_REGISTER       0x3c5

#define I2C_CONTROL             0x08
#define I2CDAT_IN               7
#define I2CCLK_IN               2
#define I2CDAT_OUT              1
#define I2CCLK_OUT              0

#define I2C_BUS_SPEED           100  //100kbps

/**
  PCI I/O byte write function.

  @param  PciIo        The pointer to PCI_IO_PROTOCOL.
  @param  Address      The bit map of I2C Data or I2C Clock pins.
  @param  Data         The date to write.

**/
STATIC
VOID
outb (
  EFI_PCI_IO_PROTOCOL    *PciIo,
  UINTN                  Address,
  UINT8                  Data
  )
{
  PciIo->Io.Write (
             PciIo,
             EfiPciIoWidthUint8,
             EFI_PCI_IO_PASS_THROUGH_BAR,
             Address,
             1,
             &Data
             );
}
/**
  PCI I/O byte read function.

  @param  PciIo        The pointer to PCI_IO_PROTOCOL.
  @param  Address      The bit map of I2C Data or I2C Clock pins.

  return byte value read from PCI I/O space.

**/
STATIC
UINT8
inb (
  EFI_PCI_IO_PROTOCOL    *PciIo,
  UINTN                  Address
  )
{
  UINT8 Data;

  PciIo->Io.Read (
             PciIo,
             EfiPciIoWidthUint8,
             EFI_PCI_IO_PASS_THROUGH_BAR,
             Address,
             1,
             &Data
             );
  return Data;
}

/**
  Read status of I2C Data and I2C Clock Pins.

  @param  PciIo        The pointer to PCI_IO_PROTOCOL.
  @param  Blt          The bit map of I2C Data or I2C Clock pins.

  @retval 0            Low on I2C Data or I2C Clock Pin.
  @retval 1            High on I2C Data or I2C Clock Pin.

**/
STATIC
UINT8
I2cPinRead (
  EFI_PCI_IO_PROTOCOL    *PciIo,
  UINT8                  Bit
  )
{
  outb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL);
  return (UINT8) ((inb (PciIo, SEQ_DATA_REGISTER) >> Bit ) & 0xfe);
}


/**
  Set/Clear I2C Data and I2C Clock Pins.

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
  @param  Blt                The bit map to controller I2C Data or I2C Clock pins.
  @param  Value              1 or 0 stands for Set or Clear I2C Data and I2C Clock Pins.

**/
STATIC
VOID
I2cPinWrite (
  EFI_PCI_IO_PROTOCOL    *PciIo,
  UINT8                  Bit,
  UINT8                  Value
  )
{
  UINT8        Byte;
  outb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL);
  Byte = (UINT8) (inb (PciIo, SEQ_DATA_REGISTER) & (UINT8) ~(1 << Bit)) ;
  Byte = (UINT8) (Byte | ((Value & 0x01) << Bit));
  outb (PciIo, SEQ_DATA_REGISTER, (UINT8) (Byte | 0x40));
  return;
}

/**
  Read/write delay acoording to I2C Bus Speed.

**/
STATIC
VOID
I2cDelay (
  VOID
  )
{
  MicroSecondDelay (1000 / I2C_BUS_SPEED);
}

/**
  Write a 8-bit data onto I2C Data Pin.

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
  @param  Data               The byte data to write.

**/
STATIC
VOID
I2cSendByte (
  EFI_PCI_IO_PROTOCOL    *PciIo,
  UINT8                  Data
  )
{
  UINTN                  Index;
  //
  // Send byte data onto I2C Bus
  //
  for (Index = 0; Index < 8; Index --) {
    I2cPinWrite (PciIo, I2CDAT_OUT, (UINT8) (Data >> (7 - Index)));
    I2cPinWrite (PciIo, I2CCLK_OUT, 1);
    I2cDelay ();
    I2cPinWrite (PciIo, I2CCLK_OUT, 0);
  }
}

/**
  Read a 8-bit data from I2C Data Pin.

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.

  Return the byte data read from I2C Data Pin.
**/
STATIC
UINT8
I2cReceiveByte (
  EFI_PCI_IO_PROTOCOL    *PciIo
  )
{
  UINT8          Data;
  UINTN          Index;

  Data = 0;
  //
  // Read byte data from I2C Bus
  //
  for (Index = 0; Index < 8; Index --) {
    I2cPinWrite (PciIo, I2CCLK_OUT, 1);
    I2cDelay ();
    Data = (UINT8) (Data << 1);
    Data = (UINT8) (Data | I2cPinRead (PciIo, I2CDAT_IN));
    I2cPinWrite (PciIo, I2CCLK_OUT, 0);
  }

  return Data;
}

/**
  Receive an ACK signal from I2C Bus.

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.

**/
STATIC
BOOLEAN
I2cWaitAck (
  EFI_PCI_IO_PROTOCOL    *PciIo
  )
{
  //
  // Wait for ACK signal
  //
  I2cPinWrite (PciIo, I2CDAT_OUT, 1);
  I2cPinWrite (PciIo, I2CCLK_OUT, 1);
  I2cDelay ();
  if (I2cPinRead (PciIo, I2CDAT_IN) == 0) {
    I2cPinWrite (PciIo, I2CDAT_OUT, 1);
    return TRUE;
  } else {
    return FALSE;
  }
}

/**
  Send an ACK signal onto I2C Bus.

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.

**/
STATIC
VOID
I2cSendAck (
  EFI_PCI_IO_PROTOCOL    *PciIo
  )
{
  I2cPinWrite (PciIo, I2CCLK_OUT, 1);
  I2cPinWrite (PciIo, I2CDAT_OUT, 1);
  I2cPinWrite (PciIo, I2CDAT_OUT, 0);
  I2cPinWrite (PciIo, I2CCLK_OUT, 0);
}

/**
  Start a I2C transfer on I2C Bus.

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.

**/
STATIC
VOID
I2cStart (
  EFI_PCI_IO_PROTOCOL    *PciIo
  )
{
  //
  // Init CLK and DAT pins
  //
  I2cPinWrite (PciIo, I2CCLK_OUT, 1);
  I2cPinWrite (PciIo, I2CDAT_OUT, 1);
  //
  // Start a I2C transfer, set SDA low from high, when SCL is high
  //
  I2cPinWrite (PciIo, I2CDAT_OUT, 0);
  I2cPinWrite (PciIo, I2CCLK_OUT, 0);
}

/**
  Stop a I2C transfer on I2C Bus.

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.

**/
STATIC
VOID
I2cStop (
  EFI_PCI_IO_PROTOCOL    *PciIo
  )
{
  //
  // Stop a I2C transfer, set SDA high from low, when SCL is high
  //
  I2cPinWrite (PciIo, I2CDAT_OUT, 0);
  I2cPinWrite (PciIo, I2CCLK_OUT, 1);
  I2cPinWrite (PciIo, I2CDAT_OUT, 1);
}

/**
  Read one byte data on I2C Bus.

  Read one byte data from the slave device connectet to I2C Bus.
  If Data is NULL, then ASSERT().

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
  @param  DeviceAddress      Slave device's address.
  @param  RegisterAddress    The register address on slave device.
  @param  Data               The pointer to returned data if EFI_SUCCESS returned.

  @retval EFI_DEVICE_ERROR
  @retval EFI_SUCCESS

**/
EFI_STATUS
EFIAPI
I2cReadByte (
  EFI_PCI_IO_PROTOCOL    *PciIo,
  UINT8                  DeviceAddress,
  UINT8                  RegisterAddress,
  UINT8                  *Data
  )
{
  ASSERT (Data != NULL);

  //
  // Start I2C transfer
  //
  I2cStart (PciIo);

  //
  // Send slave address with enabling write flag
  //
  I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe));

  //
  // Wait for ACK signal
  //
  if (I2cWaitAck (PciIo) == FALSE) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Send register address
  //
  I2cSendByte (PciIo, RegisterAddress);

  //
  // Wait for ACK signal
  //
  if (I2cWaitAck (PciIo) == FALSE) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Send slave address with enabling read flag
  //
  I2cSendByte (PciIo, (UINT8) (DeviceAddress | 0x01));

  //
  // Wait for ACK signal
  //
  if (I2cWaitAck (PciIo) == FALSE) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Read byte data from I2C Bus
  //
  *Data = I2cReceiveByte (PciIo);

  //
  // Send ACK signal onto I2C Bus
  //
  I2cSendAck (PciIo);

  //
  // Stop a I2C transfer
  //
  I2cStop (PciIo);

  return EFI_SUCCESS;
}

/**
  Write one byte data onto I2C Bus.

  Write one byte data to the slave device connectet to I2C Bus.
  If Data is NULL, then ASSERT().

  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
  @param  DeviceAddress      Slave device's address.
  @param  RegisterAddress    The register address on slave device.
  @param  Data               The pointer to write data.

  @retval EFI_DEVICE_ERROR
  @retval EFI_SUCCESS

**/
EFI_STATUS
EFIAPI
I2cWriteByte (
  EFI_PCI_IO_PROTOCOL    *PciIo,
  UINT8                  DeviceAddress,
  UINT8                  RegisterAddress,
  UINT8                  *Data
  )
{
  ASSERT (Data != NULL);

  I2cStart (PciIo);
  //
  // Send slave address with enabling write flag
  //
  I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe));

  //
  // Wait for ACK signal
  //
  if (I2cWaitAck (PciIo) == FALSE) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Send register address
  //
  I2cSendByte (PciIo, RegisterAddress);

  //
  // Wait for ACK signal
  //
  if (I2cWaitAck (PciIo) == FALSE) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Send byte data onto I2C Bus
  //
  I2cSendByte (PciIo, *Data);

  //
  // Wait for ACK signal
  //
  if (I2cWaitAck (PciIo) == FALSE) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Stop a I2C transfer
  //
  I2cStop (PciIo);

  return EFI_SUCCESS;
}