]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.c
MdeModulePkg: BaseSerialPortLib16550 library to support PCI UART device.
[mirror_edk2.git] / MdeModulePkg / Library / BaseSerialPortLib16550 / BaseSerialPortLib16550.c
CommitLineData
467d15ae 1/** @file\r
2 16550 UART Serial Port library functions\r
3\r
6e1e5405 4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>\r
467d15ae 5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include <Base.h>\r
31122d8c 16#include <IndustryStandard/Pci.h>\r
467d15ae 17#include <Library/SerialPortLib.h>\r
18#include <Library/PcdLib.h>\r
19#include <Library/IoLib.h>\r
31122d8c 20#include <Library/PciLib.h>\r
467d15ae 21#include <Library/PlatformHookLib.h>\r
31122d8c
LG
22#include <Library/BaseLib.h>\r
23\r
24//\r
25// PCI Defintions.\r
26//\r
27#define PCI_BRIDGE_32_BIT_IO_SPACE 0x01\r
467d15ae 28\r
29//\r
30// 16550 UART register offsets and bitfields\r
31//\r
32#define R_UART_RXBUF 0\r
33#define R_UART_TXBUF 0\r
34#define R_UART_BAUD_LOW 0\r
35#define R_UART_BAUD_HIGH 1\r
36#define R_UART_FCR 2\r
37#define B_UART_FCR_FIFOE BIT0\r
38#define B_UART_FCR_FIFO64 BIT5\r
39#define R_UART_LCR 3\r
40#define B_UART_LCR_DLAB BIT7\r
41#define R_UART_MCR 4\r
42#define B_UART_MCR_RTS BIT1\r
43#define R_UART_LSR 5\r
44#define B_UART_LSR_RXRDY BIT0\r
45#define B_UART_LSR_TXRDY BIT5\r
46#define B_UART_LSR_TEMT BIT6\r
47#define R_UART_MSR 6\r
48#define B_UART_MSR_CTS BIT4\r
784ce127 49#define B_UART_MSR_DSR BIT5\r
467d15ae 50\r
31122d8c
LG
51//\r
52// 4-byte structure for each PCI node in PcdSerialPciDeviceInfo\r
53//\r
54typedef struct {\r
55 UINT8 Device;\r
56 UINT8 Function;\r
57 UINT16 PowerManagementStatusAndControlRegister;\r
58} PCI_UART_DEVICE_INFO;\r
59\r
467d15ae 60/**\r
61 Read an 8-bit 16550 register. If PcdSerialUseMmio is TRUE, then the value is read from \r
62 MMIO space. If PcdSerialUseMmio is FALSE, then the value is read from I/O space. The\r
63 parameter Offset is added to the base address of the 16550 registers that is specified \r
64 by PcdSerialRegisterBase. \r
65 \r
66 @param Offset The offset of the 16550 register to read.\r
67\r
68 @return The value read from the 16550 register.\r
69\r
70**/\r
71UINT8\r
72SerialPortReadRegister (\r
31122d8c 73 UINTN Base,\r
467d15ae 74 UINTN Offset\r
75 )\r
76{\r
77 if (PcdGetBool (PcdSerialUseMmio)) {\r
31122d8c 78 return MmioRead8 (Base + Offset);\r
467d15ae 79 } else {\r
31122d8c 80 return IoRead8 (Base + Offset);\r
467d15ae 81 }\r
82}\r
83\r
84/**\r
85 Write an 8-bit 16550 register. If PcdSerialUseMmio is TRUE, then the value is written to\r
86 MMIO space. If PcdSerialUseMmio is FALSE, then the value is written to I/O space. The\r
87 parameter Offset is added to the base address of the 16550 registers that is specified \r
88 by PcdSerialRegisterBase. \r
89 \r
e25fb2c0 90 @param Offset The offset of the 16550 register to write.\r
91 @param Value The value to write to the 16550 register specified by Offset.\r
467d15ae 92\r
93 @return The value written to the 16550 register.\r
94\r
95**/\r
96UINT8\r
97SerialPortWriteRegister (\r
31122d8c 98 UINTN Base,\r
467d15ae 99 UINTN Offset,\r
100 UINT8 Value\r
101 )\r
102{\r
103 if (PcdGetBool (PcdSerialUseMmio)) {\r
31122d8c 104 return MmioWrite8 (Base + Offset, Value);\r
467d15ae 105 } else {\r
31122d8c 106 return IoWrite8 (Base + Offset, Value);\r
467d15ae 107 }\r
108}\r
109\r
31122d8c
LG
110/**\r
111 Update the value of an 16-bit PCI configuration register in a PCI device. If the \r
112 PCI Configuration register specified by PciAddress is already programmed with a \r
113 non-zero value, then return the current value. Otherwise update the PCI configuration \r
114 register specified by PciAddress with the value specified by Value and return the\r
115 value programmed into the PCI configuration register. All values must be masked \r
116 using the bitmask specified by Mask.\r
117\r
118 @param PciAddress PCI Library address of the PCI Configuration register to update.\r
119 @param Value The value to program into the PCI Configuration Register.\r
120 @param Mask Bitmask of the bits to check and update in the PCI configuration register.\r
121\r
122**/\r
123UINT16\r
124SerialPortLibUpdatePciRegister16 (\r
125 UINTN PciAddress,\r
126 UINT16 Value,\r
127 UINT16 Mask\r
128 )\r
129{\r
130 UINT16 CurrentValue;\r
131 \r
132 CurrentValue = PciRead16 (PciAddress) & Mask;\r
133 if (CurrentValue != 0) {\r
134 return CurrentValue;\r
135 }\r
136 return PciWrite16 (PciAddress, Value & Mask);\r
137}\r
138\r
139/**\r
140 Update the value of an 32-bit PCI configuration register in a PCI device. If the \r
141 PCI Configuration register specified by PciAddress is already programmed with a \r
142 non-zero value, then return the current value. Otherwise update the PCI configuration \r
143 register specified by PciAddress with the value specified by Value and return the\r
144 value programmed into the PCI configuration register. All values must be masked \r
145 using the bitmask specified by Mask.\r
146\r
147 @param PciAddress PCI Library address of the PCI Configuration register to update.\r
148 @param Value The value to program into the PCI Configuration Register.\r
149 @param Mask Bitmask of the bits to check and update in the PCI configuration register.\r
150\r
151 @return The Secondary bus number that is actually programed into the PCI to PCI Bridge device.\r
152\r
153**/\r
154UINT32\r
155SerialPortLibUpdatePciRegister32 (\r
156 UINTN PciAddress,\r
157 UINT32 Value,\r
158 UINT32 Mask\r
159 )\r
160{\r
161 UINT32 CurrentValue;\r
162 \r
163 CurrentValue = PciRead32 (PciAddress) & Mask;\r
164 if (CurrentValue != 0) {\r
165 return CurrentValue;\r
166 }\r
167 return PciWrite32 (PciAddress, Value & Mask);\r
168}\r
169\r
170/**\r
171 Retrieve the I/O or MMIO base address register for the PCI UART device. \r
172 \r
173 This function assumes Root Bus Numer is Zero, and enables I/O and MMIO in PCI UART \r
174 Device if they are not already enabled. \r
175 \r
176 @return The base address register of the PCI UART device.\r
177\r
178**/\r
179UINTN\r
180GetSerialRegisterBase (\r
181 VOID\r
182 )\r
183{\r
184 UINTN PciLibAddress;\r
185 UINTN PrimaryBusNumber;\r
186 UINTN BusNumber;\r
187 UINTN SubordinateBusNumber;\r
188 UINT32 ParentIoBase;\r
189 UINT32 ParentIoLimit;\r
190 UINT16 ParentMemoryBase;\r
191 UINT16 ParentMemoryLimit;\r
192 UINT32 IoBase;\r
193 UINT32 IoLimit;\r
194 UINT16 MemoryBase;\r
195 UINT16 MemoryLimit;\r
196 UINTN SerialRegisterBase;\r
197 UINTN BarIndex;\r
198 UINT32 RegisterBaseMask;\r
199 PCI_UART_DEVICE_INFO *DeviceInfo;\r
200\r
201 //\r
202 // Get PCI Device Info\r
203 //\r
204 DeviceInfo = (PCI_UART_DEVICE_INFO *) PcdGetPtr (PcdSerialPciDeviceInfo);\r
205 \r
206 //\r
207 // If PCI Device Info is empty, then assume fixed address UART and return PcdSerialRegisterBase\r
208 // \r
209 if (DeviceInfo->Device == 0xff) {\r
210 return (UINTN)PcdGet64 (PcdSerialRegisterBase);\r
211 }\r
212\r
213 //\r
214 // Assume PCI Bus 0 I/O window is 0-64KB and MMIO windows is 0-4GB\r
215 //\r
216 ParentMemoryBase = 0 >> 16;\r
217 ParentMemoryLimit = 0xfff00000 >> 16;\r
218 ParentIoBase = 0 >> 12;\r
219 ParentIoLimit = 0xf000 >> 12;\r
220 \r
221 //\r
222 // Enable I/O and MMIO in PCI Bridge\r
223 // Assume Root Bus Numer is Zero. \r
224 //\r
225 for (BusNumber = 0; (DeviceInfo + 1)->Device != 0xff; DeviceInfo++) {\r
226 //\r
227 // Compute PCI Lib Address to PCI to PCI Bridge\r
228 //\r
229 PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);\r
230 \r
231 //\r
232 // Retrieve and verify the bus numbers in the PCI to PCI Bridge\r
233 //\r
234 PrimaryBusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET);\r
235 BusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);\r
236 SubordinateBusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET);\r
237 if (BusNumber == 0 || BusNumber > SubordinateBusNumber) {\r
238 return 0;\r
239 }\r
240\r
241 //\r
242 // Retrieve and verify the I/O or MMIO decode window in the PCI to PCI Bridge\r
243 //\r
244 if (PcdGetBool (PcdSerialUseMmio)) {\r
245 MemoryLimit = PciRead16 (PciLibAddress + OFFSET_OF (PCI_BRIDGE_CONTROL_REGISTER, MemoryLimit)) & 0xfff0;\r
246 MemoryBase = PciRead16 (PciLibAddress + OFFSET_OF (PCI_BRIDGE_CONTROL_REGISTER, MemoryBase)) & 0xfff0;\r
247\r
248 //\r
249 // If PCI Bridge MMIO window is disabled, then return 0\r
250 //\r
251 if (MemoryLimit < MemoryBase) {\r
252 return 0;\r
253 }\r
254 \r
255 //\r
256 // If PCI Bridge MMIO window is not in the address range decoded by the parent PCI Bridge, then return 0\r
257 // \r
258 if (MemoryBase < ParentMemoryBase || MemoryBase > ParentMemoryLimit || MemoryLimit > ParentMemoryLimit) {\r
259 return 0;\r
260 }\r
261 ParentMemoryBase = MemoryBase;\r
262 ParentMemoryLimit = MemoryLimit;\r
263 } else {\r
264 IoLimit = PciRead8 (PciLibAddress + OFFSET_OF (PCI_BRIDGE_CONTROL_REGISTER, IoLimit));\r
265 if ((IoLimit & PCI_BRIDGE_32_BIT_IO_SPACE ) == 0) {\r
266 IoLimit = IoLimit >> 4;\r
267 } else {\r
268 IoLimit = (PciRead16 (PciLibAddress + OFFSET_OF (PCI_BRIDGE_CONTROL_REGISTER, IoLimitUpper16)) << 4) | (IoLimit >> 4);\r
269 }\r
270 IoBase = PciRead8 (PciLibAddress + OFFSET_OF (PCI_BRIDGE_CONTROL_REGISTER, IoBase));\r
271 if ((IoBase & PCI_BRIDGE_32_BIT_IO_SPACE ) == 0) {\r
272 IoBase = IoBase >> 4;\r
273 } else {\r
274 IoBase = (PciRead16 (PciLibAddress + OFFSET_OF (PCI_BRIDGE_CONTROL_REGISTER, IoBaseUpper16)) << 4) | (IoBase >> 4);\r
275 }\r
276 \r
277 //\r
278 // If PCI Bridge I/O window is disabled, then return 0\r
279 //\r
280 if (IoLimit < IoBase) {\r
281 return 0;\r
282 }\r
283 \r
284 //\r
285 // If PCI Bridge I/O window is not in the address range decoded by the parent PCI Bridge, then return 0\r
286 // \r
287 if (IoBase < ParentIoBase || IoBase > ParentIoLimit || IoLimit > ParentIoLimit) {\r
288 return 0;\r
289 }\r
290 ParentIoBase = IoBase;\r
291 ParentIoLimit = IoLimit;\r
292 }\r
293 }\r
294\r
295 //\r
296 // Compute PCI Lib Address to PCI UART\r
297 //\r
298 PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);\r
299 \r
300 //\r
301 // Find the first IO or MMIO BAR\r
302 //\r
303 RegisterBaseMask = 0xFFFFFFF0;\r
304 for (BarIndex = 0; BarIndex < PCI_MAX_BAR; BarIndex ++) {\r
305 SerialRegisterBase = PciRead32 (PciLibAddress + PCI_BASE_ADDRESSREG_OFFSET + BarIndex * 4);\r
306 if (PcdGetBool (PcdSerialUseMmio) && ((SerialRegisterBase & BIT0) == 0)) {\r
307 //\r
308 // MMIO BAR is found\r
309 //\r
310 RegisterBaseMask = 0xFFFFFFF0;\r
311 break;\r
312 }\r
313\r
314 if ((!PcdGetBool (PcdSerialUseMmio)) && ((SerialRegisterBase & BIT0) != 0)) {\r
315 //\r
316 // IO BAR is found\r
317 //\r
318 RegisterBaseMask = 0xFFFFFFF8;\r
319 break;\r
320 }\r
321 }\r
322\r
323 //\r
324 // MMIO or IO BAR is not found.\r
325 //\r
326 if (BarIndex == PCI_MAX_BAR) {\r
327 return 0;\r
328 }\r
329\r
330 //\r
331 // Program UART BAR\r
332 // \r
333 SerialRegisterBase = SerialPortLibUpdatePciRegister32 (\r
334 PciLibAddress + PCI_BASE_ADDRESSREG_OFFSET + BarIndex * 4,\r
335 (UINT32)PcdGet64 (PcdSerialRegisterBase), \r
336 RegisterBaseMask\r
337 );\r
338\r
339 //\r
340 // Verify that the UART BAR is in the address range decoded by the parent PCI Bridge\r
341 // \r
342 if (PcdGetBool (PcdSerialUseMmio)) {\r
343 if (((SerialRegisterBase >> 16) & 0xfff0) < ParentMemoryBase || ((SerialRegisterBase >> 16) & 0xfff0) > ParentMemoryLimit) {\r
344 return 0;\r
345 }\r
346 } else {\r
347 if ((SerialRegisterBase >> 12) < ParentIoBase || (SerialRegisterBase >> 12) > ParentIoLimit) {\r
348 return 0;\r
349 }\r
350 }\r
351 \r
352 //\r
353 // Enable I/O and MMIO in PCI UART Device if they are not already enabled\r
354 //\r
355 PciOr16 (\r
356 PciLibAddress + PCI_COMMAND_OFFSET,\r
357 PcdGetBool (PcdSerialUseMmio) ? EFI_PCI_COMMAND_MEMORY_SPACE : EFI_PCI_COMMAND_IO_SPACE\r
358 );\r
359\r
360 //\r
361 // Force D0 state if a Power Management and Status Register is specified\r
362 //\r
363 if (DeviceInfo->PowerManagementStatusAndControlRegister != 0x00) {\r
364 if ((PciRead16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister) & (BIT0 | BIT1)) != 0x00) {\r
365 PciAnd16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister, (UINT16)~(BIT0 | BIT1));\r
366 //\r
367 // If PCI UART was not in D0, then make sure FIFOs are enabled, but do not reset FIFOs\r
368 //\r
369 SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, (UINT8)(PcdGet8 (PcdSerialFifoControl) & (B_UART_FCR_FIFOE | B_UART_FCR_FIFO64)));\r
370 }\r
371 }\r
372 \r
373 //\r
374 // Get PCI Device Info\r
375 //\r
376 DeviceInfo = (PCI_UART_DEVICE_INFO *) PcdGetPtr (PcdSerialPciDeviceInfo);\r
377\r
378 //\r
379 // Enable I/O or MMIO in PCI Bridge\r
380 // Assume Root Bus Numer is Zero. \r
381 //\r
382 for (BusNumber = 0; (DeviceInfo + 1)->Device != 0xff; DeviceInfo++) {\r
383 //\r
384 // Compute PCI Lib Address to PCI to PCI Bridge\r
385 //\r
386 PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);\r
387 \r
388 //\r
389 // Enable the I/O or MMIO decode windows in the PCI to PCI Bridge\r
390 //\r
391 PciOr16 (\r
392 PciLibAddress + PCI_COMMAND_OFFSET, \r
393 PcdGetBool (PcdSerialUseMmio) ? EFI_PCI_COMMAND_MEMORY_SPACE : EFI_PCI_COMMAND_IO_SPACE\r
394 );\r
395 \r
396 //\r
397 // Force D0 state if a Power Management and Status Register is specified\r
398 //\r
399 if (DeviceInfo->PowerManagementStatusAndControlRegister != 0x00) {\r
400 if ((PciRead16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister) & (BIT0 | BIT1)) != 0x00) {\r
401 PciAnd16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister, (UINT16)~(BIT0 | BIT1));\r
402 }\r
403 }\r
404 \r
405 BusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);\r
406 }\r
407 \r
408 return SerialRegisterBase;\r
409}\r
410\r
e5010d30
RN
411/**\r
412 Return whether the hardware flow control signal allows writing.\r
413\r
414 @retval TRUE The serial port is writable.\r
415 @retval FALSE The serial port is not writable.\r
416**/\r
417BOOLEAN\r
418SerialPortWritable (\r
31122d8c 419 UINTN SerialRegisterBase\r
e5010d30
RN
420 )\r
421{\r
422 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {\r
423 if (PcdGetBool (PcdSerialDetectCable)) {\r
424 //\r
425 // Wait for both DSR and CTS to be set\r
426 // DSR is set if a cable is connected.\r
427 // CTS is set if it is ok to transmit data\r
428 //\r
429 // DSR CTS Description Action\r
430 // === === ======================================== ========\r
431 // 0 0 No cable connected. Wait\r
432 // 0 1 No cable connected. Wait\r
433 // 1 0 Cable connected, but not clear to send. Wait\r
434 // 1 1 Cable connected, and clear to send. Transmit\r
435 //\r
31122d8c 436 return (BOOLEAN) ((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) == (B_UART_MSR_DSR | B_UART_MSR_CTS));\r
e5010d30
RN
437 } else {\r
438 //\r
439 // Wait for both DSR and CTS to be set OR for DSR to be clear. \r
440 // DSR is set if a cable is connected.\r
441 // CTS is set if it is ok to transmit data\r
442 //\r
443 // DSR CTS Description Action\r
444 // === === ======================================== ========\r
445 // 0 0 No cable connected. Transmit\r
446 // 0 1 No cable connected. Transmit\r
447 // 1 0 Cable connected, but not clear to send. Wait\r
448 // 1 1 Cable connected, and clar to send. Transmit\r
449 //\r
31122d8c 450 return (BOOLEAN) ((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) != (B_UART_MSR_DSR));\r
e5010d30
RN
451 }\r
452 }\r
453\r
454 return TRUE;\r
455}\r
456\r
467d15ae 457/**\r
458 Initialize the serial device hardware.\r
459 \r
460 If no initialization is required, then return RETURN_SUCCESS.\r
e5010d30 461 If the serial device was successfully initialized, then return RETURN_SUCCESS.\r
467d15ae 462 If the serial device could not be initialized, then return RETURN_DEVICE_ERROR.\r
463 \r
464 @retval RETURN_SUCCESS The serial device was initialized.\r
465 @retval RETURN_DEVICE_ERROR The serial device could not be initialized.\r
466\r
467**/\r
468RETURN_STATUS\r
469EFIAPI\r
470SerialPortInitialize (\r
471 VOID\r
472 )\r
473{\r
474 RETURN_STATUS Status;\r
31122d8c
LG
475 UINTN SerialRegisterBase;\r
476 UINT32 Divisor;\r
477 UINT32 CurrentDivisor; \r
467d15ae 478 BOOLEAN Initialized;\r
479\r
480 //\r
481 // Perform platform specific initialization required to enable use of the 16550 device\r
482 // at the location specified by PcdSerialUseMmio and PcdSerialRegisterBase.\r
483 //\r
484 Status = PlatformHookSerialPortInitialize ();\r
485 if (RETURN_ERROR (Status)) {\r
486 return Status;\r
487 }\r
488\r
31122d8c
LG
489 //\r
490 // Calculate divisor for baud generator\r
491 // Ref_Clk_Rate / Baud_Rate / 16\r
492 //\r
493 Divisor = PcdGet32 (PcdSerialClockRate) / (PcdGet32 (PcdSerialBaudRate) * 16);\r
494 if ((PcdGet32 (PcdSerialClockRate) % (PcdGet32 (PcdSerialBaudRate) * 16)) >= PcdGet32 (PcdSerialBaudRate) * 8) {\r
495 Divisor++;\r
496 }\r
497\r
498 //\r
499 // Get the base address of the serial port in either I/O or MMIO space\r
500 //\r
501 SerialRegisterBase = GetSerialRegisterBase ();\r
502 if (SerialRegisterBase ==0) {\r
503 return RETURN_DEVICE_ERROR;\r
504 }\r
505\r
467d15ae 506 //\r
507 // See if the serial port is already initialized\r
508 //\r
509 Initialized = TRUE;\r
31122d8c 510 if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & 0x3F) != (PcdGet8 (PcdSerialLineControl) & 0x3F)) {\r
467d15ae 511 Initialized = FALSE;\r
512 }\r
31122d8c
LG
513 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) | B_UART_LCR_DLAB));\r
514 CurrentDivisor = SerialPortReadRegister (SerialRegisterBase, R_UART_BAUD_HIGH) << 8;\r
515 CurrentDivisor |= (UINT32) SerialPortReadRegister (SerialRegisterBase, R_UART_BAUD_LOW);\r
516 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & ~B_UART_LCR_DLAB));\r
517 if (CurrentDivisor != Divisor) {\r
467d15ae 518 Initialized = FALSE;\r
519 }\r
520 if (Initialized) {\r
521 return RETURN_SUCCESS;\r
522 }\r
31122d8c
LG
523\r
524 //\r
525 // Wait for the serial port to be ready.\r
526 // Verify that both the transmit FIFO and the shift register are empty.\r
527 //\r
528 while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY));\r
467d15ae 529 \r
530 //\r
531 // Configure baud rate\r
532 //\r
31122d8c
LG
533 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB);\r
534 SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_HIGH, (UINT8) (Divisor >> 8));\r
535 SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8) (Divisor & 0xff));\r
467d15ae 536\r
537 //\r
538 // Clear DLAB and configure Data Bits, Parity, and Stop Bits.\r
539 // Strip reserved bits from PcdSerialLineControl\r
540 //\r
31122d8c 541 SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(PcdGet8 (PcdSerialLineControl) & 0x3F));\r
467d15ae 542\r
543 //\r
544 // Enable and reset FIFOs\r
545 // Strip reserved bits from PcdSerialFifoControl\r
546 //\r
31122d8c
LG
547 SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, 0x00);\r
548 SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, (UINT8)(PcdGet8 (PcdSerialFifoControl) & (B_UART_FCR_FIFOE | B_UART_FCR_FIFO64)));\r
467d15ae 549\r
550 //\r
551 // Put Modem Control Register(MCR) into its reset state of 0x00.\r
552 // \r
31122d8c
LG
553 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, 0x00);\r
554\r
467d15ae 555 return RETURN_SUCCESS;\r
556}\r
557\r
558/**\r
559 Write data from buffer to serial device. \r
31122d8c 560\r
467d15ae 561 Writes NumberOfBytes data bytes from Buffer to the serial device. \r
562 The number of bytes actually written to the serial device is returned.\r
563 If the return value is less than NumberOfBytes, then the write operation failed.\r
564\r
565 If Buffer is NULL, then ASSERT(). \r
566\r
567 If NumberOfBytes is zero, then return 0.\r
568\r
569 @param Buffer Pointer to the data buffer to be written.\r
570 @param NumberOfBytes Number of bytes to written to the serial device.\r
571\r
572 @retval 0 NumberOfBytes is 0.\r
573 @retval >0 The number of bytes written to the serial device. \r
574 If this value is less than NumberOfBytes, then the read operation failed.\r
575\r
576**/\r
577UINTN\r
578EFIAPI\r
579SerialPortWrite (\r
580 IN UINT8 *Buffer,\r
581 IN UINTN NumberOfBytes\r
31122d8c 582 )\r
467d15ae 583{\r
31122d8c
LG
584 UINTN SerialRegisterBase;\r
585 UINTN Result;\r
586 UINTN Index;\r
587 UINTN FifoSize;\r
467d15ae 588\r
589 if (Buffer == NULL) {\r
590 return 0;\r
591 }\r
592\r
31122d8c
LG
593 SerialRegisterBase = GetSerialRegisterBase ();\r
594 if (SerialRegisterBase ==0) {\r
595 return 0;\r
596 }\r
597 \r
e5010d30
RN
598 if (NumberOfBytes == 0) {\r
599 //\r
600 // Flush the hardware\r
601 //\r
602\r
603 //\r
604 // Wait for both the transmit FIFO and shift register empty.\r
605 //\r
31122d8c 606 while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY));\r
e5010d30
RN
607\r
608 //\r
609 // Wait for the hardware flow control signal\r
610 //\r
31122d8c 611 while (!SerialPortWritable (SerialRegisterBase));\r
e5010d30
RN
612 return 0;\r
613 }\r
614\r
467d15ae 615 //\r
616 // Compute the maximum size of the Tx FIFO\r
617 //\r
618 FifoSize = 1;\r
619 if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFOE) != 0) {\r
620 if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFO64) == 0) {\r
621 FifoSize = 16;\r
622 } else {\r
31122d8c 623 FifoSize = PcdGet32 (PcdSerialExtendedTxFifoSize);\r
467d15ae 624 }\r
625 }\r
db662a64 626\r
467d15ae 627 Result = NumberOfBytes;\r
628 while (NumberOfBytes != 0) {\r
629 //\r
630 // Wait for the serial port to be ready, to make sure both the transmit FIFO\r
631 // and shift register empty.\r
632 //\r
31122d8c 633 while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_TEMT) == 0);\r
467d15ae 634\r
635 //\r
636 // Fill then entire Tx FIFO\r
637 //\r
638 for (Index = 0; Index < FifoSize && NumberOfBytes != 0; Index++, NumberOfBytes--, Buffer++) {\r
e5010d30
RN
639 //\r
640 // Wait for the hardware flow control signal\r
641 //\r
31122d8c 642 while (!SerialPortWritable (SerialRegisterBase));\r
e5010d30 643\r
467d15ae 644 //\r
645 // Write byte to the transmit buffer.\r
646 //\r
31122d8c 647 SerialPortWriteRegister (SerialRegisterBase, R_UART_TXBUF, *Buffer);\r
467d15ae 648 }\r
649 }\r
650 return Result;\r
651}\r
652\r
653/**\r
654 Reads data from a serial device into a buffer.\r
655\r
656 @param Buffer Pointer to the data buffer to store the data read from the serial device.\r
657 @param NumberOfBytes Number of bytes to read from the serial device.\r
658\r
659 @retval 0 NumberOfBytes is 0.\r
660 @retval >0 The number of bytes read from the serial device. \r
661 If this value is less than NumberOfBytes, then the read operation failed.\r
662\r
663**/\r
664UINTN\r
665EFIAPI\r
666SerialPortRead (\r
667 OUT UINT8 *Buffer,\r
668 IN UINTN NumberOfBytes\r
31122d8c 669 )\r
467d15ae 670{\r
31122d8c 671 UINTN SerialRegisterBase;\r
467d15ae 672 UINTN Result;\r
673 UINT8 Mcr;\r
674\r
675 if (NULL == Buffer) {\r
676 return 0;\r
677 }\r
678\r
31122d8c
LG
679 SerialRegisterBase = GetSerialRegisterBase ();\r
680 if (SerialRegisterBase ==0) {\r
681 return 0;\r
682 }\r
683\r
684 Mcr = (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) & ~B_UART_MCR_RTS);\r
467d15ae 685 \r
686 for (Result = 0; NumberOfBytes-- != 0; Result++, Buffer++) {\r
687 //\r
688 // Wait for the serial port to have some data.\r
689 //\r
31122d8c 690 while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_RXRDY) == 0) {\r
467d15ae 691 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {\r
692 //\r
693 // Set RTS to let the peer send some data\r
694 //\r
31122d8c 695 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(Mcr | B_UART_MCR_RTS));\r
467d15ae 696 }\r
697 }\r
698 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {\r
699 //\r
700 // Clear RTS to prevent peer from sending data\r
701 //\r
31122d8c 702 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, Mcr);\r
467d15ae 703 }\r
704 \r
705 //\r
706 // Read byte from the receive buffer.\r
707 //\r
31122d8c 708 *Buffer = SerialPortReadRegister (SerialRegisterBase, R_UART_RXBUF);\r
467d15ae 709 }\r
710 \r
711 return Result;\r
712}\r
713\r
31122d8c 714\r
467d15ae 715/**\r
716 Polls a serial device to see if there is any data waiting to be read.\r
717\r
718 Polls aserial device to see if there is any data waiting to be read.\r
719 If there is data waiting to be read from the serial device, then TRUE is returned.\r
720 If there is no data waiting to be read from the serial device, then FALSE is returned.\r
721\r
722 @retval TRUE Data is waiting to be read from the serial device.\r
723 @retval FALSE There is no data waiting to be read from the serial device.\r
724\r
725**/\r
726BOOLEAN\r
727EFIAPI\r
728SerialPortPoll (\r
729 VOID\r
730 )\r
731{\r
31122d8c
LG
732 UINTN SerialRegisterBase;\r
733 \r
734 SerialRegisterBase = GetSerialRegisterBase ();\r
735 if (SerialRegisterBase ==0) {\r
736 return FALSE;\r
737 }\r
738\r
467d15ae 739 //\r
740 // Read the serial port status\r
741 //\r
31122d8c 742 if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_RXRDY) != 0) {\r
467d15ae 743 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {\r
744 //\r
745 // Clear RTS to prevent peer from sending data\r
746 //\r
31122d8c 747 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) & ~B_UART_MCR_RTS));\r
467d15ae 748 }\r
749 return TRUE;\r
750 } \r
751 \r
752 if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {\r
753 //\r
754 // Set RTS to let the peer send some data\r
755 //\r
31122d8c 756 SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) | B_UART_MCR_RTS));\r
467d15ae 757 }\r
758 \r
759 return FALSE;\r
760}\r