hc
2024-03-22 a0752693d998599af469473b8dc239ef973a012f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/** @file
  PCH Serial IO UART Lib implementation.
  All function in this library is available for PEI, DXE, and SMM,
  But do not support UEFI RUNTIME environment call.
 
  Copyright (c) 2019 Intel Corporation. All rights reserved. <BR>
 
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
 
#include <Base.h>
#include <Uefi/UefiBaseType.h>
#include <Library/IoLib.h>
#include <Library/BaseLib.h>
#include <Library/PchPcrLib.h>
#include <Library/PchSerialIoLib.h>
#include <Library/PchSerialIoUartLib.h>
#include <IndustryStandard/Pci30.h>
 
#define MAX_BAUD_RATE      115200
 
#define R_PCH_SERIAL_IO_8BIT_UART_RXBUF      0x00
#define R_PCH_SERIAL_IO_8BIT_UART_TXBUF      0x00
#define R_PCH_SERIAL_IO_8BIT_UART_BAUD_LOW   0x00
#define R_PCH_SERIAL_IO_8BIT_UART_BAUD_HIGH  0x01
#define R_PCH_SERIAL_IO_8BIT_UART_FCR        0x02
#define R_PCH_SERIAL_IO_8BIT_UART_IIR        0x02
#define R_PCH_SERIAL_IO_8BIT_UART_LCR        0x03
#define R_PCH_SERIAL_IO_8BIT_UART_MCR        0x04
#define R_PCH_SERIAL_IO_8BIT_UART_LSR        0x05
#define R_PCH_SERIAL_IO_8BIT_UART_USR        0x1F
#define R_PCH_SERIAL_IO_32BIT_UART_CTR       0xFC //Component Type Register contains identification code
#define UART_COMPONENT_IDENTIFICATION_CODE   0x44570110
 
#define B_PCH_SERIAL_IO_UART_IIR_FIFOSE   BIT7|BIT6
#define B_PCH_SERIAL_IO_UART_LSR_RXDA     BIT0
#define B_PCH_SERIAL_IO_UART_LSR_BI       BIT4
#define B_PCH_SERIAL_IO_UART_LSR_TXRDY    BIT5
#define B_PCH_SERIAL_IO_UART_LCR_DLAB     BIT7
#define B_PCH_SERIAL_IO_UART_FCR_FCR      BIT0
#define B_PCH_SERIAL_IO_UART_MCR_RTS      BIT1
#define B_PCH_SERIAL_IO_UART_MCR_AFCE     BIT5
#define B_PCH_SERIAL_IO_UART_USR_TFNF     BIT1
 
/**
  Returns UART's currently active access mode, 8 or 32 bit
 
  @param[in]  MmioBase    Base address of UART MMIO space
 
  @retval     AccessMode8bit
  @retval     AccessMode32bit
**/
UART_ACCESS_MODE
DetectAccessMode (
  IN UINTN  MmioBase
  )
{
  if (MmioRead32 (MmioBase + R_PCH_SERIAL_IO_32BIT_UART_CTR) == UART_COMPONENT_IDENTIFICATION_CODE) {
    return AccessMode32bit;
  } else {
    return AccessMode8bit;
  }
}
 
 
/**
  Register access helper. Depending on SerialIO UART mode,
  its registers are aligned to 1 or 4 bytes and have 8 or 32bit size
 
  @param[in]  AccessMode     Selects between 8bit access to 1-byte aligned registers or 32bit access to 4-byte algined
  @param[in]  BaseAddress    Base address of UART MMIO space
  @param[in]  Offset         Register offset in 8bit mode
  @param[in]  Data           Data to be written
**/
STATIC
VOID
WriteRegister (
  IN UART_ACCESS_MODE AccessMode,
  IN UINTN            BaseAddress,
  IN UINTN            Offset,
  IN UINT8            Data
  )
{
  if (AccessMode == AccessMode32bit) {
    MmioWrite32 (BaseAddress + 4*Offset, Data);
  } else {
    MmioWrite8 (BaseAddress + Offset, Data);
  }
}
 
/**
  Register access helper. Depending on SerialIO UART mode,
  its registers are aligned to 1 or 4 bytes and have 8 or 32bit size
 
  @param[in]  AccessMode     Selects between 8bit access to 1-byte aligned registers or 32bit access to 4-byte algined
  @param[in]  BaseAddress    Base address of UART MMIO space
  @param[in]  Offset         Register offset in 8bit mode
  @retval                    retrieved register value, always 8bit regardless of access mode
**/
STATIC
UINT8
ReadRegister (
  IN UART_ACCESS_MODE AccessMode,
  IN UINTN            BaseAddress,
  IN UINTN            Offset
  )
{
  if (AccessMode == AccessMode32bit) {
    return (UINT8) (0xFF & MmioRead32 (BaseAddress + 4*Offset));
  } else {
    return MmioRead8 (BaseAddress + Offset);
  }
}
 
/**
  SerialIo UART in PCI mode will become unavailable when PCI enumerator
  disables its memory space. This function re-enables it
 
  @param[in]  UartNumber           Selects Serial IO UART device (0-2)
**/
STATIC
VOID
EnablePciMse (
  IN UINT8  UartNumber
  )
{
  UINTN  CfgSpace;
 
  CfgSpace = FindSerialIoBar (UartNumber + PchSerialIoIndexUart0, 1);
  if (MmioRead16 (CfgSpace + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
    return;
  }
  if ((MmioRead16 (CfgSpace + PCI_COMMAND_OFFSET) & EFI_PCI_COMMAND_MEMORY_SPACE) != EFI_PCI_COMMAND_MEMORY_SPACE) {
    if ((MmioRead32 (CfgSpace + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFF000) != 0x0 &&
        (MmioRead32 (CfgSpace + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFF000) != 0xFFFFF000 ) {
      MmioOr8 (CfgSpace + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE);
    }
  }
}
 
/**
  Initialize selected SerialIo UART.
  This init function MUST be used prior any SerialIo UART functions to init serial io controller if platform is going use serialio UART as debug output.
 
  @param  UartNumber           Selects Serial IO UART device (0-2)
  @param  FifoEnable           When TRUE, enables 64-byte FIFOs.
  @param  BaudRate             Baud rate.
  @param  LineControl          Data length, parity, stop bits.
  @param  HardwareFlowControl  Automated hardware flow control. If TRUE, hardware automatically checks CTS when sending data, and sets RTS when receiving data.
**/
VOID
EFIAPI
PchSerialIoUartInit (
  UINT8            UartNumber,
  BOOLEAN          FifoEnable,
  UINT32           BaudRate,
  UINT8            LineControl,
  BOOLEAN          HardwareFlowControl
  )
{
  UINTN            Base;
  UINTN            Divisor;
  UART_ACCESS_MODE AccessMode;
 
  Base = FindSerialIoBar (UartNumber + PchSerialIoIndexUart0, 0);
  if ((Base & 0xFFFFFF00) == 0x0 || (Base & 0xFFFFF000) == 0xFFFFF000) {
    //
    // Base is not programmed, skip it.
    //
    return;
  }
  EnablePciMse (UartNumber);
  AccessMode = DetectAccessMode (Base);
 
  Divisor = MAX_BAUD_RATE / BaudRate;
 
  //
  // Configure baud rate
  //
  WriteRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_LCR, B_PCH_SERIAL_IO_UART_LCR_DLAB);
  WriteRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_BAUD_HIGH, (UINT8) (Divisor >> 8));
  WriteRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_BAUD_LOW, (UINT8) (Divisor & 0xff));
  //
  //  Configure Line control and switch back to bank 0
  //
  WriteRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_LCR, LineControl & 0x1F);
  //
  // Enable and reset FIFOs
  //
  WriteRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_FCR, FifoEnable?B_PCH_SERIAL_IO_UART_FCR_FCR:0 );
  //
  // Put Modem Control Register(MCR) into its reset state of 0x00.
  //
  WriteRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_MCR, B_PCH_SERIAL_IO_UART_MCR_RTS | (HardwareFlowControl?B_PCH_SERIAL_IO_UART_MCR_AFCE:0));
}
 
/**
  Write data to serial device.
 
  If the buffer is NULL, then return 0;
  if NumberOfBytes is zero, then return 0.
 
  @param  UartNumber       Selects Serial IO UART device (0-2)
  @param  Buffer           Point of data buffer which need to be writed.
  @param  NumberOfBytes    Number of output bytes which are cached in Buffer.
 
  @retval                  Actual number of bytes writed to serial device.
**/
UINTN
EFIAPI
PchSerialIoUartOut (
  IN UINT8            UartNumber,
  IN UINT8            *Buffer,
  IN UINTN            NumberOfBytes
  )
{
  UINTN            BytesLeft;
  UINTN            Base;
  UART_ACCESS_MODE AccessMode;
 
  if (NULL == Buffer) {
    return 0;
  }
 
  Base = FindSerialIoBar (UartNumber + PchSerialIoIndexUart0, 0);
  //
  // Sanity checks to avoid infinite loop when trying to print through uninitialized UART
  //
  if ((Base & 0xFFFFFF00) == 0x0 || (Base & 0xFFFFF000) == 0xFFFFF000) {
    return 0;
  }
  EnablePciMse (UartNumber);
  AccessMode = DetectAccessMode (Base);
 
  if (ReadRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_USR) == 0xFF) {
    return 0;
  }
 
  BytesLeft = NumberOfBytes;
 
  while (BytesLeft != 0) {
    //
    // Write data while there's room in TXFIFO. If HW Flow Control was enabled, it happens automatically on hardware level.
    //
    if (ReadRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_USR) & B_PCH_SERIAL_IO_UART_USR_TFNF) {
      WriteRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_TXBUF, *Buffer);
      Buffer++;
      BytesLeft--;
    }
  }
 
  return NumberOfBytes;
}
 
/*
  Read data from serial device and save the datas in buffer.
 
  If the buffer is NULL, then return 0;
  if NumberOfBytes is zero, then return 0.
 
  @param  UartNumber           Selects Serial IO UART device (0-2)
  @param  Buffer               Point of data buffer which need to be writed.
  @param  NumberOfBytes        Number of output bytes which are cached in Buffer.
  @param  WaitUntilBufferFull  When TRUE, function waits until whole buffer is filled. When FALSE, function returns as soon as no new characters are available.
 
  @retval                      Actual number of bytes raed to serial device.
 
**/
UINTN
EFIAPI
PchSerialIoUartIn (
  IN  UINT8            UartNumber,
  OUT UINT8            *Buffer,
  IN  UINTN            NumberOfBytes,
  IN  BOOLEAN          WaitUntilBufferFull
  )
{
  UINTN            BytesReceived;
  UINTN            Base;
  UART_ACCESS_MODE AccessMode;
  UINT8            Lsr;
  UINT8            Byte;
 
  if (NULL == Buffer) {
    return 0;
  }
 
  Base = FindSerialIoBar (UartNumber + PchSerialIoIndexUart0, 0);
  //
  // Sanity checks to avoid infinite loop when trying to print through uninitialized UART
  //
  if ((Base & 0xFFFFFF00) == 0x0 || (Base & 0xFFFFF000) == 0xFFFFF000) {
    return 0;
  }
  EnablePciMse (UartNumber);
  AccessMode = DetectAccessMode (Base);
 
  BytesReceived = 0;
 
  while (BytesReceived != NumberOfBytes) {
    //
    // Read the line status register
    //
    Lsr = ReadRegister(AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_LSR);
 
    //
    // If there is data in the RX buffer, read it.
    //
    if ((Lsr & B_PCH_SERIAL_IO_UART_LSR_RXDA) != 0) {
      Byte = ReadRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_RXBUF);
      //
      // Check if the break interrupt bit is set. If set, the byte read from the
      // RX buffer is invalid and should be ignored. If not set, copy the byte into
      // the receive buffer.
      //
      if ((Lsr & B_PCH_SERIAL_IO_UART_LSR_BI) == 0) {
        *Buffer = Byte;
        Buffer++;
        BytesReceived++;
      }
    } else {
      if (!WaitUntilBufferFull) {
        //
        // If there's no data and function shouldn't wait, exit early
        //
        return BytesReceived;
      }
    }
  }
  return BytesReceived;
}
 
/**
  Polls a serial device to see if there is any data waiting to be read.
 
  Polls a serial device to see if there is any data waiting to be read.
  If there is data waiting to be read from the serial device, then TRUE is returned.
  If there is no data waiting to be read from the serial device, then FALSE is returned.
 
  @param  UartNumber       Selects Serial IO UART device (0-2)
 
  @retval TRUE             Data is waiting to be read from the serial device.
  @retval FALSE            There is no data waiting to be read from the serial device.
 
**/
BOOLEAN
EFIAPI
PchSerialIoUartPoll (
  IN  UINT8            UartNumber
  )
{
  UINTN Base;
  UART_ACCESS_MODE AccessMode;
 
  Base = FindSerialIoBar (UartNumber + PchSerialIoIndexUart0, 0);
  //
  // Sanity checks to avoid infinite loop when trying to print through uninitialized UART
  //
  if ((Base & 0xFFFFFF00) == 0x0 || (Base & 0xFFFFF000) == 0xFFFFF000) {
    return 0;
  }
  EnablePciMse (UartNumber);
  AccessMode = DetectAccessMode (Base);
 
  //
  // Read the serial port status
  //
  if ((ReadRegister (AccessMode, Base, R_PCH_SERIAL_IO_8BIT_UART_LSR) & B_PCH_SERIAL_IO_UART_LSR_RXDA) != 0) {
    return TRUE;
  }
  return FALSE;
}