I want to program a little "hello world" bare metal application on the Intel Galileo board. Using UEFI to print out text (to UART-1) works well, of course, but I want to access the UART "manually", without any help from UEFI.
In QEMU my code works well:
.h file
#define COM1_PORT (0x03F8)
#define UART_PORT (COM1_PORT)
enum uart_port_offs_t
{ // DLAB RW
THR = 0, // 0 W Transmitter Holding Buffer
RBR = 0, // 0 R Receiver Buffer
DLL = 0, // 1 RW Divisor Latch Low Byte
IER = 1, // 0 RW Interrupt Enable Register
DLH = 1, // 1 RW Divisor Latch High Byte
IIR = 2, // - R Interrupt Identification Register
FCR = 2, // - RW FIFO Control Register
LCR = 3, // - RW Line Control Register
MCR = 4, // - RW Modem Control Register
LSR = 5, // - R Line Status Register
MSR = 6, // - R Modem Status Register
SR = 7, // - RW Scratch Register
};
.c file
void uart_init(void)
{
outb(UART_PORT + IER, 0x00); // Disable all interrupts
outb(UART_PORT + LCR, LCR_DLAB);
outb(UART_PORT + DLL, BAUD_LL); // Set divisor (lo byte)
outb(UART_PORT + DLH, BAUD_HL); // (hi byte)
outb(UART_PORT + LCR, LCR_WORD_BITS_8 | LCR_PAR_NONE | LCR_STOP_BITS_1);
outb(UART_PORT + FCR, FCR_ENABLE | FCR_CLR_RECV | FCR_CLR_SEND | FCR_TRIGGER_16);
outb(UART_PORT + MCR, MCR_DSR | MCR_RTS | MCR_AUX2);
}
ssize_t uart_write(const char *buf, size_t len)
{
size_t written = 0;
while (written < len) {
while (!is_output_empty()) {
asm volatile ("pause");
}
outb(UART_PORT + THR, buf[written]);
++written;
}
return written;
}
main
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Exiting EFI boot services ...\r\n");
SystemTable->BootServices->ExitBootServices(ImageHandle, map_key);
uart_init();
while (1) {
const char s[] = "UART\r\n";
uart_write(s, sizeof (s) - 1);
}
The specs did not help me very much. I guess that the UARTs on the Intel Galileo board don't use/emulate the normal/legacy COM ports 3F8h, 2F8h, 3E8h, or 2E8h.
Can anyone tell me what I am doing wrong, or even post a minimal bare metal hello world example?
I assume you are aiming at the serial port that is the "audio-like" connector on the Intel Galileo board.
Here are a few resources that should help:
- Galileo schematic
- Intel Quark SoC X1000 datasheet
- Intel Galileo IO Mapping
- Sergey's blog entry about Configuring the Serial Port for Galileo
- Intel Quark Board Support Package downloads, including
- Board Support Package Sources (currently ver.1.0.0)
- Intel Quark SoC X1000 UEFI Firmware Writer’s Guide
Things to note about this UART:
- This serial port comes out of the QUARK chip as UART1 (see the schematics).
- There are a few GPIOs that you may need to manipulate (see Sergey's blog for doing this in Linux):
- gpio4: This GPIO controls level shifter for UART signals and some other signals connected to Quark SoC, such as SPI and fast I/O. Writing '1' to this GPIO enables level shifter.
- gpio40: This GPIO controls multiplexer for pin 0. Writing '0' to this GPIO connects pin 0 to UART's RxD (receive data) signal.
- gpio41: This GPIO controls multiplexer for pin 1. Writing '0' to this GPIO connects pin 1 to UART's TxD (transmit data) signal.
- Check the chapter 18 (High Speed UART) in the Quark datasheet for what to put in the UART registers:
- Registers DLH, DLL specify the baud rate
- Decide whether you want the DMA mode (chapter 18.3.1), the FIFO-interrupt mode (chapter 18.3.2), or the FIFO-polling mode (chapter 18.3.3). The latter is simpler but less effective, IMHO. The former requires you to configure DMA properly as well.
Since there is quite a bit to read for chapter 18 (~67 pages of useful information), I'm not going to retype all that here, please read the datasheet and configure the registers accordingly.
General notes:
For bare-metal approach first make sure that your boot procedure is correct, configuring all the clocking options, GPIO default modes and values, timers if any, etc. For Boot checklist read chapter 4.12 in X1000 UEFI Firmware Writer’s Guide (~18 things to do to boot this chip). After that I'd verify it with a simple "LED blinking" application on a GPIO.
Tinkering with 3F8h and similar ports is not going to help on "bare metal" of this SoC. You need to deal with the registers directly, or find and use appropriate library or framework (maybe UEFI BIOS?).
Programming sources for the particular platform should be a good read for examples.
For example, in Board Support Package Sources for Intel Quark the archive Quark_EDKII_v1.0.0.tar.gz
is the UEFI source code for Quark/Galileo. It there, the Serial.c and Serial.h files might just be what you are looking for:
Quark_EDKII_v1.0.0/QuarkSocPkg/QuarkSouthCluster/Uart/Dxe/Serial.*