ns16550.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. ns16550.c
  5. Abstract:
  6. This module implements the firmware serial port interface on a 16550
  7. standard UART.
  8. Author:
  9. Chris Stevens 10-Jul-2015
  10. Environment:
  11. Firmware
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include "uefifw.h"
  17. #include "dev/ns16550.h"
  18. //
  19. // --------------------------------------------------------------------- Macros
  20. //
  21. //
  22. // Macros to read from and write to 16550 registers.
  23. //
  24. #define NS16550_READ8(_Device, _Register) \
  25. ((PNS16550_READ8)(_Device)->Read8)((_Device), (_Register))
  26. #define NS16550_WRITE8(_Device, _Register, _Value) \
  27. ((PNS16550_WRITE8)(_Device)->Write8)((_Device), (_Register), (_Value))
  28. //
  29. // This macro returns the offset of a given register from its base.
  30. //
  31. #define NS16550_REGISTER_OFFSET(_Device, _Register) \
  32. ((_Device)->RegisterOffset + ((_Register) << (_Device)->RegisterShift))
  33. //
  34. // ---------------------------------------------------------------- Definitions
  35. //
  36. //
  37. // Define the bits for the PC UART Line Status register.
  38. //
  39. #define NS16550_LINE_STATUS_DATA_READY 0x01
  40. #define NS16550_LINE_STATUS_TRANSMIT_EMPTY 0x20
  41. #define NS16550_LINE_STATUS_ERRORS 0x8E
  42. //
  43. // ------------------------------------------------------ Data Type Definitions
  44. //
  45. typedef enum _NS16550_REGISTER {
  46. Ns16550Data = 0,
  47. Ns16550DivisorLow = 0,
  48. Ns16550InterruptEnable = 1,
  49. Ns16550DivisorHigh = 1,
  50. Ns16550InterruptStatus = 2,
  51. Ns16550FifoControl = 2,
  52. Ns16550LineControl = 3,
  53. Ns16550ModemControl = 4,
  54. Ns16550LineStatus = 5,
  55. Ns16550ModemStatus = 6,
  56. Ns16550Scratch = 7
  57. } NS16550_REGISTER, *PNS16550_REGISTER;
  58. typedef
  59. UINT8
  60. (*PNS16550_READ8) (
  61. PNS16550_CONTEXT Context,
  62. NS16550_REGISTER Register
  63. );
  64. /*++
  65. Routine Description:
  66. This routine reads a 16550 register.
  67. Arguments:
  68. Context - Supplies a pointer to the device context.
  69. Register - Supplies the register to read.
  70. Return Value:
  71. Returns the value at the register.
  72. --*/
  73. typedef
  74. VOID
  75. (*PNS16550_WRITE8) (
  76. PNS16550_CONTEXT Context,
  77. NS16550_REGISTER Register,
  78. UINT8 Value
  79. );
  80. /*++
  81. Routine Description:
  82. This routine writes to a 16550 register.
  83. Arguments:
  84. Context - Supplies a pointer to the device context.
  85. Register - Supplies the register to write to.
  86. Value - Supplies the value to write.
  87. Return Value:
  88. None.
  89. --*/
  90. //
  91. // ----------------------------------------------- Internal Function Prototypes
  92. //
  93. UINT8
  94. EfipNs16550ReadIo8 (
  95. PNS16550_CONTEXT Device,
  96. NS16550_REGISTER Register
  97. );
  98. VOID
  99. EfipNs16550WriteIo8 (
  100. PNS16550_CONTEXT Device,
  101. NS16550_REGISTER Register,
  102. UINT8 Value
  103. );
  104. UINT8
  105. EfipNs16550ReadMemory8 (
  106. PNS16550_CONTEXT Device,
  107. NS16550_REGISTER Register
  108. );
  109. VOID
  110. EfipNs16550WriteMemory8 (
  111. PNS16550_CONTEXT Device,
  112. NS16550_REGISTER Register,
  113. UINT8 Value
  114. );
  115. //
  116. // -------------------------------------------------------------------- Globals
  117. //
  118. //
  119. // ------------------------------------------------------------------ Functions
  120. //
  121. //
  122. // --------------------------------------------------------- Internal Functions
  123. //
  124. EFI_STATUS
  125. EfipNs16550ComputeDivisor (
  126. UINT32 BaseBaud,
  127. UINT32 BaudRate,
  128. UINT16 *Divisor
  129. )
  130. /*++
  131. Routine Description:
  132. This routine computes the divisor rates for a NS 16550 UART at a given baud
  133. rate.
  134. Arguments:
  135. BaseBaud - Supplies the baud rate for a divisor of 1.
  136. BaudRate - Supplies the desired baud rate.
  137. Divisor - Supplies a pointer where the divisor will be returned on success.
  138. Return Value:
  139. EFI_SUCCESS on success.
  140. EFI_UNSUPPORTED if the given baud rate cannot be achieved.
  141. --*/
  142. {
  143. UINT32 CurrentBaud;
  144. UINT32 LocalDivisor;
  145. //
  146. // Compute the baud rate divisor.
  147. //
  148. if (BaudRate > BaseBaud) {
  149. return EFI_UNSUPPORTED;
  150. }
  151. LocalDivisor = 1;
  152. while (TRUE) {
  153. CurrentBaud = BaseBaud / LocalDivisor;
  154. if ((CurrentBaud <= BaudRate) || (CurrentBaud == 0)) {
  155. break;
  156. }
  157. LocalDivisor += 1;
  158. }
  159. if ((CurrentBaud == 0) || (LocalDivisor > MAX_UINT16)) {
  160. return EFI_UNSUPPORTED;
  161. }
  162. *Divisor = (UINT16)LocalDivisor;
  163. return EFI_SUCCESS;
  164. }
  165. EFI_STATUS
  166. EfipNs16550Initialize (
  167. PNS16550_CONTEXT Context
  168. )
  169. /*++
  170. Routine Description:
  171. This routine initializes the NS 16550 serial port hardware. The caller
  172. should have initialized at least some of the context structure.
  173. Arguments:
  174. Context - Supplies the pointer to the port's initialized context.
  175. Return Value:
  176. EFI Status code.
  177. --*/
  178. {
  179. UINT8 Value;
  180. //
  181. // Determine the correct register access function.
  182. //
  183. if (Context->MemoryBase != NULL) {
  184. Context->Read8 = EfipNs16550ReadMemory8;
  185. Context->Write8 = EfipNs16550WriteMemory8;
  186. } else {
  187. Context->Read8 = EfipNs16550ReadIo8;
  188. Context->Write8 = EfipNs16550WriteIo8;
  189. }
  190. //
  191. // Begin programming the 16550 controller. The topmost bit in the line
  192. // control register turns the DLAB (Data Latch Address Byte) on. This
  193. // changes the meanings of the registers, allowing us to program the baud
  194. // rate divisor values.
  195. //
  196. Value = NS16550_READ8(Context, Ns16550LineControl);
  197. Value |= 0x80;
  198. NS16550_WRITE8(Context, Ns16550LineControl, Value);
  199. //
  200. // Set the divisor bytes. This programs the baud rate generator.
  201. //
  202. NS16550_WRITE8(Context,
  203. Ns16550DivisorLow,
  204. (UINT8)(Context->BaudRateDivisor & 0x00FF));
  205. NS16550_WRITE8(Context,
  206. Ns16550DivisorHigh,
  207. (UINT8)((Context->BaudRateDivisor >> 8) & 0x00FF));
  208. //
  209. // Now program the FIFO queue configuration. It is assumed that the FIFOs
  210. // are operational, which is not true on certain machines with very old
  211. // UARTs. Setting bit 0 enables the FIFO. Setting bits 1 and 2 clears both
  212. // FIFOs. Clearing bit 3 disables DMA mode. The top 4 bits vary depending
  213. // on the version. Setting bit 5 enables the 64 byte FIFO, which is only
  214. // available on 16750s. Bit 4 is reserved. Otherwise bits 4 and 5 are
  215. // either reserved or dictate the transmit FIFO's empty trigger. Bits 6 and
  216. // 7 set the receive FIFO's trigger, where setting both bits means that
  217. // "2 less than full", which for the default 16 byte FIFO means 14 bytes
  218. // are in the buffer.
  219. //
  220. Value = 0xC7;
  221. if ((Context->Flags & NS16550_FLAG_TRANSMIT_TRIGGER_2_CHARACTERS) != 0) {
  222. Value |= 0x10;
  223. } else if ((Context->Flags & NS16550_FLAG_64_BYTE_FIFO) != 0) {
  224. Value |= 0x20;
  225. }
  226. NS16550_WRITE8(Context, Ns16550FifoControl, Value);
  227. //
  228. // Now program the Line Control register again. Setting bits 0 and 1 sets
  229. // 8 data bits. Clearing bit 2 sets one stop bit. Clearing bit 3 sets no
  230. // parity. Additionally, clearing bit 7 turns the DLAB latch off, changing
  231. // the meaning of the registers back and allowing other control registers to
  232. // be accessed.
  233. //
  234. NS16550_WRITE8(Context, Ns16550LineControl, 0x03);
  235. //
  236. // Setting the Modem Control register to zero disables all hardware flow
  237. // control.
  238. //
  239. NS16550_WRITE8(Context, Ns16550ModemControl, 0x00);
  240. return EFI_SUCCESS;
  241. }
  242. EFI_STATUS
  243. EfipNs16550Transmit (
  244. PNS16550_CONTEXT Context,
  245. VOID *Data,
  246. UINT32 Size
  247. )
  248. /*++
  249. Routine Description:
  250. This routine writes data out the serial port. This routine should busily
  251. spin if the previously sent byte has not finished transmitting.
  252. Arguments:
  253. Context - Supplies the pointer to the port context.
  254. Data - Supplies a pointer to the data to write.
  255. Size - Supplies the size to write, in bytes.
  256. Return Value:
  257. EFI_SUCCESS on success.
  258. EFI_DEVICE_ERROR if a device error occurred.
  259. --*/
  260. {
  261. UINT32 ByteIndex;
  262. UINT8 *Bytes;
  263. UINT8 StatusRegister;
  264. Bytes = Data;
  265. for (ByteIndex = 0; ByteIndex < Size; ByteIndex += 1) {
  266. //
  267. // Spin waiting for the buffer to become ready to send. If an error is
  268. // detected, bail out and report to the caller.
  269. //
  270. do {
  271. StatusRegister = NS16550_READ8(Context, Ns16550LineStatus);
  272. if ((StatusRegister & NS16550_LINE_STATUS_ERRORS) != 0) {
  273. return EFI_DEVICE_ERROR;
  274. }
  275. } while ((StatusRegister & NS16550_LINE_STATUS_TRANSMIT_EMPTY) == 0);
  276. //
  277. // Send the byte and return.
  278. //
  279. NS16550_WRITE8(Context, Ns16550Data, Bytes[ByteIndex]);
  280. }
  281. return EFI_SUCCESS;
  282. }
  283. EFI_STATUS
  284. EfipNs16550Receive (
  285. PNS16550_CONTEXT Context,
  286. VOID *Data,
  287. UINT32 *Size
  288. )
  289. /*++
  290. Routine Description:
  291. This routine reads bytes from the serial port.
  292. Arguments:
  293. Context - Supplies the pointer to the port context.
  294. Data - Supplies a pointer where the read data will be returned on success.
  295. Size - Supplies a pointer that on input contains the size of the receive
  296. buffer. On output, returns the number of bytes read.
  297. Return Value:
  298. EFI_SUCCESS on success.
  299. EFI_NOT_READY if there was no data to be read at the current time.
  300. EFI_DEVICE_ERROR if a device error occurred.
  301. --*/
  302. {
  303. UINT32 ByteCount;
  304. UINT32 ByteIndex;
  305. UINT8 *Bytes;
  306. EFI_STATUS Status;
  307. UINT8 StatusRegister;
  308. ByteCount = *Size;
  309. Bytes = Data;
  310. Status = EFI_NOT_READY;
  311. for (ByteIndex = 0; ByteIndex < ByteCount; ByteIndex += 1) {
  312. StatusRegister = NS16550_READ8(Context, Ns16550LineStatus);
  313. if ((StatusRegister & NS16550_LINE_STATUS_ERRORS) != 0) {
  314. Status = EFI_DEVICE_ERROR;
  315. break;
  316. }
  317. if ((StatusRegister & NS16550_LINE_STATUS_DATA_READY) == 0) {
  318. break;
  319. }
  320. Bytes[ByteIndex] = NS16550_READ8(Context, Ns16550Data);
  321. Status = EFI_SUCCESS;
  322. }
  323. *Size = ByteIndex;
  324. return Status;
  325. }
  326. EFI_STATUS
  327. EfipNs16550GetStatus (
  328. PNS16550_CONTEXT Context,
  329. BOOLEAN *ReceiveDataAvailable
  330. )
  331. /*++
  332. Routine Description:
  333. This routine returns the current device status.
  334. Arguments:
  335. Context - Supplies a pointer to the serial port context.
  336. ReceiveDataAvailable - Supplies a pointer where a boolean will be returned
  337. indicating whether or not receive data is available.
  338. Return Value:
  339. EFI_SUCCESS on success.
  340. EFI_DEVICE_ERROR if a device error occurred.
  341. --*/
  342. {
  343. UINT8 StatusRegister;
  344. *ReceiveDataAvailable = FALSE;
  345. StatusRegister = NS16550_READ8(Context, Ns16550LineStatus);
  346. if ((StatusRegister & NS16550_LINE_STATUS_DATA_READY) != 0) {
  347. *ReceiveDataAvailable = TRUE;
  348. }
  349. return EFI_SUCCESS;
  350. }
  351. UINT8
  352. EfipNs16550ReadIo8 (
  353. PNS16550_CONTEXT Context,
  354. NS16550_REGISTER Register
  355. )
  356. /*++
  357. Routine Description:
  358. This routine reads a 16550 register from an I/O port.
  359. Arguments:
  360. Context - Supplies a pointer to the device context.
  361. Register - Supplies the register to read.
  362. Return Value:
  363. Returns the value at the register.
  364. --*/
  365. {
  366. UINT16 Port;
  367. Port = Context->IoBase + NS16550_REGISTER_OFFSET(Context, Register);
  368. return EfiIoPortIn8(Port);
  369. }
  370. VOID
  371. EfipNs16550WriteIo8 (
  372. PNS16550_CONTEXT Context,
  373. NS16550_REGISTER Register,
  374. UINT8 Value
  375. )
  376. /*++
  377. Routine Description:
  378. This routine writes to an I/O port based 16550 register.
  379. Arguments:
  380. Context - Supplies a pointer to the device context.
  381. Register - Supplies the register to write to.
  382. Value - Supplies the value to write.
  383. Return Value:
  384. None.
  385. --*/
  386. {
  387. UINT16 Port;
  388. Port = Context->IoBase + NS16550_REGISTER_OFFSET(Context, Register);
  389. EfiIoPortOut8(Port, Value);
  390. return;
  391. }
  392. UINT8
  393. EfipNs16550ReadMemory8 (
  394. PNS16550_CONTEXT Context,
  395. NS16550_REGISTER Register
  396. )
  397. /*++
  398. Routine Description:
  399. This routine reads a 16550 register from a memory mapped register.
  400. Arguments:
  401. Context - Supplies a pointer to the device context.
  402. Register - Supplies the register to read.
  403. Return Value:
  404. Returns the value at the register.
  405. --*/
  406. {
  407. VOID *Address;
  408. UINT8 Value;
  409. Address = Context->MemoryBase + NS16550_REGISTER_OFFSET(Context, Register);
  410. switch (Context->RegisterShift) {
  411. case NS16550_1_BYTE_REGISTER_SHIFT:
  412. Value = EfiReadRegister8(Address);
  413. break;
  414. case NS16550_2_BYTE_REGISTER_SHIFT:
  415. Value = EfiReadRegister16(Address);
  416. break;
  417. case NS16550_4_BYTE_REGISTER_SHIFT:
  418. default:
  419. Value = EfiReadRegister32(Address);
  420. break;
  421. }
  422. return Value;
  423. }
  424. VOID
  425. EfipNs16550WriteMemory8 (
  426. PNS16550_CONTEXT Context,
  427. NS16550_REGISTER Register,
  428. UINT8 Value
  429. )
  430. /*++
  431. Routine Description:
  432. This routine writes to a memory mapped 16550 register.
  433. Arguments:
  434. Context - Supplies a pointer to the device context.
  435. Register - Supplies the register to write to.
  436. Value - Supplies the value to write.
  437. Return Value:
  438. None.
  439. --*/
  440. {
  441. VOID *Address;
  442. Address = Context->MemoryBase + NS16550_REGISTER_OFFSET(Context, Register);
  443. switch (Context->RegisterShift) {
  444. case NS16550_1_BYTE_REGISTER_SHIFT:
  445. EfiWriteRegister8(Address, Value);
  446. break;
  447. case NS16550_2_BYTE_REGISTER_SHIFT:
  448. EfiWriteRegister16(Address, (UINT16)Value);
  449. break;
  450. case NS16550_4_BYTE_REGISTER_SHIFT:
  451. default:
  452. EfiWriteRegister32(Address, (UINT32)Value);
  453. break;
  454. }
  455. return;
  456. }