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