1/** @file
2
3  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
4
5  This program and the accompanying materials
6  are licensed and made available under the terms and conditions of the BSD License
7  which accompanies this distribution.  The full text of the license may be found at
8  http://opensource.org/licenses/bsd-license.php
9
10  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include <Uefi.h>
16#include <Omap3530/Omap3530.h>
17
18#include <Library/DebugLib.h>
19#include <Library/IoLib.h>
20#include <Library/UefiBootServicesTableLib.h>
21
22#include <Protocol/SmbusHc.h>
23
24#define MAX_RETRY  1000
25
26//
27// Internal Functions
28//
29STATIC
30EFI_STATUS
31WaitForBusBusy (
32  VOID
33  )
34{
35  UINTN Retry = 0;
36
37  while (++Retry < MAX_RETRY && (MmioRead16(I2C_STAT) & BB) == 0x1);
38
39  if (Retry == MAX_RETRY) {
40    return EFI_TIMEOUT;
41  }
42
43  return EFI_SUCCESS;
44}
45
46STATIC
47EFI_STATUS
48PollForStatus(
49  UINT16 StatusBit
50  )
51{
52  UINTN Retry = 0;
53
54  while(Retry < MAX_RETRY) {
55    if (MmioRead16(I2C_STAT) & StatusBit) {
56      //Clear particular status bit from Status register.
57      MmioOr16(I2C_STAT, StatusBit);
58      break;
59    }
60    Retry++;
61  }
62
63  if (Retry == MAX_RETRY) {
64    return EFI_TIMEOUT;
65  }
66
67  return EFI_SUCCESS;
68}
69
70STATIC
71EFI_STATUS
72ConfigureI2c (
73  VOID
74  )
75{
76  //Program prescaler to obtain 12-MHz clock
77  MmioWrite16(I2C_PSC, 0x0000);
78
79  //Program SCLL and SCLH
80  //NOTE: Following values are the register dump after U-Boot code executed.
81  //We need to figure out how its calculated based on the I2C functional clock and I2C_PSC.
82  MmioWrite16(I2C_SCLL, 0x0035);
83  MmioWrite16(I2C_SCLH, 0x0035);
84
85  //Take the I2C controller out of reset.
86  MmioOr16(I2C_CON, I2C_EN);
87
88  //Initialize the I2C controller.
89
90  //Set I2C controller in Master mode.
91  MmioOr16(I2C_CON, MST);
92
93  //Enable interrupts for receive/transmit mode.
94  MmioOr16(I2C_IE, (XRDY_IE | RRDY_IE | ARDY_IE | NACK_IE));
95
96  return EFI_SUCCESS;
97}
98
99STATIC
100EFI_STATUS
101I2CReadOneByte (
102  UINT8 *Data
103  )
104{
105  EFI_STATUS Status;
106
107  //I2C bus status checking
108  Status = WaitForBusBusy();
109  if (EFI_ERROR(Status)) {
110    return Status;
111  }
112
113  //Poll till Receive ready bit is set.
114  Status = PollForStatus(RRDY);
115  if (EFI_ERROR(Status)) {
116    return Status;
117  }
118
119  *Data = MmioRead8(I2C_DATA);
120
121  return EFI_SUCCESS;
122}
123
124STATIC
125EFI_STATUS
126I2CWriteOneByte (
127  UINT8 Data
128  )
129{
130  EFI_STATUS Status;
131
132  //I2C bus status checking
133  Status = WaitForBusBusy();
134  if (EFI_ERROR(Status)) {
135    return Status;
136  }
137
138  //Data transfer
139  //Poll till Transmit ready bit is set
140  Status = PollForStatus(XRDY);
141  if (EFI_ERROR(Status)) {
142    return Status;
143  }
144
145  MmioWrite8(I2C_DATA, Data);
146
147  //Wait and check if the NACK is not set.
148  gBS->Stall(1000);
149  if (MmioRead16(I2C_STAT) & NACK) {
150    return EFI_DEVICE_ERROR;
151  }
152
153  return EFI_SUCCESS;
154}
155
156STATIC
157EFI_STATUS
158SmbusBlockRead (
159  OUT UINT8       *Buffer,
160  IN  UINTN       Length
161  )
162{
163  UINTN      Index = 0;
164  EFI_STATUS Status = EFI_SUCCESS;
165
166  //Transfer configuration for receiving data.
167  MmioWrite16(I2C_CNT, Length);
168  //Need stop bit before sending data.
169  MmioWrite16(I2C_CON, (I2C_EN | MST | STP | STT));
170
171  while (Index < Length) {
172    //Read a byte
173    Status = I2CReadOneByte(&Buffer[Index++]);
174    if (EFI_ERROR(Status)) {
175      return Status;
176    }
177  }
178
179  //Transfer completion
180  Status = PollForStatus(ARDY);
181  if (EFI_ERROR(Status)) {
182    return Status;
183  }
184
185  return Status;
186}
187
188STATIC
189EFI_STATUS
190SmbusBlockWrite (
191  IN UINT8       *Buffer,
192  IN UINTN       Length
193  )
194{
195  UINTN      Index = 0;
196  EFI_STATUS Status = EFI_SUCCESS;
197
198  //Transfer configuration for transmitting data
199  MmioWrite16(I2C_CNT, Length);
200  MmioWrite16(I2C_CON, (I2C_EN | TRX | MST | STT | STP));
201
202  while (Index < Length) {
203    //Send a byte
204    Status = I2CWriteOneByte(Buffer[Index++]);
205    if (EFI_ERROR(Status)) {
206      return Status;
207    }
208  }
209
210  //Transfer completion
211  Status = PollForStatus(ARDY);
212  if (EFI_ERROR(Status)) {
213    return Status;
214  }
215
216  return Status;
217}
218
219//
220// Public Functions.
221//
222EFI_STATUS
223EFIAPI
224SmbusExecute (
225  IN CONST EFI_SMBUS_HC_PROTOCOL    *This,
226  IN CONST EFI_SMBUS_DEVICE_ADDRESS SlaveAddress,
227  IN CONST EFI_SMBUS_DEVICE_COMMAND Command,
228  IN CONST EFI_SMBUS_OPERATION      Operation,
229  IN CONST BOOLEAN                  PecCheck,
230  IN OUT   UINTN                    *Length,
231  IN OUT   VOID                     *Buffer
232  )
233{
234  UINT8      *ByteBuffer  = Buffer;
235  EFI_STATUS Status       = EFI_SUCCESS;
236  UINT8      SlaveAddr    = (UINT8)(SlaveAddress.SmbusDeviceAddress);
237
238  if (PecCheck) {
239    return EFI_UNSUPPORTED;
240  }
241
242  if ((Operation != EfiSmbusWriteBlock) && (Operation != EfiSmbusReadBlock)) {
243    return EFI_UNSUPPORTED;
244  }
245
246  //Set the Slave address.
247  MmioWrite16(I2C_SA, SlaveAddr);
248
249  if (Operation == EfiSmbusReadBlock) {
250    Status = SmbusBlockRead(ByteBuffer, *Length);
251  } else if (Operation == EfiSmbusWriteBlock) {
252    Status = SmbusBlockWrite(ByteBuffer, *Length);
253  }
254
255  return Status;
256}
257
258EFI_STATUS
259EFIAPI
260SmbusArpDevice (
261  IN CONST EFI_SMBUS_HC_PROTOCOL    *This,
262  IN       BOOLEAN                  ArpAll,
263  IN       EFI_SMBUS_UDID           *SmbusUdid OPTIONAL,
264  IN OUT   EFI_SMBUS_DEVICE_ADDRESS *SlaveAddress OPTIONAL
265  )
266{
267  return EFI_UNSUPPORTED;
268}
269
270
271EFI_STATUS
272EFIAPI
273SmbusGetArpMap (
274  IN CONST EFI_SMBUS_HC_PROTOCOL    *This,
275  IN OUT   UINTN                    *Length,
276  IN OUT   EFI_SMBUS_DEVICE_MAP     **SmbusDeviceMap
277  )
278{
279  return EFI_UNSUPPORTED;
280}
281
282
283EFI_STATUS
284EFIAPI
285SmbusNotify (
286  IN CONST  EFI_SMBUS_HC_PROTOCOL     *This,
287  IN CONST  EFI_SMBUS_DEVICE_ADDRESS  SlaveAddress,
288  IN CONST  UINTN                     Data,
289  IN CONST  EFI_SMBUS_NOTIFY_FUNCTION NotifyFunction
290  )
291{
292  return EFI_UNSUPPORTED;
293}
294
295EFI_SMBUS_HC_PROTOCOL SmbusProtocol =
296{
297  SmbusExecute,
298  SmbusArpDevice,
299  SmbusGetArpMap,
300  SmbusNotify
301};
302
303EFI_STATUS
304InitializeSmbus (
305    IN EFI_HANDLE       ImageHandle,
306    IN EFI_SYSTEM_TABLE *SystemTable
307    )
308{
309  EFI_HANDLE      Handle = NULL;
310  EFI_STATUS      Status;
311
312  //Configure I2C controller.
313  Status = ConfigureI2c();
314  if (EFI_ERROR(Status)) {
315    DEBUG ((EFI_D_ERROR, "InitializeI2c fails.\n"));
316    return Status;
317  }
318
319  // Install the SMBUS interface
320  Status = gBS->InstallMultipleProtocolInterfaces(&Handle, &gEfiSmbusHcProtocolGuid, &SmbusProtocol, NULL);
321  ASSERT_EFI_ERROR(Status);
322
323  return Status;
324}
325
326