#include <kern/clock.h>
#include <kern/debug.h>
#include <libkern/OSBase.h>
#include <mach/mach_time.h>
#include <machine/atomic.h>
#include <machine/machine_routines.h>
#include <pexpert/pexpert.h>
#include <pexpert/protos.h>
#include <pexpert/device_tree.h>
#if defined __arm__
#include <arm/caches_internal.h>
#include <arm/machine_routines.h>
#include <arm/proc_reg.h>
#include <pexpert/arm/board_config.h>
#include <vm/pmap.h>
#elif defined __arm64__
#include <pexpert/arm/consistent_debug.h>
#include <pexpert/arm64/board_config.h>
#include <arm64/proc_reg.h>
#endif
struct pe_serial_functions {
void (*uart_init) (void);
void (*uart_set_baud_rate) (int unit, uint32_t baud_rate);
int (*tr0) (void);
void (*td0) (int c);
int (*rr0) (void);
int (*rd0) (void);
};
static struct pe_serial_functions *gPESF;
static int uart_initted = 0;
static vm_offset_t uart_base;
#ifdef S3CUART
static int32_t dt_pclk = -1;
static int32_t dt_sampling = -1;
static int32_t dt_ubrdiv = -1;
static void
ln2410_uart_init(void)
{
uint32_t ucon0 = 0x405;
rULCON0 = 0x03;
if (dt_pclk != -1) {
ucon0 = ucon0 & ~0x400;
}
rUCON0 = ucon0;
rUMCON0 = 0x00;
gPESF->uart_set_baud_rate(0, 115200);
rUFCON0 = 0x03;
rUMCON0 = 0x01;
}
static void
ln2410_uart_set_baud_rate(__unused int unit, uint32_t baud_rate)
{
uint32_t div = 0;
uint32_t uart_clock = 0;
uint32_t sample_rate = 16;
if (baud_rate < 300) {
baud_rate = 9600;
}
if (rUCON0 & 0x400) {
uart_clock = (uint32_t)gPEClockFrequencyInfo.fix_frequency_hz;
} else {
uart_clock = (uint32_t)gPEClockFrequencyInfo.prf_frequency_hz;
}
if (dt_sampling != -1) {
sample_rate = dt_sampling & 0xf;
}
if (dt_ubrdiv != -1) {
div = dt_ubrdiv & 0xffff;
} else {
div = uart_clock / (baud_rate * sample_rate);
uint32_t actual_baud = uart_clock / ((div + 0) * sample_rate);
uint32_t baud_low = uart_clock / ((div + 1) * sample_rate);
if ((baud_rate - baud_low) > (actual_baud - baud_rate)) {
div--;
}
}
rUBRDIV0 = ((16 - sample_rate) << 16) | div;
}
static int
ln2410_tr0(void)
{
return rUTRSTAT0 & 0x04;
}
static void
ln2410_td0(int c)
{
rUTXH0 = (unsigned)(c & 0xff);
}
static int
ln2410_rr0(void)
{
return rUTRSTAT0 & 0x01;
}
static int
ln2410_rd0(void)
{
return (int)rURXH0;
}
static struct pe_serial_functions ln2410_serial_functions = {
ln2410_uart_init, ln2410_uart_set_baud_rate,
ln2410_tr0, ln2410_td0, ln2410_rr0, ln2410_rd0
};
#endif
static unsigned int
read_dtr(void)
{
#ifdef __arm__
unsigned int c;
__asm__ volatile (
"mrc p14, 0, %0, c0, c5\n"
: "=r"(c));
return c;
#else
panic_unimplemented();
return 0;
#endif
}
static void
write_dtr(unsigned int c)
{
#ifdef __arm__
__asm__ volatile (
"mcr p14, 0, %0, c0, c5\n"
:
:"r"(c));
#else
(void)c;
panic_unimplemented();
#endif
}
static int
dcc_tr0(void)
{
#ifdef __arm__
return !(arm_debug_read_dscr() & ARM_DBGDSCR_TXFULL);
#else
panic_unimplemented();
return 0;
#endif
}
static void
dcc_td0(int c)
{
write_dtr(c);
}
static int
dcc_rr0(void)
{
#ifdef __arm__
return arm_debug_read_dscr() & ARM_DBGDSCR_RXFULL;
#else
panic_unimplemented();
return 0;
#endif
}
static int
dcc_rd0(void)
{
return read_dtr();
}
static struct pe_serial_functions dcc_serial_functions = {
NULL, NULL,
dcc_tr0, dcc_td0, dcc_rr0, dcc_rd0
};
#ifdef SHMCON
#define CPU_CACHELINE_SIZE (1 << MMU_CLINE)
#ifndef SHMCON_NAME
#define SHMCON_NAME "AP-xnu"
#endif
#define SHMCON_MAGIC 'SHMC'
#define SHMCON_VERSION 2
#define CBUF_IN 0
#define CBUF_OUT 1
#define INBUF_SIZE (panic_size / 16)
#define FULL_ALIGNMENT (64)
#define FLAG_CACHELINE_32 1
#define FLAG_CACHELINE_64 2
#define head_in sidx[CBUF_IN]
#define tail_in midx[CBUF_IN]
#define head_out midx[CBUF_OUT]
#define tail_out sidx[CBUF_OUT]
#define NUM_CHILDREN 5
#define WRAP_INCR(len, x) do{ (x)++; if((x) >= (len)) (x) = 0; } while(0)
#define ROUNDUP(a, b) (((a) + ((b) - 1)) & (~((b) - 1)))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define shmcon_barrier() do {__asm__ volatile("dmb ish" : : : "memory");} while(0)
struct shm_buffer_info {
uint64_t base;
uint32_t unused;
uint32_t magic;
};
struct shmcon_header {
uint32_t magic;
uint8_t version;
uint8_t children;
uint16_t flags;
uint64_t buf_paddr[2];
uint32_t buf_len[2];
uint8_t name[8];
uint32_t sidx[2] __attribute__((aligned(FULL_ALIGNMENT)));
uint32_t midx[2] __attribute__((aligned(FULL_ALIGNMENT)));
uint64_t child[0];
};
static volatile struct shmcon_header *shmcon = NULL;
static volatile uint8_t *shmbuf[2];
#ifdef SHMCON_THROTTLED
static uint64_t grace = 0;
static uint64_t full_timeout = 0;
#endif
static void
shmcon_set_baud_rate(__unused int unit, __unused uint32_t baud_rate)
{
return;
}
static int
shmcon_tr0(void)
{
#ifdef SHMCON_THROTTLED
uint32_t head = shmcon->head_out;
uint32_t tail = shmcon->tail_out;
uint32_t len = shmcon->buf_len[CBUF_OUT];
WRAP_INCR(len, head);
if (head != tail) {
full_timeout = 0;
return 1;
}
if (full_timeout == 0) {
full_timeout = mach_absolute_time() + grace;
return 0;
}
if (full_timeout > mach_absolute_time()) {
return 0;
}
tail += (len / 4);
if (tail >= len) {
tail -= len;
}
shmcon_barrier();
shmcon->tail_out = tail;
full_timeout = 0;
#endif
return 1;
}
static void
shmcon_td0(int c)
{
uint32_t head = shmcon->head_out;
uint32_t len = shmcon->buf_len[CBUF_OUT];
shmbuf[CBUF_OUT][head] = (uint8_t)c;
WRAP_INCR(len, head);
shmcon_barrier();
shmcon->head_out = head;
}
static int
shmcon_rr0(void)
{
if (shmcon->tail_in == shmcon->head_in) {
return 0;
}
return 1;
}
static int
shmcon_rd0(void)
{
int c;
uint32_t tail = shmcon->tail_in;
uint32_t len = shmcon->buf_len[CBUF_IN];
c = shmbuf[CBUF_IN][tail];
WRAP_INCR(len, tail);
shmcon_barrier();
shmcon->tail_in = tail;
return c;
}
static void
shmcon_init(void)
{
DTEntry entry;
uintptr_t *reg_prop;
volatile struct shm_buffer_info *end;
size_t i, header_size;
unsigned int size;
vm_offset_t pa_panic_base, panic_size, va_buffer_base, va_buffer_end;
if (kSuccess != DTLookupEntry(0, "pram", &entry)) {
return;
}
if (kSuccess != DTGetProperty(entry, "reg", (void **)®_prop, &size)) {
return;
}
pa_panic_base = reg_prop[0];
panic_size = reg_prop[1];
shmcon = (struct shmcon_header *)ml_map_high_window(pa_panic_base, panic_size);
header_size = sizeof(*shmcon) + (NUM_CHILDREN * sizeof(shmcon->child[0]));
va_buffer_base = ROUNDUP((uintptr_t)(shmcon) + header_size, CPU_CACHELINE_SIZE);
va_buffer_end = (uintptr_t)shmcon + panic_size - (sizeof(*end));
if ((shmcon->magic == SHMCON_MAGIC) && (shmcon->version == SHMCON_VERSION)) {
vm_offset_t pa_buffer_base, pa_buffer_end;
pa_buffer_base = ml_vtophys(va_buffer_base);
pa_buffer_end = ml_vtophys(va_buffer_end);
for (i = 0; i < 2; i++) {
vm_offset_t pa_buf;
uint32_t len;
pa_buf = (uintptr_t)shmcon->buf_paddr[i];
len = shmcon->buf_len[i];
if ((pa_buf < pa_buffer_base) ||
(pa_buf >= pa_buffer_end) ||
((pa_buf + len) > pa_buffer_end) ||
(shmcon->midx[i] >= len) ||
(shmcon->sidx[i] >= len) ||
(pa_buf != ROUNDUP(pa_buf, CPU_CACHELINE_SIZE)) ||
(len < 1024) ||
(len > (pa_buffer_end - pa_buffer_base)) ||
(shmcon->children != NUM_CHILDREN)) {
goto validation_failure;
}
shmbuf[i] = (uint8_t *)(uintptr_t)shmcon + ((uintptr_t)pa_buf - (uintptr_t)pa_panic_base);
}
if ((uintptr_t)shmbuf[0] < (uintptr_t)shmbuf[1]) {
if ((uintptr_t)(shmbuf[0] + shmcon->buf_len[0]) > (uintptr_t)shmbuf[1]) {
goto validation_failure;
}
} else {
if ((uintptr_t)(shmbuf[1] + shmcon->buf_len[1]) > (uintptr_t)shmbuf[0]) {
goto validation_failure;
}
}
shmcon->tail_in = shmcon->head_in;
shmcon_barrier();
} else {
validation_failure:
shmcon->magic = 0;
shmcon_barrier();
shmcon->buf_len[CBUF_IN] = (uint32_t)INBUF_SIZE;
shmbuf[CBUF_IN] = (uint8_t *)va_buffer_base;
shmbuf[CBUF_OUT] = (uint8_t *)ROUNDUP(va_buffer_base + INBUF_SIZE, CPU_CACHELINE_SIZE);
for (i = 0; i < 2; i++) {
shmcon->midx[i] = 0;
shmcon->sidx[i] = 0;
shmcon->buf_paddr[i] = (uintptr_t)ml_vtophys((vm_offset_t)shmbuf[i]);
}
shmcon->buf_len[CBUF_OUT] = (uint32_t)(va_buffer_end - (uintptr_t)shmbuf[CBUF_OUT]);
shmcon->version = SHMCON_VERSION;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-qual"
memset((void *)shmcon->name, ' ', sizeof(shmcon->name));
memcpy((void *)shmcon->name, SHMCON_NAME, MIN(sizeof(shmcon->name), strlen(SHMCON_NAME)));
#pragma clang diagnostic pop
for (i = 0; i < NUM_CHILDREN; i++) {
shmcon->child[0] = 0;
}
shmcon_barrier();
shmcon->magic = SHMCON_MAGIC;
}
end = (volatile struct shm_buffer_info *)va_buffer_end;
end->base = pa_panic_base;
end->unused = 0;
shmcon_barrier();
end->magic = SHMCON_MAGIC;
#ifdef SHMCON_THROTTLED
grace = gPEClockFrequencyInfo.timebase_frequency_hz;
#endif
PE_consistent_debug_register(kDbgIdConsoleHeaderAP, pa_panic_base, panic_size);
}
static struct pe_serial_functions shmcon_serial_functions =
{
.uart_init = shmcon_init,
.uart_set_baud_rate = shmcon_set_baud_rate,
.tr0 = shmcon_tr0,
.td0 = shmcon_td0,
.rr0 = shmcon_rr0,
.rd0 = shmcon_rd0
};
int
pe_shmcon_set_child(uint64_t paddr, uint32_t entry)
{
if (shmcon == NULL) {
return -1;
}
if (shmcon->children >= entry) {
return -1;
}
shmcon->child[entry] = paddr;
return 0;
}
#endif
#ifdef DOCKFIFO_UART
#define DOCKFIFO_WR_MAX_STALL_US (30*1000)
static uint64_t prev_dockfifo_drained_time; static uint64_t prev_dockfifo_spaces; static uint32_t dockfifo_capacity;
static uint64_t dockfifo_stall_grace;
static int
dockfifo_drain_on_stall()
{
if (mach_absolute_time() - prev_dockfifo_drained_time >= dockfifo_stall_grace) {
(void)rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 1);
prev_dockfifo_spaces++;
return 1;
}
return 0;
}
static int
dockfifo_uart_tr0(void)
{
uint32_t spaces = rDOCKFIFO_W_STAT(DOCKFIFO_UART_WRITE) & 0xffff;
if (spaces >= dockfifo_capacity || spaces > prev_dockfifo_spaces) {
prev_dockfifo_drained_time = mach_absolute_time();
}
prev_dockfifo_spaces = spaces;
return spaces || dockfifo_drain_on_stall();
}
static void
dockfifo_uart_td0(int c)
{
rDOCKFIFO_W_DATA(DOCKFIFO_UART_WRITE, 1) = (unsigned)(c & 0xff);
prev_dockfifo_spaces--; }
static int
dockfifo_uart_rr0(void)
{
return rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 0) & 0x7f;
}
static int
dockfifo_uart_rd0(void)
{
return (int)((rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 1) >> 8) & 0xff);
}
static void
dockfifo_uart_init(void)
{
nanoseconds_to_absolutetime(DOCKFIFO_WR_MAX_STALL_US * 1000, &dockfifo_stall_grace);
rDOCKFIFO_DRAIN(DOCKFIFO_UART_WRITE) = 0;
while (rDOCKFIFO_R_DATA(DOCKFIFO_UART_WRITE, 3) & 0x7F) {
;
}
dockfifo_capacity = rDOCKFIFO_W_STAT(DOCKFIFO_UART_WRITE) & 0xffff;
}
static struct pe_serial_functions dockfifo_uart_serial_functions =
{
.uart_init = dockfifo_uart_init,
.uart_set_baud_rate = NULL,
.tr0 = dockfifo_uart_tr0,
.td0 = dockfifo_uart_td0,
.rr0 = dockfifo_uart_rr0,
.rd0 = dockfifo_uart_rd0
};
#endif
#ifdef DOCKCHANNEL_UART
#define DOCKCHANNEL_WR_MAX_STALL_US (30*1000)
static vm_offset_t dock_agent_base;
static uint32_t max_dockchannel_drain_period;
static bool use_sw_drain;
static uint64_t prev_dockchannel_drained_time; static uint64_t prev_dockchannel_spaces; static uint64_t dockchannel_stall_grace;
static int
dockchannel_drain_on_stall()
{
if ((mach_absolute_time() - prev_dockchannel_drained_time) >= dockchannel_stall_grace) {
(void)rDOCKCHANNELS_DEV_RDATA1(DOCKCHANNEL_UART_CHANNEL);
prev_dockchannel_spaces++;
return 1;
}
return 0;
}
static int
dockchannel_uart_tr0(void)
{
if (use_sw_drain) {
uint32_t spaces = rDOCKCHANNELS_DEV_WSTAT(DOCKCHANNEL_UART_CHANNEL) & 0x1ff;
if (spaces > prev_dockchannel_spaces) {
prev_dockchannel_drained_time = mach_absolute_time();
}
prev_dockchannel_spaces = spaces;
return spaces || dockchannel_drain_on_stall();
} else {
return rDOCKCHANNELS_DEV_WSTAT(DOCKCHANNEL_UART_CHANNEL) & 0x1ff;
}
}
static void
dockchannel_uart_td0(int c)
{
rDOCKCHANNELS_DEV_WDATA1(DOCKCHANNEL_UART_CHANNEL) = (unsigned)(c & 0xff);
if (use_sw_drain) {
prev_dockchannel_spaces--; }
}
static int
dockchannel_uart_rr0(void)
{
return rDOCKCHANNELS_DEV_RDATA0(DOCKCHANNEL_UART_CHANNEL) & 0x7f;
}
static int
dockchannel_uart_rd0(void)
{
return (int)((rDOCKCHANNELS_DEV_RDATA1(DOCKCHANNEL_UART_CHANNEL) >> 8) & 0xff);
}
static void
dockchannel_uart_init(void)
{
if (use_sw_drain) {
nanoseconds_to_absolutetime(DOCKCHANNEL_WR_MAX_STALL_US * NSEC_PER_USEC, &dockchannel_stall_grace);
}
rDOCKCHANNELS_AGENT_AP_INTR_CTRL &= ~(0x3);
rDOCKCHANNELS_AGENT_AP_INTR_STATUS |= 0x3;
rDOCKCHANNELS_AGENT_AP_ERR_INTR_CTRL &= ~(0x3);
rDOCKCHANNELS_AGENT_AP_ERR_INTR_STATUS |= 0x3;
rDOCKCHANNELS_DEV_DRAIN_CFG(DOCKCHANNEL_UART_CHANNEL) = max_dockchannel_drain_period;
rDOCKCHANNELS_DOCK_RDATA1(DOCKCHANNEL_UART_CHANNEL);
}
static struct pe_serial_functions dockchannel_uart_serial_functions =
{
.uart_init = dockchannel_uart_init,
.uart_set_baud_rate = NULL,
.tr0 = dockchannel_uart_tr0,
.td0 = dockchannel_uart_td0,
.rr0 = dockchannel_uart_rr0,
.rd0 = dockchannel_uart_rd0
};
#endif
#ifdef PI3_UART
vm_offset_t pi3_gpio_base_vaddr;
vm_offset_t pi3_aux_base_vaddr;
static int
pi3_uart_tr0(void)
{
return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x20;
}
static void
pi3_uart_td0(int c)
{
BCM2837_PUT32(BCM2837_AUX_MU_IO_REG_V, (uint32_t) c);
}
static int
pi3_uart_rr0(void)
{
return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x01;
}
static int
pi3_uart_rd0(void)
{
return (int) BCM2837_GET32(BCM2837_AUX_MU_IO_REG_V) & 0xff;
}
static void
pi3_uart_init(void)
{
uint32_t i;
BCM2837_PUT32(BCM2837_AUX_ENABLES_V, 1);
BCM2837_PUT32(BCM2837_AUX_MU_CNTL_REG_V, 0);
BCM2837_PUT32(BCM2837_AUX_MU_LCR_REG_V, 3);
BCM2837_PUT32(BCM2837_AUX_MU_MCR_REG_V, 0);
BCM2837_PUT32(BCM2837_AUX_MU_IER_REG_V, 0);
BCM2837_PUT32(BCM2837_AUX_MU_IIR_REG_V, 0xC6);
BCM2837_PUT32(BCM2837_AUX_MU_BAUD_REG_V, 270);
i = BCM2837_FSEL_REG(14);
i &= ~(BCM2837_FSEL_MASK(14));
i |= (BCM2837_FSEL_ALT5 << BCM2837_FSEL_OFFS(14));
i &= ~(BCM2837_FSEL_MASK(15));
i |= (BCM2837_FSEL_ALT5 << BCM2837_FSEL_OFFS(15));
BCM2837_PUT32(BCM2837_FSEL_REG(14), i);
BCM2837_PUT32(BCM2837_GPPUD_V, 0);
__builtin_arm_isb(ISB_SY);
for (i = 0; i < 150; i++) {
asm volatile ("add x0, x0, xzr");
}
__builtin_arm_isb(ISB_SY);
BCM2837_PUT32(BCM2837_GPPUDCLK0_V, (1 << 14) | (1 << 15));
__builtin_arm_isb(ISB_SY);
for (i = 0; i < 150; i++) {
asm volatile ("add x0, x0, xzr");
}
__builtin_arm_isb(ISB_SY);
BCM2837_PUT32(BCM2837_GPPUDCLK0_V, 0);
BCM2837_PUT32(BCM2837_AUX_MU_CNTL_REG_V, 3);
}
static struct pe_serial_functions pi3_uart_serial_functions =
{
.uart_init = pi3_uart_init,
.uart_set_baud_rate = NULL,
.tr0 = pi3_uart_tr0,
.td0 = pi3_uart_td0,
.rr0 = pi3_uart_rr0,
.rd0 = pi3_uart_rd0
};
#endif
int
serial_init(void)
{
DTEntry entryP = NULL;
uint32_t prop_size, dccmode;
vm_offset_t soc_base;
uintptr_t *reg_prop;
uint32_t *prop_value = NULL;
char *serial_compat = 0;
#ifdef SHMCON
uint32_t jconmode;
#endif
#ifdef DOCKFIFO_UART
uint32_t no_dockfifo_uart;
#endif
#ifdef DOCKCHANNEL_UART
uint32_t no_dockchannel_uart;
#endif
#ifdef PI3_UART
uint32_t is_pi3;
#endif
if (uart_initted && gPESF) {
gPESF->uart_init();
kprintf("reinit serial\n");
return 1;
}
dccmode = 0;
if (PE_parse_boot_argn("dcc", &dccmode, sizeof(dccmode))) {
gPESF = &dcc_serial_functions;
uart_initted = 1;
return 1;
}
#ifdef SHMCON
jconmode = 0;
if (PE_parse_boot_argn("jcon", &jconmode, sizeof jconmode)) {
gPESF = &shmcon_serial_functions;
gPESF->uart_init();
uart_initted = 1;
return 1;
}
#endif
#ifdef PI3_UART
#pragma unused(prop_value)
is_pi3 = 0;
if (PE_parse_boot_argn("-pi3", &is_pi3, sizeof(is_pi3))) { pi3_gpio_base_vaddr = ml_io_map((vm_offset_t)BCM2837_GPIO_BASE, BCM2837_GPIO_SIZE);
pi3_aux_base_vaddr = ml_io_map((vm_offset_t)BCM2837_AUX_BASE, BCM2837_AUX_SIZE);
gPESF = &pi3_uart_serial_functions;
gPESF->uart_init();
uart_initted = 1;
return 1;
}
#endif
soc_base = pe_arm_get_soc_base_phys();
if (soc_base == 0) {
return 0;
}
#ifdef DOCKFIFO_UART
no_dockfifo_uart = 0;
PE_parse_boot_argn("no-dockfifo-uart", &no_dockfifo_uart, sizeof(no_dockfifo_uart));
if (no_dockfifo_uart == 0) {
if (DTFindEntry("name", "dockfifo-uart", &entryP) == kSuccess) {
DTGetProperty(entryP, "reg", (void **)®_prop, &prop_size);
uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
} else {
return 0;
}
gPESF = &dockfifo_uart_serial_functions;
gPESF->uart_init();
uart_initted = 1;
return 1;
}
#endif
#ifdef DOCKCHANNEL_UART
no_dockchannel_uart = 0;
PE_parse_boot_argn("no-dockfifo-uart", &no_dockchannel_uart, sizeof(no_dockchannel_uart));
if (no_dockchannel_uart == 0) {
if (DTFindEntry("name", "dockchannel-uart", &entryP) == kSuccess) {
DTGetProperty(entryP, "reg", (void **)®_prop, &prop_size);
if (prop_size / sizeof(uintptr_t) != 4) {
panic("Malformed dockchannel-uart property");
}
uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
dock_agent_base = ml_io_map(soc_base + *(reg_prop + 2), *(reg_prop + 3));
gPESF = &dockchannel_uart_serial_functions;
DTGetProperty(entryP, "max-aop-clk", (void **)&prop_value, &prop_size);
max_dockchannel_drain_period = (uint32_t)((prop_value)? (*prop_value * 0.03) : DOCKCHANNEL_DRAIN_PERIOD);
DTGetProperty(entryP, "enable-sw-drain", (void **)&prop_value, &prop_size);
use_sw_drain = (prop_value)? *prop_value : 0;
gPESF->uart_init();
uart_initted = 1;
return 1;
}
}
#endif
if (DTFindEntry("boot-console", NULL, &entryP) == kSuccess) {
DTGetProperty(entryP, "reg", (void **)®_prop, &prop_size);
uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
if (serial_compat == 0) {
DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
}
} else if (DTFindEntry("name", "uart0", &entryP) == kSuccess) {
DTGetProperty(entryP, "reg", (void **)®_prop, &prop_size);
uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
if (serial_compat == 0) {
DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
}
} else if (DTFindEntry("name", "uart1", &entryP) == kSuccess) {
DTGetProperty(entryP, "reg", (void **)®_prop, &prop_size);
uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
if (serial_compat == 0) {
DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
}
}
#ifdef S3CUART
if (NULL != entryP) {
DTGetProperty(entryP, "pclk", (void **)&prop_value, &prop_size);
if (prop_value) {
dt_pclk = *prop_value;
}
prop_value = NULL;
DTGetProperty(entryP, "sampling", (void **)&prop_value, &prop_size);
if (prop_value) {
dt_sampling = *prop_value;
}
prop_value = NULL;
DTGetProperty(entryP, "ubrdiv", (void **)&prop_value, &prop_size);
if (prop_value) {
dt_ubrdiv = *prop_value;
}
}
if (!strcmp(serial_compat, "uart,16550")) {
gPESF = &ln2410_serial_functions;
} else if (!strcmp(serial_compat, "uart-16550")) {
gPESF = &ln2410_serial_functions;
} else if (!strcmp(serial_compat, "uart,s5i3000")) {
gPESF = &ln2410_serial_functions;
} else if (!strcmp(serial_compat, "uart-1,samsung")) {
gPESF = &ln2410_serial_functions;
}
#elif defined (ARM_BOARD_CONFIG_MV88F6710)
if (!strcmp(serial_compat, "uart16x50,mmio")) {
gPESF = &uart16x50_serial_functions;
}
#endif
else {
return 0;
}
gPESF->uart_init();
uart_initted = 1;
return 1;
}
void
uart_putc(char c)
{
if (uart_initted) {
while (!gPESF->tr0()) {
;
}
gPESF->td0(c);
}
}
int
uart_getc(void)
{
if (uart_initted) {
if (!gPESF->rr0()) {
return -1;
}
return gPESF->rd0();
}
return -1;
}