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