forked from cores/microwatt
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
2.4 KiB
C
136 lines
2.4 KiB
C
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "console.h"
|
|
|
|
/*
|
|
* Core UART functions to implement for a port
|
|
*/
|
|
|
|
static uint64_t potato_uart_base;
|
|
|
|
#define PROC_FREQ 100000000
|
|
#define UART_FREQ 115200
|
|
#define UART_BASE 0xc0002000
|
|
|
|
#define POTATO_CONSOLE_TX 0x00
|
|
#define POTATO_CONSOLE_RX 0x08
|
|
#define POTATO_CONSOLE_STATUS 0x10
|
|
#define POTATO_CONSOLE_STATUS_RX_EMPTY 0x01
|
|
#define POTATO_CONSOLE_STATUS_TX_EMPTY 0x02
|
|
#define POTATO_CONSOLE_STATUS_RX_FULL 0x04
|
|
#define POTATO_CONSOLE_STATUS_TX_FULL 0x08
|
|
#define POTATO_CONSOLE_CLOCK_DIV 0x18
|
|
#define POTATO_CONSOLE_IRQ_EN 0x20
|
|
|
|
static uint64_t potato_uart_reg_read(int offset)
|
|
{
|
|
uint64_t val;
|
|
|
|
__asm__ volatile("ldcix %0,%1,%2" : "=r" (val) : "b" (potato_uart_base), "r" (offset));
|
|
|
|
return val;
|
|
}
|
|
|
|
static void potato_uart_reg_write(int offset, uint64_t val)
|
|
{
|
|
__asm__ volatile("stdcix %0,%1,%2" : : "r" (val), "b" (potato_uart_base), "r" (offset));
|
|
}
|
|
|
|
static int potato_uart_rx_empty(void)
|
|
{
|
|
uint64_t val;
|
|
|
|
val = potato_uart_reg_read(POTATO_CONSOLE_STATUS);
|
|
|
|
if (val & POTATO_CONSOLE_STATUS_RX_EMPTY)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int potato_uart_tx_full(void)
|
|
{
|
|
uint64_t val;
|
|
|
|
val = potato_uart_reg_read(POTATO_CONSOLE_STATUS);
|
|
|
|
if (val & POTATO_CONSOLE_STATUS_TX_FULL)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char potato_uart_read(void)
|
|
{
|
|
uint64_t val;
|
|
|
|
val = potato_uart_reg_read(POTATO_CONSOLE_RX);
|
|
|
|
return (char)(val & 0x000000ff);
|
|
}
|
|
|
|
static void potato_uart_write(char c)
|
|
{
|
|
uint64_t val;
|
|
|
|
val = c;
|
|
|
|
potato_uart_reg_write(POTATO_CONSOLE_TX, val);
|
|
}
|
|
|
|
static unsigned long potato_uart_divisor(unsigned long proc_freq, unsigned long uart_freq)
|
|
{
|
|
return proc_freq / (uart_freq * 16) - 1;
|
|
}
|
|
|
|
void potato_uart_init(void)
|
|
{
|
|
potato_uart_base = UART_BASE;
|
|
|
|
potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, potato_uart_divisor(PROC_FREQ, UART_FREQ));
|
|
}
|
|
|
|
void potato_uart_irq_en(void)
|
|
{
|
|
potato_uart_reg_write(POTATO_CONSOLE_IRQ_EN, 0xff);
|
|
}
|
|
|
|
void potato_uart_irq_dis(void)
|
|
{
|
|
potato_uart_reg_write(POTATO_CONSOLE_IRQ_EN, 0x00);
|
|
}
|
|
|
|
int getchar(void)
|
|
{
|
|
while (potato_uart_rx_empty())
|
|
/* Do nothing */ ;
|
|
|
|
return potato_uart_read();
|
|
}
|
|
|
|
void putchar(unsigned char c)
|
|
{
|
|
while (potato_uart_tx_full())
|
|
/* Do Nothing */;
|
|
|
|
potato_uart_write(c);
|
|
}
|
|
|
|
void putstr(const char *str, unsigned long len)
|
|
{
|
|
for (unsigned long i = 0; i < len; i++) {
|
|
putchar(str[i]);
|
|
}
|
|
}
|
|
|
|
size_t strlen(const char *s)
|
|
{
|
|
size_t len = 0;
|
|
|
|
while (*s++)
|
|
len++;
|
|
|
|
return len;
|
|
}
|