Created
May 8, 2018 15:34
-
-
Save ktemkin/5426fc5464d690d782d27544b9c61f86 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include "registers.h" | |
#define UART_PROVIDES_PUTS_PUTC | |
#define CAR_BASE 0x60006000UL | |
#define PINMUX_BASE 0x70003000UL | |
#define HEX_CHAR(x) ((((x) + '0') > '9') ? ((x) + '7') : ((x) + '0')) | |
typedef struct { | |
/* uart itself */ | |
char * name; | |
uint64_t base; | |
/* clock and reset */ | |
uint32_t car_mask; | |
uint32_t car_clock_offset; | |
uint32_t car_reset_offset; | |
uint32_t car_source_offset; | |
/* pinmux */ | |
uint32_t pinmux_tx_offset; | |
/* tx offset is always +1 rx_offset */ | |
/* inversion settings */ | |
// the switch inverts these when communciating with its joycons; we may want to as well | |
int inversion_settings; | |
} uart_t; | |
static const uart_t uarts[] = { | |
/* UARTs */ | |
{ .name = "UART-A", .base = 0x70006000ULL, .car_clock_offset = 0x10, .car_reset_offset = 0x04, | |
.car_mask = (1 << 6), .car_source_offset = 0x178, .pinmux_tx_offset = 0x0e4}, | |
{ .name = "UART-B", .base = 0x70006040ULL, .car_clock_offset = 0x10, .car_reset_offset = 0x04, | |
.car_mask = (1 << 7), .car_source_offset = 0x17c, .pinmux_tx_offset = 0x0f4}, | |
{ .name = "UART-C", .base = 0x70006200ULL, .car_clock_offset = 0x14, .car_reset_offset = 0x08, | |
.car_mask = (1 << 23), .car_source_offset = 0x1a0, .pinmux_tx_offset = 0x104, .inversion_settings = 0b11}, | |
{ .name = "UART-D", .base = 0x70006300ULL, .car_clock_offset = 0x18, .car_reset_offset = 0x0c, | |
.car_mask = (1 << 1), .car_source_offset = 0x1c0, .pinmux_tx_offset = 0x114}, | |
}; | |
static const uart_t *uart; | |
enum { | |
UART_THR_DLAB = 0x00, | |
UART_IER_DLAB = 0x04, | |
UART_IIR_FCR = 0x08, | |
UART_LCR = 0x0C, | |
UART_LSR = 0x14, | |
UART_IRDA_CSR = 0x20 | |
}; | |
enum { | |
UART_RATE_115200 = (408000000/115200/16), /* based on 408000000 PLLP */ | |
FCR_TX_CLR = 0x4, /* bit 2 of FCR : clear TX FIFO */ | |
FCR_RX_CLR = 0x2, /* bit 1 of FCR : clear RX FIFO */ | |
FCR_EN_FIFO = 0x1, /* bit 0 of FCR : enable TX & RX FIFO */ | |
LCR_DLAB = 0x80, /* bit 7 of LCR : Divisor Latch Access Bit */ | |
LCR_WD_SIZE_8 = 0x3, /* bit 1:0 of LCR : word length of 8 */ | |
}; | |
static void reset_using_pmc() | |
{ | |
uint32_t *reset; | |
reset = (uint32_t *)0x7000e400; | |
*reset |= (1 << 4); | |
} | |
static inline unsigned long div_round_up(unsigned int n, unsigned int d) | |
{ | |
return (n + d - 1) / d; | |
} | |
/** | |
* Enables a given UART, and selects it for use in the UART functions. | |
*/ | |
void enable_uart(int uart_number) | |
{ | |
/* Set the active uart. */ | |
uart = &uarts[uart_number]; | |
/* Enable UART clock */ | |
reg_set(CAR_BASE, uart->car_clock_offset, uart->car_mask); | |
/* Reset and unreset UART */ | |
reg_set( CAR_BASE, uart->car_reset_offset, uart->car_mask); | |
reg_clear(CAR_BASE, uart->car_reset_offset, uart->car_mask); | |
/* Program UART clock source: PLLP (408000000) */ | |
reg_write(CAR_BASE, uart->car_source_offset, 0); | |
/* Program 115200n8 to the uart port */ | |
/* baud-rate of 115200 */ | |
reg_set( uart->base, UART_LCR, LCR_DLAB); | |
reg_write(uart->base, UART_THR_DLAB, (UART_RATE_115200 & 0xff)); | |
reg_write(uart->base, UART_IER_DLAB, (UART_RATE_115200 >> 8)); | |
reg_clear(uart->base, UART_LCR, LCR_DLAB); | |
/* 8-bit and no */ | |
reg_write(uart->base, UART_LCR, LCR_WD_SIZE_8); | |
reg_write(uart->base, UART_IRDA_CSR, uart->inversion_settings); | |
/* Handle pin multiplexing. */ | |
reg_write(PINMUX_BASE, uart->pinmux_tx_offset, 0b0001000); | |
reg_write(PINMUX_BASE, uart->pinmux_tx_offset + 4, 0b1001000); | |
/* ensure the TX fifos are up */ | |
reg_write(uart->base, UART_IIR_FCR, 0); | |
} | |
/** | |
* Returns true iff the UART buffer is ready to transmit. | |
*/ | |
static int uart_buffer_available() | |
{ | |
uint32_t holding_reg = reg_read(uart->base, UART_LSR); | |
return ((holding_reg >> 5) & 0x01); | |
} | |
/** | |
* Prints a single character (synchronously) via serial. | |
* | |
* @param c The character to be printed | |
*/ | |
void uart_putc(char c) | |
{ | |
// If we're about to send a newline, prefix it with a carriage return. | |
// This makes our putc behave like a normal console putc. | |
if(c == '\n') | |
uart_putc('\r'); | |
// Wait for the holding register to become available. | |
while(!uart_buffer_available()); | |
// Stick data in the holding register... | |
reg_write(uart->base, UART_THR_DLAB, c); | |
} | |
/** | |
* Prints a string (synchronously) via serial. | |
* | |
* @param s The string to be printed; must be null terminated. | |
*/ | |
int uart_puts(const char * s) | |
{ | |
while(*s) { | |
uart_putc(*s); | |
++s; | |
} | |
return 0; | |
} | |
/* send an 8 bit byte as two HEX characters to the console */ | |
void dump_byte(uint8_t b) | |
{ | |
uart_putc(HEX_CHAR((b >> 4) & 0xf)); | |
uart_putc(HEX_CHAR(b & 0xf)); | |
} | |
void dump_word(uint32_t w) | |
{ | |
dump_byte(w >> 8); | |
dump_byte(w & 0xff); | |
} | |
void dump_dword(uint32_t d) | |
{ | |
dump_word(d >> 16); | |
dump_word(d & 0xffff); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
enum { | |
UART_A = 0, | |
UART_B = 1, | |
UART_C = 2, | |
UART_D = 3, | |
}; | |
/** | |
* Enables a given UART, and selects it for use in the UART functions. | |
*/ | |
void enable_uart(int uart); | |
/** | |
* Prints a string (synchronously) via serial. | |
* | |
* @param s The string to be printed; must be null terminated. | |
*/ | |
int uart_puts(const char * s); | |
/** | |
* Prints a single character (synchronously) via serial. | |
* | |
* @param c The character to be printed | |
*/ | |
void uart_putc(char c); | |
/* send an 8 bit byte as two HEX characters to the console */ | |
void dump_byte(uint8_t b); | |
void dump_word(uint32_t w); | |
void dump_dword(uint32_t d); | |
#ifdef UART_PROVIDES_PUTS_PUTC | |
static inline int puts(const char * s) | |
{ | |
return uart_puts(s); | |
} | |
static inline void putc(char c) | |
{ | |
uart_putc(c); | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment