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