1 /** @file
2   UART Serial Port library functions
3 
4   Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials
6   are licensed and made available under the terms and conditions of the BSD License
7   which accompanies this distribution.  The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include <Base.h>
16 #include <Library/IoLib.h>
17 #include <Library/SerialPortLib.h>
18 
19 //---------------------------------------------
20 // UART Register Offsets
21 //---------------------------------------------
22 #define BAUD_LOW_OFFSET         0x00
23 #define BAUD_HIGH_OFFSET        0x01
24 #define IER_OFFSET              0x01
25 #define LCR_SHADOW_OFFSET       0x01
26 #define FCR_SHADOW_OFFSET       0x02
27 #define IR_CONTROL_OFFSET       0x02
28 #define FCR_OFFSET              0x02
29 #define EIR_OFFSET              0x02
30 #define BSR_OFFSET              0x03
31 #define LCR_OFFSET              0x03
32 #define MCR_OFFSET              0x04
33 #define LSR_OFFSET              0x05
34 #define MSR_OFFSET              0x06
35 
36 //---------------------------------------------
37 // UART Register Bit Defines
38 //---------------------------------------------
39 #define LSR_TXRDY               0x20
40 #define LSR_RXDA                0x01
41 #define DLAB                    0x01
42 #define MCR_DTRC                0x01
43 #define MCR_RTS                 0x02
44 #define MSR_CTS                 0x10
45 #define MSR_DSR                 0x20
46 #define MSR_RI                  0x40
47 #define MSR_DCD                 0x80
48 
49 //---------------------------------------------
50 // UART Settings
51 //---------------------------------------------
52 UINT16  gUartBase = 0x3F8;
53 UINTN   gBps      = 115200;
54 UINT8   gData     = 8;
55 UINT8   gStop     = 1;
56 UINT8   gParity   = 0;
57 UINT8   gBreakSet = 0;
58 
59 /**
60   Initialize the serial device hardware.
61 
62   If no initialization is required, then return RETURN_SUCCESS.
63   If the serial device was successfuly initialized, then return RETURN_SUCCESS.
64   If the serial device could not be initialized, then return RETURN_DEVICE_ERROR.
65 
66   @retval RETURN_SUCCESS        The serial device was initialized.
67   @retval RETURN_DEVICE_ERROR   The serail device could not be initialized.
68 
69 **/
70 RETURN_STATUS
71 EFIAPI
SerialPortInitialize(VOID)72 SerialPortInitialize (
73   VOID
74   )
75 {
76   UINTN  Divisor;
77   UINT8  OutputData;
78   UINT8  Data;
79 
80   //
81   // Map 5..8 to 0..3
82   //
83   Data = (UINT8) (gData - (UINT8) 5);
84 
85   //
86   // Calculate divisor for baud generator
87   //
88   Divisor = 115200 / gBps;
89 
90   //
91   // Set communications format
92   //
93   OutputData = (UINT8) ((DLAB << 7) | (gBreakSet << 6) | (gParity << 3) | (gStop << 2) | Data);
94   IoWrite8 ((UINTN) (gUartBase + LCR_OFFSET), OutputData);
95 
96   //
97   // Configure baud rate
98   //
99   IoWrite8 ((UINTN) (gUartBase + BAUD_HIGH_OFFSET), (UINT8) (Divisor >> 8));
100   IoWrite8 ((UINTN) (gUartBase + BAUD_LOW_OFFSET), (UINT8) (Divisor & 0xff));
101 
102   //
103   // Switch back to bank 0
104   //
105   OutputData = (UINT8) ((~DLAB << 7) | (gBreakSet << 6) | (gParity << 3) | (gStop << 2) | Data);
106   IoWrite8 ((UINTN) (gUartBase + LCR_OFFSET), OutputData);
107 
108   return RETURN_SUCCESS;
109 }
110 
111 /**
112   Write data from buffer to serial device.
113 
114   Writes NumberOfBytes data bytes from Buffer to the serial device.
115   The number of bytes actually written to the serial device is returned.
116   If the return value is less than NumberOfBytes, then the write operation failed.
117 
118   If Buffer is NULL, then ASSERT().
119 
120   If NumberOfBytes is zero, then return 0.
121 
122   @param  Buffer           Pointer to the data buffer to be written.
123   @param  NumberOfBytes    Number of bytes to written to the serial device.
124 
125   @retval 0                NumberOfBytes is 0.
126   @retval >0               The number of bytes written to the serial device.
127                            If this value is less than NumberOfBytes, then the write operation failed.
128 
129 **/
130 UINTN
131 EFIAPI
SerialPortWrite(IN UINT8 * Buffer,IN UINTN NumberOfBytes)132 SerialPortWrite (
133   IN UINT8     *Buffer,
134   IN UINTN     NumberOfBytes
135 )
136 {
137   UINTN  Result;
138   UINT8  Data;
139 
140   if (Buffer == NULL) {
141     return 0;
142   }
143 
144   Result = NumberOfBytes;
145 
146   while ((NumberOfBytes--) != 0) {
147     //
148     // Wait for the serail port to be ready.
149     //
150     do {
151       Data = IoRead8 ((UINT16) gUartBase + LSR_OFFSET);
152     } while ((Data & LSR_TXRDY) == 0);
153     IoWrite8 ((UINT16) gUartBase, *Buffer++);
154   }
155 
156   return Result;
157 }
158 
159 
160 /**
161   Reads data from a serial device into a buffer.
162 
163   @param  Buffer           Pointer to the data buffer to store the data read from the serial device.
164   @param  NumberOfBytes    Number of bytes to read from the serial device.
165 
166   @retval 0                NumberOfBytes is 0.
167   @retval >0               The number of bytes read from the serial device.
168                            If this value is less than NumberOfBytes, then the read operation failed.
169 
170 **/
171 UINTN
172 EFIAPI
SerialPortRead(OUT UINT8 * Buffer,IN UINTN NumberOfBytes)173 SerialPortRead (
174   OUT UINT8     *Buffer,
175   IN  UINTN     NumberOfBytes
176 )
177 {
178   UINTN  Result;
179   UINT8  Data;
180 
181   if (NULL == Buffer) {
182     return 0;
183   }
184 
185   Result = NumberOfBytes;
186 
187   while ((NumberOfBytes--) != 0) {
188     //
189     // Wait for the serail port to be ready.
190     //
191     do {
192       Data = IoRead8 ((UINT16) gUartBase + LSR_OFFSET);
193     } while ((Data & LSR_RXDA) == 0);
194 
195     *Buffer++ = IoRead8 ((UINT16) gUartBase);
196   }
197 
198   return Result;
199 }
200 
201 /**
202   Polls a serial device to see if there is any data waiting to be read.
203 
204   Polls aserial device to see if there is any data waiting to be read.
205   If there is data waiting to be read from the serial device, then TRUE is returned.
206   If there is no data waiting to be read from the serial device, then FALSE is returned.
207 
208   @retval TRUE             Data is waiting to be read from the serial device.
209   @retval FALSE            There is no data waiting to be read from the serial device.
210 
211 **/
212 BOOLEAN
213 EFIAPI
SerialPortPoll(VOID)214 SerialPortPoll (
215   VOID
216   )
217 {
218   UINT8  Data;
219 
220   //
221   // Read the serial port status.
222   //
223   Data = IoRead8 ((UINT16) gUartBase + LSR_OFFSET);
224 
225   return (BOOLEAN) ((Data & LSR_RXDA) != 0);
226 }
227 
228 /**
229   Sets the control bits on a serial device.
230 
231   @param Control                Sets the bits of Control that are settable.
232 
233   @retval RETURN_SUCCESS        The new control bits were set on the serial device.
234   @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
235   @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
236 
237 **/
238 RETURN_STATUS
239 EFIAPI
SerialPortSetControl(IN UINT32 Control)240 SerialPortSetControl (
241   IN UINT32 Control
242   )
243 {
244   UINT8 Mcr;
245 
246   //
247   // First determine the parameter is invalid.
248   //
249   if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY))) != 0) {
250     return RETURN_UNSUPPORTED;
251   }
252 
253   //
254   // Read the Modem Control Register.
255   //
256   Mcr = IoRead8 ((UINT16) gUartBase + MCR_OFFSET);
257   Mcr &= (~(MCR_DTRC | MCR_RTS));
258 
259   if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
260     Mcr |= MCR_DTRC;
261   }
262 
263   if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
264     Mcr |= MCR_RTS;
265   }
266 
267   //
268   // Write the Modem Control Register.
269   //
270   IoWrite8 ((UINT16) gUartBase + MCR_OFFSET, Mcr);
271 
272   return RETURN_SUCCESS;
273 }
274 
275 /**
276   Retrieve the status of the control bits on a serial device.
277 
278   @param Control                A pointer to return the current control signals from the serial device.
279 
280   @retval RETURN_SUCCESS        The control bits were read from the serial device.
281   @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
282   @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
283 
284 **/
285 RETURN_STATUS
286 EFIAPI
SerialPortGetControl(OUT UINT32 * Control)287 SerialPortGetControl (
288   OUT UINT32 *Control
289   )
290 {
291   UINT8 Msr;
292   UINT8 Mcr;
293   UINT8 Lsr;
294 
295   *Control = 0;
296 
297   //
298   // Read the Modem Status Register.
299   //
300   Msr = IoRead8 ((UINT16) gUartBase + MSR_OFFSET);
301 
302   if ((Msr & MSR_CTS) == MSR_CTS) {
303     *Control |= EFI_SERIAL_CLEAR_TO_SEND;
304   }
305 
306   if ((Msr & MSR_DSR) == MSR_DSR) {
307     *Control |= EFI_SERIAL_DATA_SET_READY;
308   }
309 
310   if ((Msr & MSR_RI) == MSR_RI) {
311     *Control |= EFI_SERIAL_RING_INDICATE;
312   }
313 
314   if ((Msr & MSR_DCD) == MSR_DCD) {
315     *Control |= EFI_SERIAL_CARRIER_DETECT;
316   }
317 
318   //
319   // Read the Modem Control Register.
320   //
321   Mcr = IoRead8 ((UINT16) gUartBase + MCR_OFFSET);
322 
323   if ((Mcr & MCR_DTRC) == MCR_DTRC) {
324     *Control |= EFI_SERIAL_DATA_TERMINAL_READY;
325   }
326 
327   if ((Mcr & MCR_RTS) == MCR_RTS) {
328     *Control |= EFI_SERIAL_REQUEST_TO_SEND;
329   }
330 
331   //
332   // Read the Line Status Register.
333   //
334   Lsr = IoRead8 ((UINT16) gUartBase + LSR_OFFSET);
335 
336   if ((Lsr & LSR_TXRDY) == LSR_TXRDY) {
337     *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
338   }
339 
340   if ((Lsr & LSR_RXDA) == 0) {
341     *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
342   }
343 
344   return RETURN_SUCCESS;
345 }
346 
347 /**
348   Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
349   data bits, and stop bits on a serial device.
350 
351   @param BaudRate           The requested baud rate. A BaudRate value of 0 will use the
352                             device's default interface speed.
353                             On output, the value actually set.
354   @param ReveiveFifoDepth   The requested depth of the FIFO on the receive side of the
355                             serial interface. A ReceiveFifoDepth value of 0 will use
356                             the device's default FIFO depth.
357                             On output, the value actually set.
358   @param Timeout            The requested time out for a single character in microseconds.
359                             This timeout applies to both the transmit and receive side of the
360                             interface. A Timeout value of 0 will use the device's default time
361                             out value.
362                             On output, the value actually set.
363   @param Parity             The type of parity to use on this serial device. A Parity value of
364                             DefaultParity will use the device's default parity value.
365                             On output, the value actually set.
366   @param DataBits           The number of data bits to use on the serial device. A DataBits
367                             vaule of 0 will use the device's default data bit setting.
368                             On output, the value actually set.
369   @param StopBits           The number of stop bits to use on this serial device. A StopBits
370                             value of DefaultStopBits will use the device's default number of
371                             stop bits.
372                             On output, the value actually set.
373 
374   @retval RETURN_SUCCESS            The new attributes were set on the serial device.
375   @retval RETURN_UNSUPPORTED        The serial device does not support this operation.
376   @retval RETURN_INVALID_PARAMETER  One or more of the attributes has an unsupported value.
377   @retval RETURN_DEVICE_ERROR       The serial device is not functioning correctly.
378 
379 **/
380 RETURN_STATUS
381 EFIAPI
SerialPortSetAttributes(IN OUT UINT64 * BaudRate,IN OUT UINT32 * ReceiveFifoDepth,IN OUT UINT32 * Timeout,IN OUT EFI_PARITY_TYPE * Parity,IN OUT UINT8 * DataBits,IN OUT EFI_STOP_BITS_TYPE * StopBits)382 SerialPortSetAttributes (
383   IN OUT UINT64             *BaudRate,
384   IN OUT UINT32             *ReceiveFifoDepth,
385   IN OUT UINT32             *Timeout,
386   IN OUT EFI_PARITY_TYPE    *Parity,
387   IN OUT UINT8              *DataBits,
388   IN OUT EFI_STOP_BITS_TYPE *StopBits
389   )
390 {
391   UINTN Divisor;
392   UINT8 OutputData;
393   UINT8 LcrData;
394   UINT8 LcrParity;
395   UINT8 LcrStop;
396 
397   //
398   // Check for default settings and fill in actual values.
399   //
400   if (*BaudRate == 0) {
401     *BaudRate = gBps;
402   }
403 
404   if (*DataBits == 0) {
405     *DataBits = gData;
406   }
407 
408   if (*Parity == DefaultParity) {
409     *Parity = NoParity;
410   }
411 
412   if (*StopBits == DefaultStopBits) {
413     *StopBits = OneStopBit;
414   }
415 
416   if ((*DataBits < 5) || (*DataBits > 8)) {
417     return RETURN_INVALID_PARAMETER;
418   }
419 
420   //
421   // Map 5..8 to 0..3
422   //
423   LcrData = (UINT8) (*DataBits - (UINT8) 5);
424 
425   switch (*Parity) {
426     case NoParity:
427       LcrParity = 0;
428       break;
429 
430     case EvenParity:
431       LcrParity = 3;
432       break;
433 
434     case OddParity:
435       LcrParity = 1;
436       break;
437 
438     case SpaceParity:
439       LcrParity = 7;
440       break;
441 
442     case MarkParity:
443       LcrParity = 5;
444       break;
445 
446     default:
447       return RETURN_INVALID_PARAMETER;
448   }
449 
450   switch (*StopBits) {
451     case OneStopBit:
452       LcrStop = 0;
453       break;
454 
455     case OneFiveStopBits:
456     case TwoStopBits:
457       LcrStop = 1;
458       break;
459 
460     default:
461       return RETURN_INVALID_PARAMETER;
462   }
463 
464   //
465   // Calculate divisor for baud generator
466   //
467   Divisor = 115200 / (UINTN) *BaudRate;
468 
469   //
470   // Set communications format
471   //
472   OutputData = (UINT8) ((DLAB << 7) | (gBreakSet << 6) | (LcrParity << 3) | (LcrStop << 2) | LcrData);
473   IoWrite8 ((UINTN) (gUartBase + LCR_OFFSET), OutputData);
474 
475   //
476   // Configure baud rate
477   //
478   IoWrite8 ((UINTN) (gUartBase + BAUD_HIGH_OFFSET), (UINT8) (Divisor >> 8));
479   IoWrite8 ((UINTN) (gUartBase + BAUD_LOW_OFFSET), (UINT8) (Divisor & 0xff));
480 
481   //
482   // Switch back to bank 0
483   //
484   OutputData = (UINT8) ((~DLAB << 7) | (gBreakSet << 6) | (LcrParity << 3) | (LcrStop << 2) | LcrData);
485   IoWrite8 ((UINTN) (gUartBase + LCR_OFFSET), OutputData);
486 
487   return RETURN_SUCCESS;
488 }
489 
490