/* References: https://wiki.osdev.org/Raspberry_Pi_Bare_Bones */ #include #include #include enum { // The base address for UART. // For raspi4 0xFE201000, raspi2 & 3 0x3F201000, and 0x20201000 for raspi1. UART0_BASE = (GPIO_BASE + 0x1000), // The offsets for reach register for the UART. UART0_DR = (UART0_BASE + 0x00), UART0_RSRECR = (UART0_BASE + 0x04), UART0_FR = (UART0_BASE + 0x18), UART0_ILPR = (UART0_BASE + 0x20), UART0_IBRD = (UART0_BASE + 0x24), UART0_FBRD = (UART0_BASE + 0x28), UART0_LCRH = (UART0_BASE + 0x2C), UART0_CR = (UART0_BASE + 0x30), UART0_IFLS = (UART0_BASE + 0x34), UART0_IMSC = (UART0_BASE + 0x38), UART0_RIS = (UART0_BASE + 0x3C), UART0_MIS = (UART0_BASE + 0x40), UART0_ICR = (UART0_BASE + 0x44), UART0_DMACR = (UART0_BASE + 0x48), UART0_ITCR = (UART0_BASE + 0x80), UART0_ITIP = (UART0_BASE + 0x84), UART0_ITOP = (UART0_BASE + 0x88), UART0_TDR = (UART0_BASE + 0x8C), // The offsets for Mailbox registers. MBOX_BASE = 0xB880, MBOX_READ = (MBOX_BASE + 0x00), MBOX_STATUS = (MBOX_BASE + 0x18), MBOX_WRITE = (MBOX_BASE + 0x20) }; // A mailbox message with set clock rate of PL011 to 3MHz tag. static volatile unsigned int __attribute__((aligned(16))) mbox[9] = { 9*4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0 }; // Loop times in a way that the compiler won't optimize away. static inline void delay(int32_t count) { asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n" : "=r"(count): [count]"0"(count) : "cc"); } void uart_init(int raspi) { // Disable UART0. mmio_write(UART0_CR, 0x00000000); // Set up GPIO pins 14 and 15. // Disable pull up/down for all GPIO pins. mmio_write(GPPUD, 0x00000000); delay(150); // Disable pull up/down for pins 14-15. mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15)); delay(150); // Write 0 to GPPUDCLK0 to make it take effect. mmio_write(GPPUDCLK0, 0x00000000); // Clear pending interrupts. mmio_write(UART0_ICR, 0x7FF); // Set integer & fractional part of baud rate. // Divider = UART_CLOCK/(16 * Baud) // Fraction part register = (Fractional part * 64) + 0.5 // Baud = 115200. // For Raspi3 and 4 the UART_CLOCK is system-clock dependent by default. // Set it to 3Mhz so that we can consistently set the baud rate if (raspi >= 3) { // UART_CLOCK = 30000000; unsigned int r = (unsigned int) (((uint64_t)(&mbox) & ~0xF) | 8); // Wait until we can talk to the VC. while (mmio_read(MBOX_STATUS) & 0x80000000); // Send our message to property channel and wait for the response. mmio_write(MBOX_WRITE, r); while ((mmio_read(MBOX_STATUS) & 0x40000000) || (mmio_read(MBOX_READ) != r)); } // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. mmio_write(UART0_IBRD, 1); // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. mmio_write(UART0_FBRD, 40); // Enable FIFO & 8 bit data transmission (1 stop bit, no parity). mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); // Disable all interrupts. mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)); // Enable UART0, receive & transfer part of UART. mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9)); } void uart_putc(unsigned char c) { // Wait for UART to become ready to transmit. while (mmio_read(UART0_FR) & (1 << 5)); mmio_write(UART0_DR, c); } unsigned char uart_getc() { // Wait for UART to have received something. while (mmio_read(UART0_FR) & (1 << 4)); return mmio_read(UART0_DR); } void uart_write(const void* buffer, size_t length) { const uint8_t* bytes = buffer; for (size_t i = 0; i < length; ++i) { uart_putc(*bytes++); } } void uart_print(const char* string) { while (*string) { uart_putc(*string++); } }