+/**\r
+ Update the value of an 16-bit PCI configuration register in a PCI device. If the\r
+ PCI Configuration register specified by PciAddress is already programmed with a\r
+ non-zero value, then return the current value. Otherwise update the PCI configuration\r
+ register specified by PciAddress with the value specified by Value and return the\r
+ value programmed into the PCI configuration register. All values must be masked\r
+ using the bitmask specified by Mask.\r
+\r
+ @param PciAddress PCI Library address of the PCI Configuration register to update.\r
+ @param Value The value to program into the PCI Configuration Register.\r
+ @param Mask Bitmask of the bits to check and update in the PCI configuration register.\r
+\r
+**/\r
+UINT16\r
+SerialPortLibUpdatePciRegister16 (\r
+ UINTN PciAddress,\r
+ UINT16 Value,\r
+ UINT16 Mask\r
+ )\r
+{\r
+ UINT16 CurrentValue;\r
+\r
+ CurrentValue = PciRead16 (PciAddress) & Mask;\r
+ if (CurrentValue != 0) {\r
+ return CurrentValue;\r
+ }\r
+ return PciWrite16 (PciAddress, Value & Mask);\r
+}\r
+\r
+/**\r
+ Update the value of an 32-bit PCI configuration register in a PCI device. If the\r
+ PCI Configuration register specified by PciAddress is already programmed with a\r
+ non-zero value, then return the current value. Otherwise update the PCI configuration\r
+ register specified by PciAddress with the value specified by Value and return the\r
+ value programmed into the PCI configuration register. All values must be masked\r
+ using the bitmask specified by Mask.\r
+\r
+ @param PciAddress PCI Library address of the PCI Configuration register to update.\r
+ @param Value The value to program into the PCI Configuration Register.\r
+ @param Mask Bitmask of the bits to check and update in the PCI configuration register.\r
+\r
+ @return The Secondary bus number that is actually programed into the PCI to PCI Bridge device.\r
+\r
+**/\r
+UINT32\r
+SerialPortLibUpdatePciRegister32 (\r
+ UINTN PciAddress,\r
+ UINT32 Value,\r
+ UINT32 Mask\r
+ )\r
+{\r
+ UINT32 CurrentValue;\r
+\r
+ CurrentValue = PciRead32 (PciAddress) & Mask;\r
+ if (CurrentValue != 0) {\r
+ return CurrentValue;\r
+ }\r
+ return PciWrite32 (PciAddress, Value & Mask);\r
+}\r
+\r
+/**\r
+ Retrieve the I/O or MMIO base address register for the PCI UART device.\r
+\r
+ This function assumes Root Bus Numer is Zero, and enables I/O and MMIO in PCI UART\r
+ Device if they are not already enabled.\r
+\r
+ @return The base address register of the UART device.\r
+\r
+**/\r
+UINTN\r
+GetSerialRegisterBase (\r
+ VOID\r
+ )\r
+{\r
+ UINTN PciLibAddress;\r
+ UINTN BusNumber;\r
+ UINTN SubordinateBusNumber;\r
+ UINT32 ParentIoBase;\r
+ UINT32 ParentIoLimit;\r
+ UINT16 ParentMemoryBase;\r
+ UINT16 ParentMemoryLimit;\r
+ UINT32 IoBase;\r
+ UINT32 IoLimit;\r
+ UINT16 MemoryBase;\r
+ UINT16 MemoryLimit;\r
+ UINTN SerialRegisterBase;\r
+ UINTN BarIndex;\r
+ UINT32 RegisterBaseMask;\r
+ PCI_UART_DEVICE_INFO *DeviceInfo;\r
+\r
+ //\r
+ // Get PCI Device Info\r
+ //\r
+ DeviceInfo = (PCI_UART_DEVICE_INFO *) PcdGetPtr (PcdSerialPciDeviceInfo);\r
+\r
+ //\r
+ // If PCI Device Info is empty, then assume fixed address UART and return PcdSerialRegisterBase\r
+ //\r
+ if (DeviceInfo->Device == 0xff) {\r
+ return (UINTN)PcdGet64 (PcdSerialRegisterBase);\r
+ }\r
+\r
+ //\r
+ // Assume PCI Bus 0 I/O window is 0-64KB and MMIO windows is 0-4GB\r
+ //\r
+ ParentMemoryBase = 0 >> 16;\r
+ ParentMemoryLimit = 0xfff00000 >> 16;\r
+ ParentIoBase = 0 >> 12;\r
+ ParentIoLimit = 0xf000 >> 12;\r
+\r
+ //\r
+ // Enable I/O and MMIO in PCI Bridge\r
+ // Assume Root Bus Numer is Zero.\r
+ //\r
+ for (BusNumber = 0; (DeviceInfo + 1)->Device != 0xff; DeviceInfo++) {\r
+ //\r
+ // Compute PCI Lib Address to PCI to PCI Bridge\r
+ //\r
+ PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);\r
+\r
+ //\r
+ // Retrieve and verify the bus numbers in the PCI to PCI Bridge\r
+ //\r
+ BusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);\r
+ SubordinateBusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET);\r
+ if (BusNumber == 0 || BusNumber > SubordinateBusNumber) {\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // Retrieve and verify the I/O or MMIO decode window in the PCI to PCI Bridge\r
+ //\r
+ if (PcdGetBool (PcdSerialUseMmio)) {\r
+ MemoryLimit = PciRead16 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.MemoryLimit)) & 0xfff0;\r
+ MemoryBase = PciRead16 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.MemoryBase)) & 0xfff0;\r
+\r
+ //\r
+ // If PCI Bridge MMIO window is disabled, then return 0\r
+ //\r
+ if (MemoryLimit < MemoryBase) {\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // If PCI Bridge MMIO window is not in the address range decoded by the parent PCI Bridge, then return 0\r
+ //\r
+ if (MemoryBase < ParentMemoryBase || MemoryBase > ParentMemoryLimit || MemoryLimit > ParentMemoryLimit) {\r
+ return 0;\r
+ }\r
+ ParentMemoryBase = MemoryBase;\r
+ ParentMemoryLimit = MemoryLimit;\r
+ } else {\r
+ IoLimit = PciRead8 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.IoLimit));\r
+ if ((IoLimit & PCI_BRIDGE_32_BIT_IO_SPACE ) == 0) {\r
+ IoLimit = IoLimit >> 4;\r
+ } else {\r
+ IoLimit = (PciRead16 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.IoLimitUpper16)) << 4) | (IoLimit >> 4);\r
+ }\r
+ IoBase = PciRead8 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.IoBase));\r
+ if ((IoBase & PCI_BRIDGE_32_BIT_IO_SPACE ) == 0) {\r
+ IoBase = IoBase >> 4;\r
+ } else {\r
+ IoBase = (PciRead16 (PciLibAddress + OFFSET_OF (PCI_TYPE01, Bridge.IoBaseUpper16)) << 4) | (IoBase >> 4);\r
+ }\r
+\r
+ //\r
+ // If PCI Bridge I/O window is disabled, then return 0\r
+ //\r
+ if (IoLimit < IoBase) {\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // If PCI Bridge I/O window is not in the address range decoded by the parent PCI Bridge, then return 0\r
+ //\r
+ if (IoBase < ParentIoBase || IoBase > ParentIoLimit || IoLimit > ParentIoLimit) {\r
+ return 0;\r
+ }\r
+ ParentIoBase = IoBase;\r
+ ParentIoLimit = IoLimit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Compute PCI Lib Address to PCI UART\r
+ //\r
+ PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);\r
+\r
+ //\r
+ // Find the first IO or MMIO BAR\r
+ //\r
+ RegisterBaseMask = 0xFFFFFFF0;\r
+ for (BarIndex = 0; BarIndex < PCI_MAX_BAR; BarIndex ++) {\r
+ SerialRegisterBase = PciRead32 (PciLibAddress + PCI_BASE_ADDRESSREG_OFFSET + BarIndex * 4);\r
+ if (PcdGetBool (PcdSerialUseMmio) && ((SerialRegisterBase & BIT0) == 0)) {\r
+ //\r
+ // MMIO BAR is found\r
+ //\r
+ RegisterBaseMask = 0xFFFFFFF0;\r
+ break;\r
+ }\r
+\r
+ if ((!PcdGetBool (PcdSerialUseMmio)) && ((SerialRegisterBase & BIT0) != 0)) {\r
+ //\r
+ // IO BAR is found\r
+ //\r
+ RegisterBaseMask = 0xFFFFFFF8;\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // MMIO or IO BAR is not found.\r
+ //\r
+ if (BarIndex == PCI_MAX_BAR) {\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // Program UART BAR\r
+ //\r
+ SerialRegisterBase = SerialPortLibUpdatePciRegister32 (\r
+ PciLibAddress + PCI_BASE_ADDRESSREG_OFFSET + BarIndex * 4,\r
+ (UINT32)PcdGet64 (PcdSerialRegisterBase),\r
+ RegisterBaseMask\r
+ );\r
+\r
+ //\r
+ // Verify that the UART BAR is in the address range decoded by the parent PCI Bridge\r
+ //\r
+ if (PcdGetBool (PcdSerialUseMmio)) {\r
+ if (((SerialRegisterBase >> 16) & 0xfff0) < ParentMemoryBase || ((SerialRegisterBase >> 16) & 0xfff0) > ParentMemoryLimit) {\r
+ return 0;\r
+ }\r
+ } else {\r
+ if ((SerialRegisterBase >> 12) < ParentIoBase || (SerialRegisterBase >> 12) > ParentIoLimit) {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Enable I/O and MMIO in PCI UART Device if they are not already enabled\r
+ //\r
+ PciOr16 (\r
+ PciLibAddress + PCI_COMMAND_OFFSET,\r
+ PcdGetBool (PcdSerialUseMmio) ? EFI_PCI_COMMAND_MEMORY_SPACE : EFI_PCI_COMMAND_IO_SPACE\r
+ );\r
+\r
+ //\r
+ // Force D0 state if a Power Management and Status Register is specified\r
+ //\r
+ if (DeviceInfo->PowerManagementStatusAndControlRegister != 0x00) {\r
+ if ((PciRead16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister) & (BIT0 | BIT1)) != 0x00) {\r
+ PciAnd16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister, (UINT16)~(BIT0 | BIT1));\r
+ //\r
+ // If PCI UART was not in D0, then make sure FIFOs are enabled, but do not reset FIFOs\r
+ //\r
+ SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, (UINT8)(PcdGet8 (PcdSerialFifoControl) & (B_UART_FCR_FIFOE | B_UART_FCR_FIFO64)));\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get PCI Device Info\r
+ //\r
+ DeviceInfo = (PCI_UART_DEVICE_INFO *) PcdGetPtr (PcdSerialPciDeviceInfo);\r
+\r
+ //\r
+ // Enable I/O or MMIO in PCI Bridge\r
+ // Assume Root Bus Numer is Zero.\r
+ //\r
+ for (BusNumber = 0; (DeviceInfo + 1)->Device != 0xff; DeviceInfo++) {\r
+ //\r
+ // Compute PCI Lib Address to PCI to PCI Bridge\r
+ //\r
+ PciLibAddress = PCI_LIB_ADDRESS (BusNumber, DeviceInfo->Device, DeviceInfo->Function, 0);\r
+\r
+ //\r
+ // Enable the I/O or MMIO decode windows in the PCI to PCI Bridge\r
+ //\r
+ PciOr16 (\r
+ PciLibAddress + PCI_COMMAND_OFFSET,\r
+ PcdGetBool (PcdSerialUseMmio) ? EFI_PCI_COMMAND_MEMORY_SPACE : EFI_PCI_COMMAND_IO_SPACE\r
+ );\r
+\r
+ //\r
+ // Force D0 state if a Power Management and Status Register is specified\r
+ //\r
+ if (DeviceInfo->PowerManagementStatusAndControlRegister != 0x00) {\r
+ if ((PciRead16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister) & (BIT0 | BIT1)) != 0x00) {\r
+ PciAnd16 (PciLibAddress + DeviceInfo->PowerManagementStatusAndControlRegister, (UINT16)~(BIT0 | BIT1));\r
+ }\r
+ }\r
+\r
+ BusNumber = PciRead8 (PciLibAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);\r
+ }\r
+\r
+ return SerialRegisterBase;\r
+}\r
+\r
+/**\r
+ Return whether the hardware flow control signal allows writing.\r
+\r
+ @param SerialRegisterBase The base address register of UART device.\r
+\r
+ @retval TRUE The serial port is writable.\r
+ @retval FALSE The serial port is not writable.\r
+**/\r
+BOOLEAN\r
+SerialPortWritable (\r
+ UINTN SerialRegisterBase\r
+ )\r
+{\r
+ if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {\r
+ if (PcdGetBool (PcdSerialDetectCable)) {\r
+ //\r
+ // Wait for both DSR and CTS to be set\r
+ // DSR is set if a cable is connected.\r
+ // CTS is set if it is ok to transmit data\r
+ //\r
+ // DSR CTS Description Action\r
+ // === === ======================================== ========\r
+ // 0 0 No cable connected. Wait\r
+ // 0 1 No cable connected. Wait\r
+ // 1 0 Cable connected, but not clear to send. Wait\r
+ // 1 1 Cable connected, and clear to send. Transmit\r
+ //\r
+ return (BOOLEAN) ((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) == (B_UART_MSR_DSR | B_UART_MSR_CTS));\r
+ } else {\r
+ //\r
+ // Wait for both DSR and CTS to be set OR for DSR to be clear.\r
+ // DSR is set if a cable is connected.\r
+ // CTS is set if it is ok to transmit data\r
+ //\r
+ // DSR CTS Description Action\r
+ // === === ======================================== ========\r
+ // 0 0 No cable connected. Transmit\r
+ // 0 1 No cable connected. Transmit\r
+ // 1 0 Cable connected, but not clear to send. Wait\r
+ // 1 1 Cable connected, and clar to send. Transmit\r
+ //\r
+ return (BOOLEAN) ((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) != (B_UART_MSR_DSR));\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r