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 //
29 STATIC
30 EFI_STATUS
WaitForBusBusy(VOID)31 WaitForBusBusy (
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 
46 STATIC
47 EFI_STATUS
PollForStatus(UINT16 StatusBit)48 PollForStatus(
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 
70 STATIC
71 EFI_STATUS
ConfigureI2c(VOID)72 ConfigureI2c (
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 
99 STATIC
100 EFI_STATUS
I2CReadOneByte(UINT8 * Data)101 I2CReadOneByte (
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 
124 STATIC
125 EFI_STATUS
I2CWriteOneByte(UINT8 Data)126 I2CWriteOneByte (
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 
156 STATIC
157 EFI_STATUS
SmbusBlockRead(OUT UINT8 * Buffer,IN UINTN Length)158 SmbusBlockRead (
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 
188 STATIC
189 EFI_STATUS
SmbusBlockWrite(IN UINT8 * Buffer,IN UINTN Length)190 SmbusBlockWrite (
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 //
222 EFI_STATUS
223 EFIAPI
SmbusExecute(IN CONST EFI_SMBUS_HC_PROTOCOL * This,IN CONST EFI_SMBUS_DEVICE_ADDRESS SlaveAddress,IN CONST EFI_SMBUS_DEVICE_COMMAND Command,IN CONST EFI_SMBUS_OPERATION Operation,IN CONST BOOLEAN PecCheck,IN OUT UINTN * Length,IN OUT VOID * Buffer)224 SmbusExecute (
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 
258 EFI_STATUS
259 EFIAPI
SmbusArpDevice(IN CONST EFI_SMBUS_HC_PROTOCOL * This,IN BOOLEAN ArpAll,IN EFI_SMBUS_UDID * SmbusUdid OPTIONAL,IN OUT EFI_SMBUS_DEVICE_ADDRESS * SlaveAddress OPTIONAL)260 SmbusArpDevice (
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 
271 EFI_STATUS
272 EFIAPI
SmbusGetArpMap(IN CONST EFI_SMBUS_HC_PROTOCOL * This,IN OUT UINTN * Length,IN OUT EFI_SMBUS_DEVICE_MAP ** SmbusDeviceMap)273 SmbusGetArpMap (
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 
283 EFI_STATUS
284 EFIAPI
SmbusNotify(IN CONST EFI_SMBUS_HC_PROTOCOL * This,IN CONST EFI_SMBUS_DEVICE_ADDRESS SlaveAddress,IN CONST UINTN Data,IN CONST EFI_SMBUS_NOTIFY_FUNCTION NotifyFunction)285 SmbusNotify (
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 
295 EFI_SMBUS_HC_PROTOCOL SmbusProtocol =
296 {
297   SmbusExecute,
298   SmbusArpDevice,
299   SmbusGetArpMap,
300   SmbusNotify
301 };
302 
303 EFI_STATUS
InitializeSmbus(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)304 InitializeSmbus (
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