]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / PciSioSerialDxe / SerialIo.c
CommitLineData
a59e2ede
RN
1/** @file\r
2 SerialIo implementation for PCI or SIO UARTs.\r
3\r
6932f4bf 4Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR>\r
9d510e61 5SPDX-License-Identifier: BSD-2-Clause-Patent\r
a59e2ede
RN
6\r
7**/\r
8\r
9#include "Serial.h"\r
10\r
11/**\r
12 Skip the optional Controller device path node and return the\r
13 pointer to the next device path node.\r
14\r
15 @param DevicePath Pointer to the device path.\r
16 @param ContainsControllerNode Returns TRUE if the Controller device path exists.\r
17 @param ControllerNumber Returns the Controller Number if Controller device path exists.\r
18\r
19 @return Pointer to the next device path node.\r
20**/\r
21UART_DEVICE_PATH *\r
22SkipControllerDevicePathNode (\r
1436aea4
MK
23 EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
24 BOOLEAN *ContainsControllerNode,\r
25 UINT32 *ControllerNumber\r
a59e2ede
RN
26 )\r
27{\r
28 if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) &&\r
29 (DevicePathSubType (DevicePath) == HW_CONTROLLER_DP)\r
1436aea4
MK
30 )\r
31 {\r
a59e2ede
RN
32 if (ContainsControllerNode != NULL) {\r
33 *ContainsControllerNode = TRUE;\r
34 }\r
1436aea4 35\r
a59e2ede 36 if (ControllerNumber != NULL) {\r
1436aea4 37 *ControllerNumber = ((CONTROLLER_DEVICE_PATH *)DevicePath)->ControllerNumber;\r
a59e2ede 38 }\r
1436aea4 39\r
a59e2ede
RN
40 DevicePath = NextDevicePathNode (DevicePath);\r
41 } else {\r
42 if (ContainsControllerNode != NULL) {\r
43 *ContainsControllerNode = FALSE;\r
44 }\r
45 }\r
1436aea4
MK
46\r
47 return (UART_DEVICE_PATH *)DevicePath;\r
a59e2ede
RN
48}\r
49\r
50/**\r
51 Checks whether the UART parameters are valid and computes the Divisor.\r
52\r
53 @param ClockRate The clock rate of the serial device used to verify\r
54 the BaudRate. Do not verify the BaudRate if it's 0.\r
55 @param BaudRate The requested baudrate of the serial device.\r
56 @param DataBits Number of databits used in serial device.\r
57 @param Parity The type of parity used in serial device.\r
58 @param StopBits Number of stopbits used in serial device.\r
59 @param Divisor Return the divisor if ClockRate is not 0.\r
60 @param ActualBaudRate Return the actual supported baudrate without\r
61 exceeding BaudRate. NULL means baudrate degradation\r
62 is not allowed.\r
63 If the requested BaudRate is not supported, the routine\r
64 returns TRUE and the Actual Baud Rate when ActualBaudRate\r
65 is not NULL, returns FALSE when ActualBaudRate is NULL.\r
66\r
67 @retval TRUE The UART parameters are valid.\r
68 @retval FALSE The UART parameters are not valid.\r
69**/\r
70BOOLEAN\r
71VerifyUartParameters (\r
1436aea4
MK
72 IN UINT32 ClockRate,\r
73 IN UINT64 BaudRate,\r
74 IN UINT8 DataBits,\r
75 IN EFI_PARITY_TYPE Parity,\r
76 IN EFI_STOP_BITS_TYPE StopBits,\r
77 OUT UINT64 *Divisor,\r
78 OUT UINT64 *ActualBaudRate\r
a59e2ede
RN
79 )\r
80{\r
1436aea4
MK
81 UINT64 Remainder;\r
82 UINT32 ComputedBaudRate;\r
83 UINT64 ComputedDivisor;\r
84 UINT64 Percent;\r
a59e2ede
RN
85\r
86 if ((DataBits < 5) || (DataBits > 8) ||\r
87 (Parity < NoParity) || (Parity > SpaceParity) ||\r
88 (StopBits < OneStopBit) || (StopBits > TwoStopBits) ||\r
89 ((DataBits == 5) && (StopBits == TwoStopBits)) ||\r
90 ((DataBits >= 6) && (DataBits <= 8) && (StopBits == OneFiveStopBits))\r
1436aea4
MK
91 )\r
92 {\r
a59e2ede 93 return FALSE;\r
d1102dba 94 }\r
a59e2ede
RN
95\r
96 //\r
97 // Do not verify the baud rate if clock rate is unknown (0).\r
98 //\r
99 if (ClockRate == 0) {\r
100 return TRUE;\r
101 }\r
102\r
103 //\r
104 // Compute divisor use to program the baud rate using a round determination\r
105 // Divisor = ClockRate / 16 / BaudRate = ClockRate / (16 * BaudRate)\r
106 // = ClockRate / (BaudRate << 4)\r
107 //\r
108 ComputedDivisor = DivU64x64Remainder (ClockRate, LShiftU64 (BaudRate, 4), &Remainder);\r
109 //\r
110 // Round Divisor up by 1 if the Remainder is more than half (16 * BaudRate)\r
111 // BaudRate * 16 / 2 = BaudRate * 8 = (BaudRate << 3)\r
112 //\r
113 if (Remainder >= LShiftU64 (BaudRate, 3)) {\r
114 ComputedDivisor++;\r
115 }\r
1436aea4 116\r
a59e2ede
RN
117 //\r
118 // If the computed divisor is larger than the maximum value that can be programmed\r
119 // into the UART, then the requested baud rate can not be supported.\r
120 //\r
121 if (ComputedDivisor > MAX_UINT16) {\r
122 return FALSE;\r
123 }\r
124\r
125 //\r
126 // If the computed divisor is 0, then use a computed divisor of 1, which will select\r
127 // the maximum supported baud rate.\r
128 //\r
129 if (ComputedDivisor == 0) {\r
130 ComputedDivisor = 1;\r
131 }\r
132\r
133 //\r
134 // Actual baud rate that the serial port will be programmed for\r
135 // should be with in 4% of requested one.\r
136 //\r
1436aea4 137 ComputedBaudRate = ClockRate / ((UINT16)ComputedDivisor << 4);\r
a59e2ede
RN
138 if (ComputedBaudRate == 0) {\r
139 return FALSE;\r
140 }\r
141\r
142 Percent = DivU64x32 (MultU64x32 (BaudRate, 100), ComputedBaudRate);\r
1436aea4 143 DEBUG ((DEBUG_INFO, "ClockRate = %d\n", ClockRate));\r
87000d77
MK
144 DEBUG ((DEBUG_INFO, "Divisor = %ld\n", ComputedDivisor));\r
145 DEBUG ((DEBUG_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent));\r
a59e2ede
RN
146\r
147 //\r
148 // If the requested BaudRate is not supported:\r
149 // Returns TRUE and the Actual Baud Rate when ActualBaudRate is not NULL;\r
150 // Returns FALSE when ActualBaudRate is NULL.\r
151 //\r
152 if ((Percent >= 96) && (Percent <= 104)) {\r
153 if (ActualBaudRate != NULL) {\r
154 *ActualBaudRate = BaudRate;\r
155 }\r
1436aea4 156\r
a59e2ede
RN
157 if (Divisor != NULL) {\r
158 *Divisor = ComputedDivisor;\r
159 }\r
1436aea4 160\r
a59e2ede
RN
161 return TRUE;\r
162 }\r
1436aea4 163\r
a59e2ede
RN
164 if (ComputedBaudRate < BaudRate) {\r
165 if (ActualBaudRate != NULL) {\r
166 *ActualBaudRate = ComputedBaudRate;\r
167 }\r
1436aea4 168\r
a59e2ede
RN
169 if (Divisor != NULL) {\r
170 *Divisor = ComputedDivisor;\r
171 }\r
1436aea4 172\r
a59e2ede
RN
173 return TRUE;\r
174 }\r
175\r
176 //\r
d1102dba
LG
177 // ActualBaudRate is higher than requested baud rate and more than 4%\r
178 // higher than the requested value. Increment Divisor if it is less\r
a59e2ede
RN
179 // than MAX_UINT16 and computed baud rate with new divisor.\r
180 //\r
181 if (ComputedDivisor == MAX_UINT16) {\r
182 return FALSE;\r
183 }\r
1436aea4 184\r
a59e2ede 185 ComputedDivisor++;\r
1436aea4 186 ComputedBaudRate = ClockRate / ((UINT16)ComputedDivisor << 4);\r
a59e2ede
RN
187 if (ComputedBaudRate == 0) {\r
188 return FALSE;\r
189 }\r
190\r
1436aea4 191 DEBUG ((DEBUG_INFO, "ClockRate = %d\n", ClockRate));\r
87000d77
MK
192 DEBUG ((DEBUG_INFO, "Divisor = %ld\n", ComputedDivisor));\r
193 DEBUG ((DEBUG_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent));\r
a59e2ede
RN
194\r
195 if (ActualBaudRate != NULL) {\r
196 *ActualBaudRate = ComputedBaudRate;\r
197 }\r
1436aea4 198\r
a59e2ede
RN
199 if (Divisor != NULL) {\r
200 *Divisor = ComputedDivisor;\r
201 }\r
1436aea4 202\r
a59e2ede
RN
203 return TRUE;\r
204}\r
205\r
206/**\r
207 Detect whether specific FIFO is full or not.\r
208\r
209 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO\r
210\r
211 @return whether specific FIFO is full or not\r
212**/\r
213BOOLEAN\r
214SerialFifoFull (\r
1436aea4 215 IN SERIAL_DEV_FIFO *Fifo\r
a59e2ede
RN
216 )\r
217{\r
1436aea4 218 return (BOOLEAN)(((Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE) == Fifo->Head);\r
a59e2ede
RN
219}\r
220\r
221/**\r
222 Detect whether specific FIFO is empty or not.\r
d1102dba 223\r
a59e2ede
RN
224 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO\r
225\r
226 @return whether specific FIFO is empty or not\r
227**/\r
228BOOLEAN\r
229SerialFifoEmpty (\r
1436aea4 230 IN SERIAL_DEV_FIFO *Fifo\r
a59e2ede
RN
231 )\r
232\r
233{\r
1436aea4 234 return (BOOLEAN)(Fifo->Head == Fifo->Tail);\r
a59e2ede
RN
235}\r
236\r
237/**\r
238 Add data to specific FIFO.\r
239\r
240 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO\r
241 @param Data the data added to FIFO\r
242\r
243 @retval EFI_SUCCESS Add data to specific FIFO successfully\r
244 @retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full\r
245**/\r
246EFI_STATUS\r
247SerialFifoAdd (\r
1436aea4
MK
248 IN OUT SERIAL_DEV_FIFO *Fifo,\r
249 IN UINT8 Data\r
a59e2ede
RN
250 )\r
251{\r
252 //\r
253 // if FIFO full can not add data\r
254 //\r
255 if (SerialFifoFull (Fifo)) {\r
256 return EFI_OUT_OF_RESOURCES;\r
257 }\r
1436aea4 258\r
a59e2ede
RN
259 //\r
260 // FIFO is not full can add data\r
261 //\r
262 Fifo->Data[Fifo->Tail] = Data;\r
1436aea4 263 Fifo->Tail = (Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE;\r
a59e2ede
RN
264 return EFI_SUCCESS;\r
265}\r
266\r
267/**\r
268 Remove data from specific FIFO.\r
269\r
270 @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO\r
271 @param Data the data removed from FIFO\r
272\r
273 @retval EFI_SUCCESS Remove data from specific FIFO successfully\r
274 @retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty\r
275\r
276**/\r
277EFI_STATUS\r
278SerialFifoRemove (\r
1436aea4
MK
279 IN OUT SERIAL_DEV_FIFO *Fifo,\r
280 OUT UINT8 *Data\r
a59e2ede
RN
281 )\r
282{\r
283 //\r
284 // if FIFO is empty, no data can remove\r
285 //\r
286 if (SerialFifoEmpty (Fifo)) {\r
287 return EFI_OUT_OF_RESOURCES;\r
288 }\r
1436aea4 289\r
a59e2ede
RN
290 //\r
291 // FIFO is not empty, can remove data\r
292 //\r
1436aea4 293 *Data = Fifo->Data[Fifo->Head];\r
a59e2ede
RN
294 Fifo->Head = (Fifo->Head + 1) % SERIAL_MAX_FIFO_SIZE;\r
295 return EFI_SUCCESS;\r
296}\r
297\r
298/**\r
2048c585 299 Reads and writes all available data.\r
a59e2ede
RN
300\r
301 @param SerialDevice The device to transmit.\r
302\r
303 @retval EFI_SUCCESS Data was read/written successfully.\r
304 @retval EFI_OUT_OF_RESOURCE Failed because software receive FIFO is full. Note, when\r
305 this happens, pending writes are not done.\r
306\r
307**/\r
308EFI_STATUS\r
309SerialReceiveTransmit (\r
1436aea4 310 IN SERIAL_DEV *SerialDevice\r
a59e2ede
RN
311 )\r
312\r
313{\r
1436aea4
MK
314 SERIAL_PORT_LSR Lsr;\r
315 UINT8 Data;\r
316 BOOLEAN ReceiveFifoFull;\r
317 SERIAL_PORT_MSR Msr;\r
318 SERIAL_PORT_MCR Mcr;\r
319 UINTN TimeOut;\r
a59e2ede
RN
320\r
321 Data = 0;\r
322\r
323 //\r
324 // Begin the read or write\r
325 //\r
326 if (SerialDevice->SoftwareLoopbackEnable) {\r
327 do {\r
328 ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);\r
329 if (!SerialFifoEmpty (&SerialDevice->Transmit)) {\r
330 SerialFifoRemove (&SerialDevice->Transmit, &Data);\r
331 if (ReceiveFifoFull) {\r
332 return EFI_OUT_OF_RESOURCES;\r
333 }\r
334\r
335 SerialFifoAdd (&SerialDevice->Receive, Data);\r
336 }\r
337 } while (!SerialFifoEmpty (&SerialDevice->Transmit));\r
338 } else {\r
339 ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);\r
340 //\r
341 // For full handshake flow control, tell the peer to send data\r
342 // if receive buffer is available.\r
343 //\r
344 if (SerialDevice->HardwareFlowControl &&\r
1436aea4 345 !FeaturePcdGet (PcdSerialUseHalfHandshake) &&\r
a59e2ede 346 !ReceiveFifoFull\r
1436aea4
MK
347 )\r
348 {\r
a59e2ede
RN
349 Mcr.Data = READ_MCR (SerialDevice);\r
350 Mcr.Bits.Rts = 1;\r
351 WRITE_MCR (SerialDevice, Mcr.Data);\r
352 }\r
1436aea4 353\r
a59e2ede
RN
354 do {\r
355 Lsr.Data = READ_LSR (SerialDevice);\r
356\r
357 //\r
358 // Flush incomming data to prevent a an overrun during a long write\r
359 //\r
360 if ((Lsr.Bits.Dr == 1) && !ReceiveFifoFull) {\r
361 ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);\r
362 if (!ReceiveFifoFull) {\r
1436aea4 363 if ((Lsr.Bits.FIFOe == 1) || (Lsr.Bits.Oe == 1) || (Lsr.Bits.Pe == 1) || (Lsr.Bits.Fe == 1) || (Lsr.Bits.Bi == 1)) {\r
a59e2ede
RN
364 REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
365 EFI_ERROR_CODE,\r
366 EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,\r
367 SerialDevice->DevicePath\r
368 );\r
1436aea4 369 if ((Lsr.Bits.FIFOe == 1) || (Lsr.Bits.Pe == 1) || (Lsr.Bits.Fe == 1) || (Lsr.Bits.Bi == 1)) {\r
a59e2ede
RN
370 Data = READ_RBR (SerialDevice);\r
371 continue;\r
372 }\r
373 }\r
374\r
375 Data = READ_RBR (SerialDevice);\r
376\r
377 SerialFifoAdd (&SerialDevice->Receive, Data);\r
d1102dba 378\r
a59e2ede
RN
379 //\r
380 // For full handshake flow control, if receive buffer full\r
381 // tell the peer to stop sending data.\r
382 //\r
383 if (SerialDevice->HardwareFlowControl &&\r
1436aea4 384 !FeaturePcdGet (PcdSerialUseHalfHandshake) &&\r
a59e2ede 385 SerialFifoFull (&SerialDevice->Receive)\r
1436aea4
MK
386 )\r
387 {\r
a59e2ede
RN
388 Mcr.Data = READ_MCR (SerialDevice);\r
389 Mcr.Bits.Rts = 0;\r
390 WRITE_MCR (SerialDevice, Mcr.Data);\r
391 }\r
392\r
a59e2ede
RN
393 continue;\r
394 } else {\r
395 REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
396 EFI_PROGRESS_CODE,\r
397 EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER | EFI_PERIPHERAL_SERIAL_PORT,\r
398 SerialDevice->DevicePath\r
399 );\r
400 }\r
401 }\r
1436aea4 402\r
a59e2ede
RN
403 //\r
404 // Do the write\r
405 //\r
1436aea4 406 if ((Lsr.Bits.Thre == 1) && !SerialFifoEmpty (&SerialDevice->Transmit)) {\r
a59e2ede
RN
407 //\r
408 // Make sure the transmit data will not be missed\r
409 //\r
410 if (SerialDevice->HardwareFlowControl) {\r
411 //\r
412 // For half handshake flow control assert RTS before sending.\r
413 //\r
1436aea4 414 if (FeaturePcdGet (PcdSerialUseHalfHandshake)) {\r
a59e2ede 415 Mcr.Data = READ_MCR (SerialDevice);\r
1436aea4 416 Mcr.Bits.Rts = 0;\r
a59e2ede
RN
417 WRITE_MCR (SerialDevice, Mcr.Data);\r
418 }\r
1436aea4 419\r
a59e2ede
RN
420 //\r
421 // Wait for CTS\r
422 //\r
1436aea4
MK
423 TimeOut = 0;\r
424 Msr.Data = READ_MSR (SerialDevice);\r
425 while ((Msr.Bits.Dcd == 1) && ((Msr.Bits.Cts == 0) ^ FeaturePcdGet (PcdSerialUseHalfHandshake))) {\r
a59e2ede
RN
426 gBS->Stall (TIMEOUT_STALL_INTERVAL);\r
427 TimeOut++;\r
428 if (TimeOut > 5) {\r
429 break;\r
430 }\r
431\r
432 Msr.Data = READ_MSR (SerialDevice);\r
433 }\r
434\r
1436aea4 435 if ((Msr.Bits.Dcd == 0) || ((Msr.Bits.Cts == 1) ^ FeaturePcdGet (PcdSerialUseHalfHandshake))) {\r
a59e2ede
RN
436 SerialFifoRemove (&SerialDevice->Transmit, &Data);\r
437 WRITE_THR (SerialDevice, Data);\r
438 }\r
439\r
440 //\r
441 // For half handshake flow control, tell DCE we are done.\r
442 //\r
1436aea4
MK
443 if (FeaturePcdGet (PcdSerialUseHalfHandshake)) {\r
444 Mcr.Data = READ_MCR (SerialDevice);\r
a59e2ede
RN
445 Mcr.Bits.Rts = 1;\r
446 WRITE_MCR (SerialDevice, Mcr.Data);\r
447 }\r
448 } else {\r
449 SerialFifoRemove (&SerialDevice->Transmit, &Data);\r
450 WRITE_THR (SerialDevice, Data);\r
451 }\r
452 }\r
453 } while (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit));\r
454 }\r
455\r
456 return EFI_SUCCESS;\r
457}\r
458\r
6932f4bf
MK
459/**\r
460 Flush the serial hardware transmit FIFO, holding register, and shift register.\r
461\r
462 @param SerialDevice The device to flush.\r
463\r
464 @retval EFI_SUCCESS The transmit FIFO is completely flushed.\r
465 @retval EFI_TIMEOUT A timeout occured waiting for the transmit FIFO to flush.\r
466**/\r
467EFI_STATUS\r
468SerialFlushTransmitFifo (\r
469 SERIAL_DEV *SerialDevice\r
470 )\r
471{\r
472 SERIAL_PORT_LSR Lsr;\r
473 UINTN Timeout;\r
474 UINTN Elapsed;\r
475\r
476 //\r
477 // If this is the first time the UART is being configured, then the current\r
478 // UART settings are not known, so compute a timeout to wait for the Tx FIFO\r
479 // assuming the worst case current settings.\r
480 //\r
481 // Timeout = (Max Bits per Char) * (Max Pending Chars) / (Slowest Baud Rate)\r
482 // Max Bits per Char = Start bit + 8 data bits + parity + 2 stop bits = 12\r
483 // Max Pending Chars = Largest Tx FIFO + hold + shift = 64 + 1 + 1 = 66\r
484 // Slowest Reasonable Baud Rate = 300 baud\r
485 // Timeout = 12 * 66 / 300 = 2.64 seconds = 2,640,000 uS\r
486 //\r
487 Timeout = 2640000;\r
488\r
489 //\r
490 // Use the largest of the computed timeout, the default timeout, and the\r
491 // currently set timeout.\r
492 //\r
493 Timeout = MAX (Timeout, SERIAL_PORT_DEFAULT_TIMEOUT);\r
494 Timeout = MAX (Timeout, SerialDevice->SerialMode.Timeout);\r
495\r
496 //\r
497 // Wait for the shortest time possible for the serial port to be ready making\r
498 // sure the transmit FIFO, holding register, and shift register are all\r
499 // empty. The actual wait time is expected to be very small because the\r
500 // number characters currently in the FIFO should be small when a\r
501 // configuration change is requested.\r
502 //\r
503 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls\r
504 // in the rest of this function that may send additional characters to this\r
505 // UART device invalidating the flush operation.\r
506 //\r
1436aea4 507 Elapsed = 0;\r
6932f4bf
MK
508 Lsr.Data = READ_LSR (SerialDevice);\r
509 while (Lsr.Bits.Temt == 0 || Lsr.Bits.Thre == 0) {\r
510 if (Elapsed >= Timeout) {\r
511 return EFI_TIMEOUT;\r
512 }\r
1436aea4 513\r
6932f4bf
MK
514 gBS->Stall (TIMEOUT_STALL_INTERVAL);\r
515 Elapsed += TIMEOUT_STALL_INTERVAL;\r
516 Lsr.Data = READ_LSR (SerialDevice);\r
517 }\r
518\r
519 return EFI_SUCCESS;\r
520}\r
521\r
a59e2ede
RN
522//\r
523// Interface Functions\r
524//\r
1436aea4 525\r
a59e2ede
RN
526/**\r
527 Reset serial device.\r
528\r
529 @param This Pointer to EFI_SERIAL_IO_PROTOCOL\r
530\r
531 @retval EFI_SUCCESS Reset successfully\r
532 @retval EFI_DEVICE_ERROR Failed to reset\r
533\r
534**/\r
535EFI_STATUS\r
536EFIAPI\r
537SerialReset (\r
538 IN EFI_SERIAL_IO_PROTOCOL *This\r
539 )\r
540{\r
1436aea4
MK
541 EFI_STATUS Status;\r
542 SERIAL_DEV *SerialDevice;\r
543 SERIAL_PORT_LCR Lcr;\r
544 SERIAL_PORT_IER Ier;\r
545 SERIAL_PORT_MCR Mcr;\r
546 SERIAL_PORT_FCR Fcr;\r
547 EFI_TPL Tpl;\r
548 UINT32 Control;\r
a59e2ede
RN
549\r
550 SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
551\r
552 //\r
553 // Report the status code reset the serial\r
554 //\r
555 REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
556 EFI_PROGRESS_CODE,\r
557 EFI_P_PC_RESET | EFI_PERIPHERAL_SERIAL_PORT,\r
558 SerialDevice->DevicePath\r
559 );\r
560\r
561 Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
562\r
6932f4bf
MK
563 //\r
564 // Wait for all data to be transmitted before changing the UART configuration.\r
565 //\r
566 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls\r
567 // that may send additional characters to this UART device until the UART\r
568 // configuration change is complete.\r
569 //\r
570 SerialFlushTransmitFifo (SerialDevice);\r
571\r
a59e2ede
RN
572 //\r
573 // Make sure DLAB is 0.\r
574 //\r
575 Lcr.Data = READ_LCR (SerialDevice);\r
576 Lcr.Bits.DLab = 0;\r
577 WRITE_LCR (SerialDevice, Lcr.Data);\r
578\r
579 //\r
580 // Turn off all interrupts\r
581 //\r
1436aea4
MK
582 Ier.Data = READ_IER (SerialDevice);\r
583 Ier.Bits.Ravie = 0;\r
584 Ier.Bits.Theie = 0;\r
585 Ier.Bits.Rie = 0;\r
586 Ier.Bits.Mie = 0;\r
a59e2ede
RN
587 WRITE_IER (SerialDevice, Ier.Data);\r
588\r
589 //\r
590 // Reset the FIFO\r
591 //\r
1436aea4 592 Fcr.Data = 0;\r
a59e2ede
RN
593 Fcr.Bits.TrFIFOE = 0;\r
594 WRITE_FCR (SerialDevice, Fcr.Data);\r
595\r
596 //\r
597 // Turn off loopback and disable device interrupt.\r
598 //\r
599 Mcr.Data = READ_MCR (SerialDevice);\r
600 Mcr.Bits.Out1 = 0;\r
601 Mcr.Bits.Out2 = 0;\r
602 Mcr.Bits.Lme = 0;\r
603 WRITE_MCR (SerialDevice, Mcr.Data);\r
604\r
605 //\r
606 // Clear the scratch pad register\r
607 //\r
608 WRITE_SCR (SerialDevice, 0);\r
609\r
610 //\r
611 // Enable FIFO\r
612 //\r
1436aea4 613 Fcr.Bits.TrFIFOE = 1;\r
a59e2ede
RN
614 if (SerialDevice->ReceiveFifoDepth > 16) {\r
615 Fcr.Bits.TrFIFO64 = 1;\r
616 }\r
1436aea4
MK
617\r
618 Fcr.Bits.ResetRF = 1;\r
619 Fcr.Bits.ResetTF = 1;\r
a59e2ede
RN
620 WRITE_FCR (SerialDevice, Fcr.Data);\r
621\r
622 //\r
623 // Go set the current attributes\r
624 //\r
625 Status = This->SetAttributes (\r
626 This,\r
627 This->Mode->BaudRate,\r
628 This->Mode->ReceiveFifoDepth,\r
629 This->Mode->Timeout,\r
1436aea4
MK
630 (EFI_PARITY_TYPE)This->Mode->Parity,\r
631 (UINT8)This->Mode->DataBits,\r
632 (EFI_STOP_BITS_TYPE)This->Mode->StopBits\r
a59e2ede
RN
633 );\r
634\r
635 if (EFI_ERROR (Status)) {\r
636 gBS->RestoreTPL (Tpl);\r
637 return EFI_DEVICE_ERROR;\r
638 }\r
1436aea4 639\r
a59e2ede
RN
640 //\r
641 // Go set the current control bits\r
642 //\r
643 Control = 0;\r
644 if (SerialDevice->HardwareFlowControl) {\r
645 Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;\r
646 }\r
1436aea4 647\r
a59e2ede
RN
648 if (SerialDevice->SoftwareLoopbackEnable) {\r
649 Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;\r
650 }\r
1436aea4 651\r
a59e2ede
RN
652 Status = This->SetControl (\r
653 This,\r
654 Control\r
655 );\r
656\r
657 if (EFI_ERROR (Status)) {\r
658 gBS->RestoreTPL (Tpl);\r
659 return EFI_DEVICE_ERROR;\r
660 }\r
661\r
662 //\r
663 // Reset the software FIFO\r
664 //\r
1436aea4 665 SerialDevice->Receive.Head = SerialDevice->Receive.Tail = 0;\r
a59e2ede
RN
666 SerialDevice->Transmit.Head = SerialDevice->Transmit.Tail = 0;\r
667 gBS->RestoreTPL (Tpl);\r
668\r
669 //\r
670 // Device reset is complete\r
671 //\r
672 return EFI_SUCCESS;\r
673}\r
674\r
675/**\r
676 Set new attributes to a serial device.\r
677\r
678 @param This Pointer to EFI_SERIAL_IO_PROTOCOL\r
679 @param BaudRate The baudrate of the serial device\r
680 @param ReceiveFifoDepth The depth of receive FIFO buffer\r
681 @param Timeout The request timeout for a single char\r
682 @param Parity The type of parity used in serial device\r
683 @param DataBits Number of databits used in serial device\r
684 @param StopBits Number of stopbits used in serial device\r
685\r
686 @retval EFI_SUCCESS The new attributes were set\r
687 @retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value\r
688 @retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6\r
689 @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return)\r
690\r
691**/\r
692EFI_STATUS\r
693EFIAPI\r
694SerialSetAttributes (\r
695 IN EFI_SERIAL_IO_PROTOCOL *This,\r
696 IN UINT64 BaudRate,\r
697 IN UINT32 ReceiveFifoDepth,\r
698 IN UINT32 Timeout,\r
699 IN EFI_PARITY_TYPE Parity,\r
700 IN UINT8 DataBits,\r
701 IN EFI_STOP_BITS_TYPE StopBits\r
702 )\r
703{\r
1436aea4
MK
704 EFI_STATUS Status;\r
705 SERIAL_DEV *SerialDevice;\r
706 UINT64 Divisor;\r
707 SERIAL_PORT_LCR Lcr;\r
708 UART_DEVICE_PATH *Uart;\r
709 EFI_TPL Tpl;\r
a59e2ede
RN
710\r
711 SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
712\r
713 //\r
714 // Check for default settings and fill in actual values.\r
715 //\r
716 if (BaudRate == 0) {\r
717 BaudRate = PcdGet64 (PcdUartDefaultBaudRate);\r
718 }\r
719\r
720 if (ReceiveFifoDepth == 0) {\r
721 ReceiveFifoDepth = SerialDevice->ReceiveFifoDepth;\r
722 }\r
723\r
724 if (Timeout == 0) {\r
725 Timeout = SERIAL_PORT_DEFAULT_TIMEOUT;\r
726 }\r
727\r
728 if (Parity == DefaultParity) {\r
1436aea4 729 Parity = (EFI_PARITY_TYPE)PcdGet8 (PcdUartDefaultParity);\r
a59e2ede
RN
730 }\r
731\r
732 if (DataBits == 0) {\r
733 DataBits = PcdGet8 (PcdUartDefaultDataBits);\r
734 }\r
735\r
736 if (StopBits == DefaultStopBits) {\r
1436aea4 737 StopBits = (EFI_STOP_BITS_TYPE)PcdGet8 (PcdUartDefaultStopBits);\r
a59e2ede
RN
738 }\r
739\r
740 if (!VerifyUartParameters (SerialDevice->ClockRate, BaudRate, DataBits, Parity, StopBits, &Divisor, &BaudRate)) {\r
741 return EFI_INVALID_PARAMETER;\r
742 }\r
743\r
744 if ((ReceiveFifoDepth == 0) || (ReceiveFifoDepth > SerialDevice->ReceiveFifoDepth)) {\r
745 return EFI_INVALID_PARAMETER;\r
746 }\r
747\r
748 if ((Timeout < SERIAL_PORT_MIN_TIMEOUT) || (Timeout > SERIAL_PORT_MAX_TIMEOUT)) {\r
749 return EFI_INVALID_PARAMETER;\r
750 }\r
751\r
752 Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
753\r
6932f4bf
MK
754 //\r
755 // Wait for all data to be transmitted before changing the UART configuration.\r
756 //\r
757 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls\r
758 // that may send additional characters to this UART device until the UART\r
759 // configuration change is complete.\r
760 //\r
761 SerialFlushTransmitFifo (SerialDevice);\r
762\r
a59e2ede
RN
763 //\r
764 // Put serial port on Divisor Latch Mode\r
765 //\r
766 Lcr.Data = READ_LCR (SerialDevice);\r
767 Lcr.Bits.DLab = 1;\r
768 WRITE_LCR (SerialDevice, Lcr.Data);\r
769\r
770 //\r
771 // Write the divisor to the serial port\r
772 //\r
1436aea4
MK
773 WRITE_DLL (SerialDevice, (UINT8)Divisor);\r
774 WRITE_DLM (SerialDevice, (UINT8)((UINT16)Divisor >> 8));\r
a59e2ede
RN
775\r
776 //\r
777 // Put serial port back in normal mode and set remaining attributes.\r
778 //\r
779 Lcr.Bits.DLab = 0;\r
780\r
781 switch (Parity) {\r
1436aea4
MK
782 case NoParity:\r
783 Lcr.Bits.ParEn = 0;\r
784 Lcr.Bits.EvenPar = 0;\r
785 Lcr.Bits.SticPar = 0;\r
786 break;\r
787\r
788 case EvenParity:\r
789 Lcr.Bits.ParEn = 1;\r
790 Lcr.Bits.EvenPar = 1;\r
791 Lcr.Bits.SticPar = 0;\r
792 break;\r
793\r
794 case OddParity:\r
795 Lcr.Bits.ParEn = 1;\r
796 Lcr.Bits.EvenPar = 0;\r
797 Lcr.Bits.SticPar = 0;\r
798 break;\r
799\r
800 case SpaceParity:\r
801 Lcr.Bits.ParEn = 1;\r
802 Lcr.Bits.EvenPar = 1;\r
803 Lcr.Bits.SticPar = 1;\r
804 break;\r
805\r
806 case MarkParity:\r
807 Lcr.Bits.ParEn = 1;\r
808 Lcr.Bits.EvenPar = 0;\r
809 Lcr.Bits.SticPar = 1;\r
810 break;\r
811\r
812 default:\r
813 break;\r
a59e2ede
RN
814 }\r
815\r
816 switch (StopBits) {\r
1436aea4
MK
817 case OneStopBit:\r
818 Lcr.Bits.StopB = 0;\r
819 break;\r
a59e2ede 820\r
1436aea4
MK
821 case OneFiveStopBits:\r
822 case TwoStopBits:\r
823 Lcr.Bits.StopB = 1;\r
824 break;\r
a59e2ede 825\r
1436aea4
MK
826 default:\r
827 break;\r
a59e2ede 828 }\r
1436aea4 829\r
a59e2ede
RN
830 //\r
831 // DataBits\r
832 //\r
1436aea4 833 Lcr.Bits.SerialDB = (UINT8)((DataBits - 5) & 0x03);\r
a59e2ede
RN
834 WRITE_LCR (SerialDevice, Lcr.Data);\r
835\r
836 //\r
837 // Set the Serial I/O mode\r
838 //\r
1436aea4
MK
839 This->Mode->BaudRate = BaudRate;\r
840 This->Mode->ReceiveFifoDepth = ReceiveFifoDepth;\r
841 This->Mode->Timeout = Timeout;\r
842 This->Mode->Parity = Parity;\r
843 This->Mode->DataBits = DataBits;\r
844 This->Mode->StopBits = StopBits;\r
a59e2ede
RN
845\r
846 //\r
847 // See if Device Path Node has actually changed\r
848 //\r
1436aea4
MK
849 if ((SerialDevice->UartDevicePath.BaudRate == BaudRate) &&\r
850 (SerialDevice->UartDevicePath.DataBits == DataBits) &&\r
851 (SerialDevice->UartDevicePath.Parity == Parity) &&\r
852 (SerialDevice->UartDevicePath.StopBits == StopBits)\r
853 )\r
854 {\r
a59e2ede
RN
855 gBS->RestoreTPL (Tpl);\r
856 return EFI_SUCCESS;\r
857 }\r
1436aea4 858\r
a59e2ede
RN
859 //\r
860 // Update the device path\r
861 //\r
862 SerialDevice->UartDevicePath.BaudRate = BaudRate;\r
863 SerialDevice->UartDevicePath.DataBits = DataBits;\r
1436aea4
MK
864 SerialDevice->UartDevicePath.Parity = (UINT8)Parity;\r
865 SerialDevice->UartDevicePath.StopBits = (UINT8)StopBits;\r
a59e2ede
RN
866\r
867 Status = EFI_SUCCESS;\r
868 if (SerialDevice->Handle != NULL) {\r
a59e2ede
RN
869 //\r
870 // Skip the optional Controller device path node\r
871 //\r
872 Uart = SkipControllerDevicePathNode (\r
1436aea4
MK
873 (EFI_DEVICE_PATH_PROTOCOL *)(\r
874 (UINT8 *)SerialDevice->DevicePath + GetDevicePathSize (SerialDevice->ParentDevicePath) - END_DEVICE_PATH_LENGTH\r
875 ),\r
a59e2ede
RN
876 NULL,\r
877 NULL\r
878 );\r
879 CopyMem (Uart, &SerialDevice->UartDevicePath, sizeof (UART_DEVICE_PATH));\r
880 Status = gBS->ReinstallProtocolInterface (\r
881 SerialDevice->Handle,\r
882 &gEfiDevicePathProtocolGuid,\r
883 SerialDevice->DevicePath,\r
884 SerialDevice->DevicePath\r
885 );\r
886 }\r
887\r
888 gBS->RestoreTPL (Tpl);\r
889\r
890 return Status;\r
891}\r
892\r
893/**\r
894 Set Control Bits.\r
895\r
896 @param This Pointer to EFI_SERIAL_IO_PROTOCOL\r
897 @param Control Control bits that can be settable\r
898\r
899 @retval EFI_SUCCESS New Control bits were set successfully\r
900 @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported\r
901\r
902**/\r
903EFI_STATUS\r
904EFIAPI\r
905SerialSetControl (\r
906 IN EFI_SERIAL_IO_PROTOCOL *This,\r
907 IN UINT32 Control\r
908 )\r
909{\r
1436aea4
MK
910 SERIAL_DEV *SerialDevice;\r
911 SERIAL_PORT_MCR Mcr;\r
912 EFI_TPL Tpl;\r
913 UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;\r
914 EFI_STATUS Status;\r
a59e2ede
RN
915\r
916 //\r
917 // The control bits that can be set are :\r
918 // EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO\r
919 // EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO\r
920 // EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW\r
921 // EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW\r
922 // EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW\r
923 //\r
924 SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
925\r
926 //\r
927 // first determine the parameter is invalid\r
928 //\r
929 if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |\r
930 EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |\r
1436aea4
MK
931 EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0)\r
932 {\r
a59e2ede
RN
933 return EFI_UNSUPPORTED;\r
934 }\r
935\r
936 Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
937\r
6932f4bf
MK
938 //\r
939 // Wait for all data to be transmitted before changing the UART configuration.\r
940 //\r
941 // NOTE: Do not use any DEBUG() or REPORT_STATUS_CODE() or any other calls\r
942 // that may send additional characters to this UART device until the UART\r
943 // configuration change is complete.\r
944 //\r
945 SerialFlushTransmitFifo (SerialDevice);\r
946\r
1436aea4
MK
947 Mcr.Data = READ_MCR (SerialDevice);\r
948 Mcr.Bits.DtrC = 0;\r
949 Mcr.Bits.Rts = 0;\r
950 Mcr.Bits.Lme = 0;\r
a59e2ede 951 SerialDevice->SoftwareLoopbackEnable = FALSE;\r
1436aea4 952 SerialDevice->HardwareFlowControl = FALSE;\r
a59e2ede
RN
953\r
954 if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {\r
955 Mcr.Bits.DtrC = 1;\r
956 }\r
957\r
958 if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {\r
959 Mcr.Bits.Rts = 1;\r
960 }\r
961\r
962 if ((Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) {\r
963 Mcr.Bits.Lme = 1;\r
964 }\r
965\r
966 if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) {\r
967 SerialDevice->HardwareFlowControl = TRUE;\r
968 }\r
969\r
970 WRITE_MCR (SerialDevice, Mcr.Data);\r
971\r
972 if ((Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) {\r
973 SerialDevice->SoftwareLoopbackEnable = TRUE;\r
974 }\r
975\r
976 Status = EFI_SUCCESS;\r
977 if (SerialDevice->Handle != NULL) {\r
1436aea4
MK
978 FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *)(\r
979 (UINTN)SerialDevice->DevicePath\r
980 + GetDevicePathSize (SerialDevice->ParentDevicePath)\r
981 - END_DEVICE_PATH_LENGTH\r
982 + sizeof (UART_DEVICE_PATH)\r
983 );\r
a59e2ede 984 if (IsUartFlowControlDevicePathNode (FlowControl) &&\r
1436aea4
MK
985 ((BOOLEAN)(ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) != SerialDevice->HardwareFlowControl))\r
986 {\r
a59e2ede
RN
987 //\r
988 // Flow Control setting is changed, need to reinstall device path protocol\r
989 //\r
990 WriteUnaligned32 (&FlowControl->FlowControlMap, SerialDevice->HardwareFlowControl ? UART_FLOW_CONTROL_HARDWARE : 0);\r
991 Status = gBS->ReinstallProtocolInterface (\r
992 SerialDevice->Handle,\r
993 &gEfiDevicePathProtocolGuid,\r
994 SerialDevice->DevicePath,\r
995 SerialDevice->DevicePath\r
996 );\r
997 }\r
998 }\r
999\r
1000 gBS->RestoreTPL (Tpl);\r
1001\r
1002 return Status;\r
1003}\r
1004\r
1005/**\r
1006 Get ControlBits.\r
1007\r
1008 @param This Pointer to EFI_SERIAL_IO_PROTOCOL\r
1009 @param Control Control signals of the serial device\r
1010\r
1011 @retval EFI_SUCCESS Get Control signals successfully\r
1012\r
1013**/\r
1014EFI_STATUS\r
1015EFIAPI\r
1016SerialGetControl (\r
1017 IN EFI_SERIAL_IO_PROTOCOL *This,\r
1018 OUT UINT32 *Control\r
1019 )\r
1020{\r
1436aea4
MK
1021 SERIAL_DEV *SerialDevice;\r
1022 SERIAL_PORT_MSR Msr;\r
1023 SERIAL_PORT_MCR Mcr;\r
1024 EFI_TPL Tpl;\r
a59e2ede 1025\r
1436aea4 1026 Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
a59e2ede 1027\r
1436aea4 1028 SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
a59e2ede 1029\r
1436aea4 1030 *Control = 0;\r
a59e2ede
RN
1031\r
1032 //\r
1033 // Read the Modem Status Register\r
1034 //\r
1035 Msr.Data = READ_MSR (SerialDevice);\r
1036\r
1037 if (Msr.Bits.Cts == 1) {\r
1038 *Control |= EFI_SERIAL_CLEAR_TO_SEND;\r
1039 }\r
1040\r
1041 if (Msr.Bits.Dsr == 1) {\r
1042 *Control |= EFI_SERIAL_DATA_SET_READY;\r
1043 }\r
1044\r
1045 if (Msr.Bits.Ri == 1) {\r
1046 *Control |= EFI_SERIAL_RING_INDICATE;\r
1047 }\r
1048\r
1049 if (Msr.Bits.Dcd == 1) {\r
1050 *Control |= EFI_SERIAL_CARRIER_DETECT;\r
1051 }\r
1436aea4 1052\r
a59e2ede
RN
1053 //\r
1054 // Read the Modem Control Register\r
1055 //\r
1056 Mcr.Data = READ_MCR (SerialDevice);\r
1057\r
1058 if (Mcr.Bits.DtrC == 1) {\r
1059 *Control |= EFI_SERIAL_DATA_TERMINAL_READY;\r
1060 }\r
1061\r
1062 if (Mcr.Bits.Rts == 1) {\r
1063 *Control |= EFI_SERIAL_REQUEST_TO_SEND;\r
1064 }\r
1065\r
1066 if (Mcr.Bits.Lme == 1) {\r
1067 *Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE;\r
1068 }\r
1069\r
1070 if (SerialDevice->HardwareFlowControl) {\r
1071 *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;\r
1072 }\r
1436aea4 1073\r
a59e2ede
RN
1074 //\r
1075 // Update FIFO status\r
1076 //\r
1077 SerialReceiveTransmit (SerialDevice);\r
1078\r
1079 //\r
1080 // See if the Transmit FIFO is empty\r
1081 //\r
1082 if (SerialFifoEmpty (&SerialDevice->Transmit)) {\r
1083 *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;\r
1084 }\r
1085\r
1086 //\r
1087 // See if the Receive FIFO is empty.\r
1088 //\r
1089 if (SerialFifoEmpty (&SerialDevice->Receive)) {\r
1090 *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;\r
1091 }\r
1092\r
1093 if (SerialDevice->SoftwareLoopbackEnable) {\r
1094 *Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;\r
1095 }\r
1096\r
1097 gBS->RestoreTPL (Tpl);\r
1098\r
1099 return EFI_SUCCESS;\r
1100}\r
1101\r
1102/**\r
1103 Write the specified number of bytes to serial device.\r
1104\r
1105 @param This Pointer to EFI_SERIAL_IO_PROTOCOL\r
1106 @param BufferSize On input the size of Buffer, on output the amount of\r
1107 data actually written\r
1108 @param Buffer The buffer of data to write\r
1109\r
1110 @retval EFI_SUCCESS The data were written successfully\r
1111 @retval EFI_DEVICE_ERROR The device reported an error\r
1112 @retval EFI_TIMEOUT The write operation was stopped due to timeout\r
1113\r
1114**/\r
1115EFI_STATUS\r
1116EFIAPI\r
1117SerialWrite (\r
1118 IN EFI_SERIAL_IO_PROTOCOL *This,\r
1119 IN OUT UINTN *BufferSize,\r
1120 IN VOID *Buffer\r
1121 )\r
1122{\r
1123 SERIAL_DEV *SerialDevice;\r
1124 UINT8 *CharBuffer;\r
1125 UINT32 Index;\r
1126 UINTN Elapsed;\r
1127 UINTN ActualWrite;\r
1128 EFI_TPL Tpl;\r
1129 UINTN Timeout;\r
1130 UINTN BitsPerCharacter;\r
1131\r
1436aea4
MK
1132 SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
1133 Elapsed = 0;\r
1134 ActualWrite = 0;\r
a59e2ede
RN
1135\r
1136 if (*BufferSize == 0) {\r
1137 return EFI_SUCCESS;\r
1138 }\r
1139\r
1140 if (Buffer == NULL) {\r
1141 REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
1142 EFI_ERROR_CODE,\r
1143 EFI_P_EC_OUTPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,\r
1144 SerialDevice->DevicePath\r
1145 );\r
1146\r
1147 return EFI_DEVICE_ERROR;\r
1148 }\r
1149\r
1436aea4 1150 Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
a59e2ede 1151\r
1436aea4 1152 CharBuffer = (UINT8 *)Buffer;\r
a59e2ede
RN
1153\r
1154 //\r
1155 // Compute the number of bits in a single character. This is a start bit,\r
1156 // followed by the number of data bits, followed by the number of stop bits.\r
1157 // The number of stop bits is specified by an enumeration that includes\r
1158 // support for 1.5 stop bits. Treat 1.5 stop bits as 2 stop bits.\r
1159 //\r
1160 BitsPerCharacter =\r
1161 1 +\r
1162 This->Mode->DataBits +\r
1163 ((This->Mode->StopBits == TwoStopBits) ? 2 : This->Mode->StopBits);\r
1164\r
1165 //\r
1166 // Compute the timeout in microseconds to wait for a single byte to be\r
1167 // transmitted. The Mode structure contans a Timeout field that is the\r
1168 // maximum time to transmit or receive a character. However, many UARTs\r
1169 // have a FIFO for transmits, so the time required to add one new character\r
1170 // to the transmit FIFO may be the time required to flush a full FIFO. If\r
1171 // the Timeout in the Mode structure is smaller than the time required to\r
1172 // flush a full FIFO at the current baud rate, then use a timeout value that\r
1173 // is required to flush a full transmit FIFO.\r
1174 //\r
1175 Timeout = MAX (\r
1176 This->Mode->Timeout,\r
1177 (UINTN)DivU64x64Remainder (\r
1436aea4
MK
1178 BitsPerCharacter * (SerialDevice->TransmitFifoDepth + 1) * 1000000,\r
1179 This->Mode->BaudRate,\r
1180 NULL\r
1181 )\r
a59e2ede 1182 );\r
d1102dba 1183\r
a59e2ede
RN
1184 for (Index = 0; Index < *BufferSize; Index++) {\r
1185 SerialFifoAdd (&SerialDevice->Transmit, CharBuffer[Index]);\r
1186\r
1187 while (SerialReceiveTransmit (SerialDevice) != EFI_SUCCESS || !SerialFifoEmpty (&SerialDevice->Transmit)) {\r
1188 //\r
1189 // Unsuccessful write so check if timeout has expired, if not,\r
1190 // stall for a bit, increment time elapsed, and try again\r
1191 //\r
1192 if (Elapsed >= Timeout) {\r
1193 *BufferSize = ActualWrite;\r
1194 gBS->RestoreTPL (Tpl);\r
1195 return EFI_TIMEOUT;\r
1196 }\r
1197\r
1198 gBS->Stall (TIMEOUT_STALL_INTERVAL);\r
1199\r
1200 Elapsed += TIMEOUT_STALL_INTERVAL;\r
1201 }\r
1202\r
1203 ActualWrite++;\r
1204 //\r
1205 // Successful write so reset timeout\r
1206 //\r
1207 Elapsed = 0;\r
1208 }\r
1209\r
1210 gBS->RestoreTPL (Tpl);\r
1211\r
1212 return EFI_SUCCESS;\r
1213}\r
1214\r
1215/**\r
1216 Read the specified number of bytes from serial device.\r
1217\r
1218 @param This Pointer to EFI_SERIAL_IO_PROTOCOL\r
1219 @param BufferSize On input the size of Buffer, on output the amount of\r
1220 data returned in buffer\r
1221 @param Buffer The buffer to return the data into\r
1222\r
1223 @retval EFI_SUCCESS The data were read successfully\r
1224 @retval EFI_DEVICE_ERROR The device reported an error\r
1225 @retval EFI_TIMEOUT The read operation was stopped due to timeout\r
1226\r
1227**/\r
1228EFI_STATUS\r
1229EFIAPI\r
1230SerialRead (\r
1231 IN EFI_SERIAL_IO_PROTOCOL *This,\r
1232 IN OUT UINTN *BufferSize,\r
1233 OUT VOID *Buffer\r
1234 )\r
1235{\r
1236 SERIAL_DEV *SerialDevice;\r
1237 UINT32 Index;\r
1238 UINT8 *CharBuffer;\r
1239 UINTN Elapsed;\r
1240 EFI_STATUS Status;\r
1241 EFI_TPL Tpl;\r
1242\r
1436aea4
MK
1243 SerialDevice = SERIAL_DEV_FROM_THIS (This);\r
1244 Elapsed = 0;\r
a59e2ede
RN
1245\r
1246 if (*BufferSize == 0) {\r
1247 return EFI_SUCCESS;\r
1248 }\r
1249\r
1250 if (Buffer == NULL) {\r
1251 return EFI_DEVICE_ERROR;\r
1252 }\r
1253\r
1436aea4 1254 Tpl = gBS->RaiseTPL (TPL_NOTIFY);\r
a59e2ede 1255\r
1436aea4 1256 Status = SerialReceiveTransmit (SerialDevice);\r
a59e2ede
RN
1257\r
1258 if (EFI_ERROR (Status)) {\r
1259 *BufferSize = 0;\r
1260\r
1261 REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
1262 EFI_ERROR_CODE,\r
1263 EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,\r
1264 SerialDevice->DevicePath\r
1265 );\r
1266\r
1267 gBS->RestoreTPL (Tpl);\r
1268\r
1269 return EFI_DEVICE_ERROR;\r
1270 }\r
1271\r
1436aea4 1272 CharBuffer = (UINT8 *)Buffer;\r
a59e2ede
RN
1273 for (Index = 0; Index < *BufferSize; Index++) {\r
1274 while (SerialFifoRemove (&SerialDevice->Receive, &(CharBuffer[Index])) != EFI_SUCCESS) {\r
1275 //\r
1276 // Unsuccessful read so check if timeout has expired, if not,\r
1277 // stall for a bit, increment time elapsed, and try again\r
1278 // Need this time out to get conspliter to work.\r
1279 //\r
1280 if (Elapsed >= This->Mode->Timeout) {\r
1281 *BufferSize = Index;\r
1282 gBS->RestoreTPL (Tpl);\r
1283 return EFI_TIMEOUT;\r
1284 }\r
1285\r
1286 gBS->Stall (TIMEOUT_STALL_INTERVAL);\r
1287 Elapsed += TIMEOUT_STALL_INTERVAL;\r
1288\r
1289 Status = SerialReceiveTransmit (SerialDevice);\r
1290 if (Status == EFI_DEVICE_ERROR) {\r
1291 *BufferSize = Index;\r
1292 gBS->RestoreTPL (Tpl);\r
1293 return EFI_DEVICE_ERROR;\r
1294 }\r
1295 }\r
1436aea4 1296\r
a59e2ede
RN
1297 //\r
1298 // Successful read so reset timeout\r
1299 //\r
1300 Elapsed = 0;\r
1301 }\r
1302\r
1303 SerialReceiveTransmit (SerialDevice);\r
1304\r
1305 gBS->RestoreTPL (Tpl);\r
1306\r
1307 return EFI_SUCCESS;\r
1308}\r
1309\r
1310/**\r
1311 Use scratchpad register to test if this serial port is present.\r
1312\r
1313 @param SerialDevice Pointer to serial device structure\r
1314\r
1315 @return if this serial port is present\r
1316**/\r
1317BOOLEAN\r
1318SerialPresent (\r
1436aea4 1319 IN SERIAL_DEV *SerialDevice\r
a59e2ede
RN
1320 )\r
1321\r
1322{\r
1436aea4
MK
1323 UINT8 Temp;\r
1324 BOOLEAN Status;\r
a59e2ede
RN
1325\r
1326 Status = TRUE;\r
1327\r
1328 //\r
1329 // Save SCR reg\r
1330 //\r
1331 Temp = READ_SCR (SerialDevice);\r
1332 WRITE_SCR (SerialDevice, 0xAA);\r
1333\r
1334 if (READ_SCR (SerialDevice) != 0xAA) {\r
1335 Status = FALSE;\r
1336 }\r
1337\r
1338 WRITE_SCR (SerialDevice, 0x55);\r
1339\r
1340 if (READ_SCR (SerialDevice) != 0x55) {\r
1341 Status = FALSE;\r
1342 }\r
1436aea4 1343\r
a59e2ede
RN
1344 //\r
1345 // Restore SCR\r
1346 //\r
1347 WRITE_SCR (SerialDevice, Temp);\r
1348 return Status;\r
1349}\r
1350\r
1351/**\r
1352 Read serial port.\r
1353\r
1354 @param SerialDev Pointer to serial device\r
1355 @param Offset Offset in register group\r
1356\r
1357 @return Data read from serial port\r
1358\r
1359**/\r
1360UINT8\r
1361SerialReadRegister (\r
1436aea4
MK
1362 IN SERIAL_DEV *SerialDev,\r
1363 IN UINT32 Offset\r
a59e2ede
RN
1364 )\r
1365{\r
1436aea4
MK
1366 UINT8 Data;\r
1367 EFI_STATUS Status;\r
a59e2ede
RN
1368\r
1369 if (SerialDev->PciDeviceInfo == NULL) {\r
1436aea4 1370 return IoRead8 ((UINTN)SerialDev->BaseAddress + Offset * SerialDev->RegisterStride);\r
a59e2ede
RN
1371 } else {\r
1372 if (SerialDev->MmioAccess) {\r
1436aea4
MK
1373 Status = SerialDev->PciDeviceInfo->PciIo->Mem.Read (\r
1374 SerialDev->PciDeviceInfo->PciIo,\r
1375 EfiPciIoWidthUint8,\r
1376 EFI_PCI_IO_PASS_THROUGH_BAR,\r
1377 SerialDev->BaseAddress + Offset * SerialDev->RegisterStride,\r
1378 1,\r
1379 &Data\r
1380 );\r
a59e2ede 1381 } else {\r
1436aea4
MK
1382 Status = SerialDev->PciDeviceInfo->PciIo->Io.Read (\r
1383 SerialDev->PciDeviceInfo->PciIo,\r
1384 EfiPciIoWidthUint8,\r
1385 EFI_PCI_IO_PASS_THROUGH_BAR,\r
1386 SerialDev->BaseAddress + Offset * SerialDev->RegisterStride,\r
1387 1,\r
1388 &Data\r
1389 );\r
a59e2ede 1390 }\r
1436aea4 1391\r
a59e2ede
RN
1392 ASSERT_EFI_ERROR (Status);\r
1393 return Data;\r
1394 }\r
1395}\r
1396\r
1397/**\r
1398 Write serial port.\r
1399\r
1400 @param SerialDev Pointer to serial device\r
1401 @param Offset Offset in register group\r
1402 @param Data data which is to be written to some serial port register\r
1403**/\r
1404VOID\r
1405SerialWriteRegister (\r
1436aea4
MK
1406 IN SERIAL_DEV *SerialDev,\r
1407 IN UINT32 Offset,\r
1408 IN UINT8 Data\r
a59e2ede
RN
1409 )\r
1410{\r
1436aea4 1411 EFI_STATUS Status;\r
a59e2ede
RN
1412\r
1413 if (SerialDev->PciDeviceInfo == NULL) {\r
1436aea4 1414 IoWrite8 ((UINTN)SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, Data);\r
a59e2ede
RN
1415 } else {\r
1416 if (SerialDev->MmioAccess) {\r
1436aea4
MK
1417 Status = SerialDev->PciDeviceInfo->PciIo->Mem.Write (\r
1418 SerialDev->PciDeviceInfo->PciIo,\r
1419 EfiPciIoWidthUint8,\r
1420 EFI_PCI_IO_PASS_THROUGH_BAR,\r
1421 SerialDev->BaseAddress + Offset * SerialDev->RegisterStride,\r
1422 1,\r
1423 &Data\r
1424 );\r
a59e2ede 1425 } else {\r
1436aea4
MK
1426 Status = SerialDev->PciDeviceInfo->PciIo->Io.Write (\r
1427 SerialDev->PciDeviceInfo->PciIo,\r
1428 EfiPciIoWidthUint8,\r
1429 EFI_PCI_IO_PASS_THROUGH_BAR,\r
1430 SerialDev->BaseAddress + Offset * SerialDev->RegisterStride,\r
1431 1,\r
1432 &Data\r
1433 );\r
a59e2ede 1434 }\r
1436aea4 1435\r
a59e2ede
RN
1436 ASSERT_EFI_ERROR (Status);\r
1437 }\r
1438}\r