]> git.proxmox.com Git - mirror_edk2.git/blame - CorebootModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.c
CorebootModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / CorebootModulePkg / Library / BaseSerialPortLib16550 / BaseSerialPortLib16550.c
CommitLineData
12460e22
LL
1/** @file
2 16550 UART Serial Port library functions
3
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
f3342b7a 6 SPDX-License-Identifier: BSD-2-Clause-Patent
12460e22
LL
7
8**/
9
10#include <Base.h>
11#include <IndustryStandard/Pci.h>
12#include <Library/SerialPortLib.h>
13#include <Library/PcdLib.h>
14#include <Library/IoLib.h>
15#include <Library/PciLib.h>
16#include <Library/PlatformHookLib.h>
17#include <Library/BaseLib.h>
18
19//
06516768 20// PCI Definitions.\r
12460e22
LL
21//
22#define PCI_BRIDGE_32_BIT_IO_SPACE 0x01
23
24//
25// 16550 UART register offsets and bitfields
26//
27#define R_UART_RXBUF 0
28#define R_UART_TXBUF 0
29#define R_UART_BAUD_LOW 0
30#define R_UART_BAUD_HIGH 1
31#define R_UART_FCR 2
32#define B_UART_FCR_FIFOE BIT0
33#define B_UART_FCR_FIFO64 BIT5
34#define R_UART_LCR 3
35#define B_UART_LCR_DLAB BIT7
36#define R_UART_MCR 4
37#define B_UART_MCR_DTRC BIT0
38#define B_UART_MCR_RTS BIT1
39#define R_UART_LSR 5
40#define B_UART_LSR_RXRDY BIT0
41#define B_UART_LSR_TXRDY BIT5
42#define B_UART_LSR_TEMT BIT6
43#define R_UART_MSR 6
44#define B_UART_MSR_CTS BIT4
45#define B_UART_MSR_DSR BIT5
46#define B_UART_MSR_RI BIT6
47#define B_UART_MSR_DCD BIT7
48
49//
50// 4-byte structure for each PCI node in PcdSerialPciDeviceInfo
51//
52typedef struct {
53 UINT8 Device;
54 UINT8 Function;
55 UINT16 PowerManagementStatusAndControlRegister;
56} PCI_UART_DEVICE_INFO;
57
58/**
b08993bd 59 Read an 8-bit 16550 register. If PcdSerialUseMmio is TRUE, then the value is read from
12460e22 60 MMIO space. If PcdSerialUseMmio is FALSE, then the value is read from I/O space. The
b08993bd
LL
61 parameter Offset is added to the base address of the 16550 registers that is specified
62 by PcdSerialRegisterBase.
63
12460e22
LL
64 @param Base The base address register of UART device.
65 @param Offset The offset of the 16550 register to read.
66
67 @return The value read from the 16550 register.
68
69**/
70UINT8
71SerialPortReadRegister (
72 UINTN Base,
73 UINTN Offset
74 )
75{
76 if (PcdGetBool (PcdSerialUseMmio)) {
77 return MmioRead8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride));
78 } else {
79 return IoRead8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride));
80 }
81}
82
83/**
84 Write an 8-bit 16550 register. If PcdSerialUseMmio is TRUE, then the value is written to
85 MMIO space. If PcdSerialUseMmio is FALSE, then the value is written to I/O space. The
b08993bd
LL
86 parameter Offset is added to the base address of the 16550 registers that is specified
87 by PcdSerialRegisterBase.
88
12460e22
LL
89 @param Base The base address register of UART device.
90 @param Offset The offset of the 16550 register to write.
91 @param Value The value to write to the 16550 register specified by Offset.
92
93 @return The value written to the 16550 register.
94
95**/
96UINT8
97SerialPortWriteRegister (
98 UINTN Base,
99 UINTN Offset,
100 UINT8 Value
101 )
102{
103 if (PcdGetBool (PcdSerialUseMmio)) {
104 return MmioWrite8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), Value);
105 } else {
106 return IoWrite8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), Value);
107 }
108}
109
110/**
b08993bd
LL
111 Update the value of an 16-bit PCI configuration register in a PCI device. If the
112 PCI Configuration register specified by PciAddress is already programmed with a
113 non-zero value, then return the current value. Otherwise update the PCI configuration
12460e22 114 register specified by PciAddress with the value specified by Value and return the
b08993bd 115 value programmed into the PCI configuration register. All values must be masked
12460e22
LL
116 using the bitmask specified by Mask.
117
118 @param PciAddress PCI Library address of the PCI Configuration register to update.
119 @param Value The value to program into the PCI Configuration Register.
120 @param Mask Bitmask of the bits to check and update in the PCI configuration register.
121
122**/
123UINT16
124SerialPortLibUpdatePciRegister16 (
125 UINTN PciAddress,
126 UINT16 Value,
127 UINT16 Mask
128 )
129{
130 UINT16 CurrentValue;
b08993bd 131
12460e22
LL
132 CurrentValue = PciRead16 (PciAddress) & Mask;
133 if (CurrentValue != 0) {
134 return CurrentValue;
135 }
136 return PciWrite16 (PciAddress, Value & Mask);
137}
138
139/**
b08993bd
LL
140 Update the value of an 32-bit PCI configuration register in a PCI device. If the
141 PCI Configuration register specified by PciAddress is already programmed with a
142 non-zero value, then return the current value. Otherwise update the PCI configuration
12460e22 143 register specified by PciAddress with the value specified by Value and return the
b08993bd 144 value programmed into the PCI configuration register. All values must be masked
12460e22
LL
145 using the bitmask specified by Mask.
146
147 @param PciAddress PCI Library address of the PCI Configuration register to update.
148 @param Value The value to program into the PCI Configuration Register.
149 @param Mask Bitmask of the bits to check and update in the PCI configuration register.
150
151 @return The Secondary bus number that is actually programed into the PCI to PCI Bridge device.
152
153**/
154UINT32
155SerialPortLibUpdatePciRegister32 (
156 UINTN PciAddress,
157 UINT32 Value,
158 UINT32 Mask
159 )
160{
161 UINT32 CurrentValue;
b08993bd 162
12460e22
LL
163 CurrentValue = PciRead32 (PciAddress) & Mask;
164 if (CurrentValue != 0) {
165 return CurrentValue;
166 }
167 return PciWrite32 (PciAddress, Value & Mask);
168}
169
170/**
b08993bd
LL
171 Retrieve the I/O or MMIO base address register for the PCI UART device.
172
173 This function assumes Root Bus Numer is Zero, and enables I/O and MMIO in PCI UART
174 Device if they are not already enabled.
175
12460e22
LL
176 @return The base address register of the UART device.
177
178**/
179UINTN
180GetSerialRegisterBase (
181 VOID
182 )
183{
184 UINTN PciLibAddress;
185 UINTN BusNumber;
186 UINTN SubordinateBusNumber;
187 UINT32 ParentIoBase;
188 UINT32 ParentIoLimit;
189 UINT16 ParentMemoryBase;
190 UINT16 ParentMemoryLimit;
191 UINT32 IoBase;
192 UINT32 IoLimit;
193 UINT16 MemoryBase;
194 UINT16 MemoryLimit;
195 UINTN SerialRegisterBase;
196 UINTN BarIndex;
197 UINT32 RegisterBaseMask;
198 PCI_UART_DEVICE_INFO *DeviceInfo;
199
200 //
201 // Get PCI Device Info
202 //
203 DeviceInfo = (PCI_UART_DEVICE_INFO *) PcdGetPtr (PcdSerialPciDeviceInfo);
b08993bd 204
12460e22
LL
205 //
206 // If PCI Device Info is empty, then assume fixed address UART and return PcdSerialRegisterBase
b08993bd 207 //
12460e22
LL
208 if (DeviceInfo->Device == 0xff) {
209 return (UINTN)PcdGet64 (PcdSerialRegisterBase);
210 }
211
212 //
213 // Assume PCI Bus 0 I/O window is 0-64KB and MMIO windows is 0-4GB
214 //
215 ParentMemoryBase = 0 >> 16;
216 ParentMemoryLimit = 0xfff00000 >> 16;
217 ParentIoBase = 0 >> 12;
218 ParentIoLimit = 0xf000 >> 12;
b08993bd 219
12460e22
LL
220 //
221 // Enable I/O and MMIO in PCI Bridge
b08993bd 222 // Assume Root Bus Numer is Zero.
12460e22
LL
223 //
224 for (BusNumber = 0; (DeviceInfo + 1)->Device != 0xff; DeviceInfo++) {
225 //
226 // Compute PCI Lib Address to PCI to PCI Bridge
227 //
228 PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);
b08993bd 229
12460e22
LL
230 //
231 // Retrieve and verify the bus numbers in the PCI to PCI Bridge
232 //
233 BusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
234 SubordinateBusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET);
235 if (BusNumber == 0 || BusNumber > SubordinateBusNumber) {
236 return 0;
237 }
238
239 //
240 // Retrieve and verify the I/O or MMIO decode window in the PCI to PCI Bridge
241 //
242 if (PcdGetBool (PcdSerialUseMmio)) {
243 MemoryLimit = PciRead16 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.MemoryLimit)) & 0xfff0;
244 MemoryBase = PciRead16 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.MemoryBase)) & 0xfff0;
245
246 //
247 // If PCI Bridge MMIO window is disabled, then return 0
248 //
249 if (MemoryLimit < MemoryBase) {
250 return 0;
251 }
b08993bd 252
12460e22
LL
253 //
254 // If PCI Bridge MMIO window is not in the address range decoded by the parent PCI Bridge, then return 0
b08993bd 255 //
12460e22
LL
256 if (MemoryBase < ParentMemoryBase || MemoryBase > ParentMemoryLimit || MemoryLimit > ParentMemoryLimit) {
257 return 0;
258 }
259 ParentMemoryBase = MemoryBase;
260 ParentMemoryLimit = MemoryLimit;
261 } else {
262 IoLimit = PciRead8 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.IoLimit));
263 if ((IoLimit & PCI_BRIDGE_32_BIT_IO_SPACE ) == 0) {
264 IoLimit = IoLimit >> 4;
265 } else {
266 IoLimit = (PciRead16 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.IoLimitUpper16)) << 4) | (IoLimit >> 4);
267 }
268 IoBase = PciRead8 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.IoBase));
269 if ((IoBase & PCI_BRIDGE_32_BIT_IO_SPACE ) == 0) {
270 IoBase = IoBase >> 4;
271 } else {
272 IoBase = (PciRead16 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.IoBaseUpper16)) << 4) | (IoBase >> 4);
273 }
b08993bd 274
12460e22
LL
275 //
276 // If PCI Bridge I/O window is disabled, then return 0
277 //
278 if (IoLimit < IoBase) {
279 return 0;
280 }
b08993bd 281
12460e22
LL
282 //
283 // If PCI Bridge I/O window is not in the address range decoded by the parent PCI Bridge, then return 0
b08993bd 284 //
12460e22
LL
285 if (IoBase < ParentIoBase || IoBase > ParentIoLimit || IoLimit > ParentIoLimit) {
286 return 0;
287 }
288 ParentIoBase = IoBase;
289 ParentIoLimit = IoLimit;
290 }
291 }
292
293 //
294 // Compute PCI Lib Address to PCI UART
295 //
296 PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);
b08993bd 297
12460e22
LL
298 //
299 // Find the first IO or MMIO BAR
300 //
301 RegisterBaseMask = 0xFFFFFFF0;
302 for (BarIndex = 0; BarIndex < PCI_MAX_BAR; BarIndex ++) {
303 SerialRegisterBase = PciRead32 (PciLibAddress + PCI_BASE_ADDRESSREG_OFFSET + BarIndex * 4);
304 if (PcdGetBool (PcdSerialUseMmio) && ((SerialRegisterBase & BIT0) == 0)) {
305 //
306 // MMIO BAR is found
307 //
308 RegisterBaseMask = 0xFFFFFFF0;
309 break;
310 }
311
312 if ((!PcdGetBool (PcdSerialUseMmio)) && ((SerialRegisterBase & BIT0) != 0)) {
313 //
314 // IO BAR is found
315 //
316 RegisterBaseMask = 0xFFFFFFF8;
317 break;
318 }
319 }
320
321 //
322 // MMIO or IO BAR is not found.
323 //
324 if (BarIndex == PCI_MAX_BAR) {
325 return 0;
326 }
327
328 //
329 // Program UART BAR
b08993bd 330 //
12460e22
LL
331 SerialRegisterBase = SerialPortLibUpdatePciRegister32 (
332 PciLibAddress + PCI_BASE_ADDRESSREG_OFFSET + BarIndex * 4,
b08993bd 333 (UINT32)PcdGet64 (PcdSerialRegisterBase),
12460e22
LL
334 RegisterBaseMask
335 );
336
337 //
338 // Verify that the UART BAR is in the address range decoded by the parent PCI Bridge
b08993bd 339 //
12460e22
LL
340 if (PcdGetBool (PcdSerialUseMmio)) {
341 if (((SerialRegisterBase >> 16) & 0xfff0) < ParentMemoryBase || ((SerialRegisterBase >> 16) & 0xfff0) > ParentMemoryLimit) {
342 return 0;
343 }
344 } else {
345 if ((SerialRegisterBase >> 12) < ParentIoBase || (SerialRegisterBase >> 12) > ParentIoLimit) {
346 return 0;
347 }
348 }
b08993bd 349
12460e22
LL
350 //
351 // Enable I/O and MMIO in PCI UART Device if they are not already enabled
352 //
353 PciOr16 (
354 PciLibAddress + PCI_COMMAND_OFFSET,
355 PcdGetBool (PcdSerialUseMmio) ? EFI_PCI_COMMAND_MEMORY_SPACE : EFI_PCI_COMMAND_IO_SPACE
356 );
357
358 //
359 // Force D0 state if a Power Management and Status Register is specified
360 //
361 if (DeviceInfo->PowerManagementStatusAndControlRegister != 0x00) {
362 if ((PciRead16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister) & (BIT0 | BIT1)) != 0x00) {
363 PciAnd16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister, (UINT16)~(BIT0 | BIT1));
364 //
365 // If PCI UART was not in D0, then make sure FIFOs are enabled, but do not reset FIFOs
366 //
367 SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, (UINT8)(PcdGet8 (PcdSerialFifoControl) & (B_UART_FCR_FIFOE | B_UART_FCR_FIFO64)));
368 }
369 }
b08993bd 370
12460e22
LL
371 //
372 // Get PCI Device Info
373 //
374 DeviceInfo = (PCI_UART_DEVICE_INFO *) PcdGetPtr (PcdSerialPciDeviceInfo);
375
376 //
377 // Enable I/O or MMIO in PCI Bridge
b08993bd 378 // Assume Root Bus Numer is Zero.
12460e22
LL
379 //
380 for (BusNumber = 0; (DeviceInfo + 1)->Device != 0xff; DeviceInfo++) {
381 //
382 // Compute PCI Lib Address to PCI to PCI Bridge
383 //
384 PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);
b08993bd 385
12460e22
LL
386 //
387 // Enable the I/O or MMIO decode windows in the PCI to PCI Bridge
388 //
389 PciOr16 (
b08993bd 390 PciLibAddress + PCI_COMMAND_OFFSET,
12460e22
LL
391 PcdGetBool (PcdSerialUseMmio) ? EFI_PCI_COMMAND_MEMORY_SPACE : EFI_PCI_COMMAND_IO_SPACE
392 );
b08993bd 393
12460e22
LL
394 //
395 // Force D0 state if a Power Management and Status Register is specified
396 //
397 if (DeviceInfo->PowerManagementStatusAndControlRegister != 0x00) {
398 if ((PciRead16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister) & (BIT0 | BIT1)) != 0x00) {
399 PciAnd16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister, (UINT16)~(BIT0 | BIT1));
400 }
401 }
b08993bd 402
12460e22
LL
403 BusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
404 }
b08993bd 405
12460e22
LL
406 return SerialRegisterBase;
407}
408
409/**
410 Return whether the hardware flow control signal allows writing.
411
412 @param SerialRegisterBase The base address register of UART device.
413
414 @retval TRUE The serial port is writable.
415 @retval FALSE The serial port is not writable.
416**/
417BOOLEAN
418SerialPortWritable (
419 UINTN SerialRegisterBase
420 )
421{
422 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
423 if (PcdGetBool (PcdSerialDetectCable)) {
424 //
425 // Wait for both DSR and CTS to be set
426 // DSR is set if a cable is connected.
427 // CTS is set if it is ok to transmit data
428 //
429 // DSR CTS Description Action
430 // === === ======================================== ========
431 // 0 0 No cable connected. Wait
432 // 0 1 No cable connected. Wait
433 // 1 0 Cable connected, but not clear to send. Wait
434 // 1 1 Cable connected, and clear to send. Transmit
435 //
436 return (BOOLEAN) ((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) == (B_UART_MSR_DSR | B_UART_MSR_CTS));
437 } else {
438 //
b08993bd 439 // Wait for both DSR and CTS to be set OR for DSR to be clear.
12460e22
LL
440 // DSR is set if a cable is connected.
441 // CTS is set if it is ok to transmit data
442 //
443 // DSR CTS Description Action
444 // === === ======================================== ========
445 // 0 0 No cable connected. Transmit
446 // 0 1 No cable connected. Transmit
447 // 1 0 Cable connected, but not clear to send. Wait
06516768 448 // 1 1 Cable connected, and clear to send. Transmit\r
12460e22
LL
449 //
450 return (BOOLEAN) ((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) != (B_UART_MSR_DSR));
451 }
452 }
453
454 return TRUE;
455}
456
457/**
458 Initialize the serial device hardware.
b08993bd 459
12460e22
LL
460 If no initialization is required, then return RETURN_SUCCESS.
461 If the serial device was successfully initialized, then return RETURN_SUCCESS.
462 If the serial device could not be initialized, then return RETURN_DEVICE_ERROR.
b08993bd 463
12460e22
LL
464 @retval RETURN_SUCCESS The serial device was initialized.
465 @retval RETURN_DEVICE_ERROR The serial device could not be initialized.
466
467**/
468RETURN_STATUS
469EFIAPI
470SerialPortInitialize (
471 VOID
472 )
473{
474 RETURN_STATUS Status;
475 UINTN SerialRegisterBase;
476 UINT32 Divisor;
b08993bd 477 UINT32 CurrentDivisor;
12460e22
LL
478 BOOLEAN Initialized;
479
480 //
481 // Perform platform specific initialization required to enable use of the 16550 device
482 // at the location specified by PcdSerialUseMmio and PcdSerialRegisterBase.
483 //
484 Status = PlatformHookSerialPortInitialize ();
485 if (RETURN_ERROR (Status)) {
486 return Status;
487 }
488
489 //
490 // Calculate divisor for baud generator
491 // Ref_Clk_Rate / Baud_Rate / 16
492 //
493 Divisor = PcdGet32 (PcdSerialClockRate) / (PcdGet32 (PcdSerialBaudRate) * 16);
494 if ((PcdGet32 (PcdSerialClockRate) % (PcdGet32 (PcdSerialBaudRate) * 16)) >= PcdGet32 (PcdSerialBaudRate) * 8) {
495 Divisor++;
496 }
497
498 //
499 // Get the base address of the serial port in either I/O or MMIO space
500 //
501 SerialRegisterBase = GetSerialRegisterBase ();
502 if (SerialRegisterBase ==0) {
503 return RETURN_DEVICE_ERROR;
504 }
505
506 //
507 // See if the serial port is already initialized
508 //
509 Initialized = TRUE;
510 if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & 0x3F) != (PcdGet8 (PcdSerialLineControl) & 0x3F)) {
511 Initialized = FALSE;
512 }
513 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) | B_UART_LCR_DLAB));
514 CurrentDivisor = SerialPortReadRegister (SerialRegisterBase, R_UART_BAUD_HIGH) << 8;
515 CurrentDivisor |= (UINT32) SerialPortReadRegister (SerialRegisterBase, R_UART_BAUD_LOW);
516 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & ~B_UART_LCR_DLAB));
517 if (CurrentDivisor != Divisor) {
518 Initialized = FALSE;
519 }
520 if (Initialized) {
521 return RETURN_SUCCESS;
522 }
523
524 //
525 // Wait for the serial port to be ready.
526 // Verify that both the transmit FIFO and the shift register are empty.
527 //
528 while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY));
b08993bd 529
12460e22
LL
530 //
531 // Configure baud rate
532 //
533 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB);
534 SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_HIGH, (UINT8) (Divisor >> 8));
535 SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8) (Divisor & 0xff));
536
537 //
538 // Clear DLAB and configure Data Bits, Parity, and Stop Bits.
539 // Strip reserved bits from PcdSerialLineControl
540 //
541 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(PcdGet8 (PcdSerialLineControl) & 0x3F));
542
543 //
544 // Enable and reset FIFOs
545 // Strip reserved bits from PcdSerialFifoControl
546 //
547 SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, 0x00);
548 SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, (UINT8)(PcdGet8 (PcdSerialFifoControl) & (B_UART_FCR_FIFOE | B_UART_FCR_FIFO64)));
549
550 //
bb083167 551 // Set RTS and DTR in Modem Control Register(MCR)
b08993bd 552 //
bb083167
LL
553 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR,
554 EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY);
12460e22
LL
555
556 return RETURN_SUCCESS;
557}
558
559/**
b08993bd 560 Write data from buffer to serial device.
12460e22 561
b08993bd 562 Writes NumberOfBytes data bytes from Buffer to the serial device.
12460e22
LL
563 The number of bytes actually written to the serial device is returned.
564 If the return value is less than NumberOfBytes, then the write operation failed.
565
b08993bd 566 If Buffer is NULL, then ASSERT().
12460e22
LL
567
568 If NumberOfBytes is zero, then return 0.
569
570 @param Buffer Pointer to the data buffer to be written.
571 @param NumberOfBytes Number of bytes to written to the serial device.
572
573 @retval 0 NumberOfBytes is 0.
b08993bd 574 @retval >0 The number of bytes written to the serial device.
12460e22
LL
575 If this value is less than NumberOfBytes, then the write operation failed.
576
577**/
578UINTN
579EFIAPI
580SerialPortWrite (
581 IN UINT8 *Buffer,
582 IN UINTN NumberOfBytes
583 )
584{
585 UINTN SerialRegisterBase;
586 UINTN Result;
587 UINTN Index;
588 UINTN FifoSize;
589
590 if (Buffer == NULL) {
591 return 0;
592 }
593
594 SerialRegisterBase = GetSerialRegisterBase ();
595 if (SerialRegisterBase ==0) {
596 return 0;
597 }
b08993bd 598
12460e22
LL
599 if (NumberOfBytes == 0) {
600 //
601 // Flush the hardware
602 //
603
604 //
605 // Wait for both the transmit FIFO and shift register empty.
606 //
607 while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY));
608
609 //
610 // Wait for the hardware flow control signal
611 //
612 while (!SerialPortWritable (SerialRegisterBase));
613 return 0;
614 }
615
616 //
617 // Compute the maximum size of the Tx FIFO
618 //
619 FifoSize = 1;
620 if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFOE) != 0) {
621 if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFO64) == 0) {
622 FifoSize = 16;
623 } else {
624 FifoSize = PcdGet32 (PcdSerialExtendedTxFifoSize);
625 }
626 }
627
628 Result = NumberOfBytes;
629 while (NumberOfBytes != 0) {
630 //
631 // Wait for the serial port to be ready, to make sure both the transmit FIFO
632 // and shift register empty.
633 //
634 while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_TEMT) == 0);
635
636 //
637 // Fill then entire Tx FIFO
638 //
639 for (Index = 0; Index < FifoSize && NumberOfBytes != 0; Index++, NumberOfBytes--, Buffer++) {
640 //
641 // Wait for the hardware flow control signal
642 //
643 while (!SerialPortWritable (SerialRegisterBase));
644
645 //
646 // Write byte to the transmit buffer.
647 //
648 SerialPortWriteRegister (SerialRegisterBase, R_UART_TXBUF, *Buffer);
649 }
650 }
651 return Result;
652}
653
654/**
655 Reads data from a serial device into a buffer.
656
657 @param Buffer Pointer to the data buffer to store the data read from the serial device.
658 @param NumberOfBytes Number of bytes to read from the serial device.
659
660 @retval 0 NumberOfBytes is 0.
b08993bd 661 @retval >0 The number of bytes read from the serial device.
12460e22
LL
662 If this value is less than NumberOfBytes, then the read operation failed.
663
664**/
665UINTN
666EFIAPI
667SerialPortRead (
668 OUT UINT8 *Buffer,
669 IN UINTN NumberOfBytes
670 )
671{
672 UINTN SerialRegisterBase;
673 UINTN Result;
674 UINT8 Mcr;
675
676 if (NULL == Buffer) {
677 return 0;
678 }
679
680 SerialRegisterBase = GetSerialRegisterBase ();
681 if (SerialRegisterBase ==0) {
682 return 0;
683 }
684
685 Mcr = (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) & ~B_UART_MCR_RTS);
b08993bd 686
12460e22
LL
687 for (Result = 0; NumberOfBytes-- != 0; Result++, Buffer++) {
688 //
689 // Wait for the serial port to have some data.
690 //
691 while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_RXRDY) == 0) {
692 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
693 //
694 // Set RTS to let the peer send some data
695 //
696 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(Mcr | B_UART_MCR_RTS));
697 }
698 }
699 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
700 //
701 // Clear RTS to prevent peer from sending data
702 //
703 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, Mcr);
704 }
b08993bd 705
12460e22
LL
706 //
707 // Read byte from the receive buffer.
708 //
709 *Buffer = SerialPortReadRegister (SerialRegisterBase, R_UART_RXBUF);
710 }
b08993bd 711
12460e22
LL
712 return Result;
713}
714
715
716/**
717 Polls a serial device to see if there is any data waiting to be read.
718
06516768 719 Polls a serial device to see if there is any data waiting to be read.\r
12460e22
LL
720 If there is data waiting to be read from the serial device, then TRUE is returned.
721 If there is no data waiting to be read from the serial device, then FALSE is returned.
722
723 @retval TRUE Data is waiting to be read from the serial device.
724 @retval FALSE There is no data waiting to be read from the serial device.
725
726**/
727BOOLEAN
728EFIAPI
729SerialPortPoll (
730 VOID
731 )
732{
733 UINTN SerialRegisterBase;
b08993bd 734
12460e22
LL
735 SerialRegisterBase = GetSerialRegisterBase ();
736 if (SerialRegisterBase ==0) {
737 return FALSE;
738 }
739
740 //
741 // Read the serial port status
742 //
743 if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_RXRDY) != 0) {
744 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
745 //
746 // Clear RTS to prevent peer from sending data
747 //
748 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) & ~B_UART_MCR_RTS));
749 }
750 return TRUE;
b08993bd
LL
751 }
752
12460e22
LL
753 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
754 //
755 // Set RTS to let the peer send some data
756 //
757 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) | B_UART_MCR_RTS));
758 }
b08993bd 759
12460e22
LL
760 return FALSE;
761}
762
763/**
764 Sets the control bits on a serial device.
765
766 @param Control Sets the bits of Control that are settable.
767
768 @retval RETURN_SUCCESS The new control bits were set on the serial device.
769 @retval RETURN_UNSUPPORTED The serial device does not support this operation.
770 @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
771
772**/
773RETURN_STATUS
774EFIAPI
775SerialPortSetControl (
776 IN UINT32 Control
777 )
778{
779 UINTN SerialRegisterBase;
780 UINT8 Mcr;
781
782 //
783 // First determine the parameter is invalid.
784 //
785 if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |
786 EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0) {
787 return RETURN_UNSUPPORTED;
788 }
789
790 SerialRegisterBase = GetSerialRegisterBase ();
791 if (SerialRegisterBase ==0) {
792 return RETURN_UNSUPPORTED;
793 }
794
795 //
796 // Read the Modem Control Register.
797 //
798 Mcr = SerialPortReadRegister (SerialRegisterBase, R_UART_MCR);
799 Mcr &= (~(B_UART_MCR_DTRC | B_UART_MCR_RTS));
800
801 if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
802 Mcr |= B_UART_MCR_DTRC;
803 }
804
805 if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
806 Mcr |= B_UART_MCR_RTS;
807 }
808
809 //
810 // Write the Modem Control Register.
811 //
812 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, Mcr);
813
814 return RETURN_SUCCESS;
815}
816
817/**
818 Retrieve the status of the control bits on a serial device.
819
820 @param Control A pointer to return the current control signals from the serial device.
821
822 @retval RETURN_SUCCESS The control bits were read from the serial device.
823 @retval RETURN_UNSUPPORTED The serial device does not support this operation.
824 @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
825
826**/
827RETURN_STATUS
828EFIAPI
829SerialPortGetControl (
830 OUT UINT32 *Control
831 )
832{
833 UINTN SerialRegisterBase;
834 UINT8 Msr;
835 UINT8 Mcr;
836 UINT8 Lsr;
837
838 SerialRegisterBase = GetSerialRegisterBase ();
839 if (SerialRegisterBase ==0) {
840 return RETURN_UNSUPPORTED;
841 }
842
843 *Control = 0;
844
845 //
846 // Read the Modem Status Register.
847 //
848 Msr = SerialPortReadRegister (SerialRegisterBase, R_UART_MSR);
849
850 if ((Msr & B_UART_MSR_CTS) == B_UART_MSR_CTS) {
851 *Control |= EFI_SERIAL_CLEAR_TO_SEND;
852 }
853
854 if ((Msr & B_UART_MSR_DSR) == B_UART_MSR_DSR) {
855 *Control |= EFI_SERIAL_DATA_SET_READY;
856 }
857
858 if ((Msr & B_UART_MSR_RI) == B_UART_MSR_RI) {
859 *Control |= EFI_SERIAL_RING_INDICATE;
860 }
861
862 if ((Msr & B_UART_MSR_DCD) == B_UART_MSR_DCD) {
863 *Control |= EFI_SERIAL_CARRIER_DETECT;
864 }
865
866 //
867 // Read the Modem Control Register.
868 //
869 Mcr = SerialPortReadRegister (SerialRegisterBase, R_UART_MCR);
870
871 if ((Mcr & B_UART_MCR_DTRC) == B_UART_MCR_DTRC) {
872 *Control |= EFI_SERIAL_DATA_TERMINAL_READY;
873 }
874
875 if ((Mcr & B_UART_MCR_RTS) == B_UART_MCR_RTS) {
876 *Control |= EFI_SERIAL_REQUEST_TO_SEND;
877 }
878
879 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
880 *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
881 }
882
883 //
884 // Read the Line Status Register.
885 //
886 Lsr = SerialPortReadRegister (SerialRegisterBase, R_UART_LSR);
887
888 if ((Lsr & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) == (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) {
889 *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
890 }
891
892 if ((Lsr & B_UART_LSR_RXRDY) == 0) {
893 *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
894 }
895
896 return RETURN_SUCCESS;
897}
898
899/**
06516768 900 Sets the baud rate, receive FIFO depth, transmit/receive time out, parity,\r
12460e22
LL
901 data bits, and stop bits on a serial device.
902
903 @param BaudRate The requested baud rate. A BaudRate value of 0 will use the
904 device's default interface speed.
905 On output, the value actually set.
06516768 906 @param ReceiveFifoDepth The requested depth of the FIFO on the receive side of the\r
12460e22
LL
907 serial interface. A ReceiveFifoDepth value of 0 will use
908 the device's default FIFO depth.
909 On output, the value actually set.
910 @param Timeout The requested time out for a single character in microseconds.
911 This timeout applies to both the transmit and receive side of the
912 interface. A Timeout value of 0 will use the device's default time
913 out value.
914 On output, the value actually set.
915 @param Parity The type of parity to use on this serial device. A Parity value of
916 DefaultParity will use the device's default parity value.
917 On output, the value actually set.
918 @param DataBits The number of data bits to use on the serial device. A DataBits
06516768 919 value of 0 will use the device's default data bit setting.\r
12460e22
LL
920 On output, the value actually set.
921 @param StopBits The number of stop bits to use on this serial device. A StopBits
922 value of DefaultStopBits will use the device's default number of
923 stop bits.
924 On output, the value actually set.
925
926 @retval RETURN_SUCCESS The new attributes were set on the serial device.
927 @retval RETURN_UNSUPPORTED The serial device does not support this operation.
928 @retval RETURN_INVALID_PARAMETER One or more of the attributes has an unsupported value.
929 @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
930
931**/
932RETURN_STATUS
933EFIAPI
934SerialPortSetAttributes (
935 IN OUT UINT64 *BaudRate,
936 IN OUT UINT32 *ReceiveFifoDepth,
937 IN OUT UINT32 *Timeout,
938 IN OUT EFI_PARITY_TYPE *Parity,
939 IN OUT UINT8 *DataBits,
940 IN OUT EFI_STOP_BITS_TYPE *StopBits
941 )
942{
943 UINTN SerialRegisterBase;
944 UINT32 SerialBaudRate;
945 UINTN Divisor;
946 UINT8 Lcr;
947 UINT8 LcrData;
948 UINT8 LcrParity;
949 UINT8 LcrStop;
950
951 SerialRegisterBase = GetSerialRegisterBase ();
952 if (SerialRegisterBase ==0) {
953 return RETURN_UNSUPPORTED;
954 }
955
956 //
957 // Check for default settings and fill in actual values.
958 //
959 if (*BaudRate == 0) {
960 *BaudRate = PcdGet32 (PcdSerialBaudRate);
961 }
962 SerialBaudRate = (UINT32) *BaudRate;
963
964 if (*DataBits == 0) {
965 LcrData = (UINT8) (PcdGet8 (PcdSerialLineControl) & 0x3);
966 *DataBits = LcrData + 5;
967 } else {
968 if ((*DataBits < 5) || (*DataBits > 8)) {
969 return RETURN_INVALID_PARAMETER;
970 }
971 //
972 // Map 5..8 to 0..3
973 //
974 LcrData = (UINT8) (*DataBits - (UINT8) 5);
975 }
976
977 if (*Parity == DefaultParity) {
978 LcrParity = (UINT8) ((PcdGet8 (PcdSerialLineControl) >> 3) & 0x7);
979 switch (LcrParity) {
980 case 0:
981 *Parity = NoParity;
982 break;
983
984 case 3:
985 *Parity = EvenParity;
986 break;
987
988 case 1:
989 *Parity = OddParity;
990 break;
991
992 case 7:
993 *Parity = SpaceParity;
994 break;
995
996 case 5:
997 *Parity = MarkParity;
998 break;
999
1000 default:
1001 break;
1002 }
1003 } else {
1004 switch (*Parity) {
1005 case NoParity:
1006 LcrParity = 0;
1007 break;
1008
1009 case EvenParity:
1010 LcrParity = 3;
1011 break;
1012
1013 case OddParity:
1014 LcrParity = 1;
1015 break;
1016
1017 case SpaceParity:
1018 LcrParity = 7;
1019 break;
1020
1021 case MarkParity:
1022 LcrParity = 5;
1023 break;
1024
1025 default:
1026 return RETURN_INVALID_PARAMETER;
1027 }
1028 }
1029
1030 if (*StopBits == DefaultStopBits) {
1031 LcrStop = (UINT8) ((PcdGet8 (PcdSerialLineControl) >> 2) & 0x1);
1032 switch (LcrStop) {
1033 case 0:
1034 *StopBits = OneStopBit;
1035 break;
1036
1037 case 1:
1038 if (*DataBits == 5) {
1039 *StopBits = OneFiveStopBits;
1040 } else {
1041 *StopBits = TwoStopBits;
1042 }
1043 break;
1044
1045 default:
1046 break;
1047 }
1048 } else {
1049 switch (*StopBits) {
1050 case OneStopBit:
1051 LcrStop = 0;
1052 break;
1053
1054 case OneFiveStopBits:
1055 case TwoStopBits:
1056 LcrStop = 1;
1057 break;
1058
1059 default:
1060 return RETURN_INVALID_PARAMETER;
1061 }
1062 }
1063
1064 //
1065 // Calculate divisor for baud generator
1066 // Ref_Clk_Rate / Baud_Rate / 16
1067 //
1068 Divisor = PcdGet32 (PcdSerialClockRate) / (SerialBaudRate * 16);
1069 if ((PcdGet32 (PcdSerialClockRate) % (SerialBaudRate * 16)) >= SerialBaudRate * 8) {
1070 Divisor++;
1071 }
1072
1073 //
1074 // Configure baud rate
1075 //
1076 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB);
1077 SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_HIGH, (UINT8) (Divisor >> 8));
1078 SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8) (Divisor & 0xff));
1079
1080 //
1081 // Clear DLAB and configure Data Bits, Parity, and Stop Bits.
1082 // Strip reserved bits from line control value
1083 //
1084 Lcr = (UINT8) ((LcrParity << 3) | (LcrStop << 2) | LcrData);
1085 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8) (Lcr & 0x3F));
1086
1087 return RETURN_SUCCESS;
1088}
1089