#include <pexpert/protos.h>
#include <pexpert/pexpert.h>
enum {
COM1_PORT_ADDR = 0x3f8,
COM2_PORT_ADDR = 0x2f8
};
enum {
UART_RBR = 0,
UART_THR = 0,
UART_DLL = 0,
UART_IER = 1,
UART_DLM = 1,
UART_IIR = 2,
UART_FCR = 2,
UART_LCR = 3,
UART_MCR = 4,
UART_LSR = 5,
UART_MSR = 6,
UART_SCR = 7
};
enum {
UART_LCR_8BITS = 0x03,
UART_LCR_DLAB = 0x80
};
enum {
UART_MCR_DTR = 0x01,
UART_MCR_RTS = 0x02,
UART_MCR_OUT1 = 0x04,
UART_MCR_OUT2 = 0x08,
UART_MCR_LOOP = 0x10
};
enum {
UART_LSR_DR = 0x01,
UART_LSR_OE = 0x02,
UART_LSR_PE = 0x04,
UART_LSR_FE = 0x08,
UART_LSR_THRE = 0x20
};
static unsigned uart_baud_rate = 115200;
#define UART_PORT_ADDR COM1_PORT_ADDR
#define UART_CLOCK 1843200
#define WRITE(r, v) outb(UART_PORT_ADDR + UART_##r, v)
#define READ(r) inb(UART_PORT_ADDR + UART_##r)
#define DELAY(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; }
static int uart_initted = 0;
static int
uart_probe( void )
{
WRITE( SCR, 0x5a );
if (READ(SCR) != 0x5a) return 0;
WRITE( SCR, 0xa5 );
if (READ(SCR) != 0xa5) return 0;
return 1;
}
static void
uart_set_baud_rate( unsigned long baud_rate )
{
const unsigned char lcr = READ( LCR );
unsigned long div;
if (baud_rate == 0) baud_rate = 9600;
div = UART_CLOCK / 16 / baud_rate;
WRITE( LCR, lcr | UART_LCR_DLAB );
WRITE( DLM, (unsigned char)(div >> 8) );
WRITE( DLL, (unsigned char) div );
WRITE( LCR, lcr & ~UART_LCR_DLAB);
}
static void
uart_putc( char c )
{
if (!uart_initted) return;
while ( !(READ(LSR) & UART_LSR_THRE) ) DELAY(1);
WRITE( THR, c );
}
static int
uart_getc( void )
{
unsigned char lsr;
if (!uart_initted) return -1;
lsr = READ( LSR );
if ( lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE) )
{
READ( RBR );
return -2;
}
if ( lsr & UART_LSR_DR )
{
return READ( RBR );
}
return -1;
}
int serial_init( void )
{
unsigned serial_baud_rate = 0;
if ( uart_probe() == 0 ) return 0;
WRITE( MCR, 0 );
WRITE( IER, 0 );
WRITE( FCR, 0 );
WRITE( LCR, UART_LCR_8BITS );
if (PE_parse_boot_argn("serialbaud", &serial_baud_rate, sizeof (serial_baud_rate)))
{
if (!((UART_CLOCK / 16) % serial_baud_rate)) {
uart_baud_rate = serial_baud_rate;
}
}
uart_set_baud_rate( uart_baud_rate );
WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS );
READ( RBR );
uart_initted = 1;
return 1;
}
void serial_putc( char c )
{
uart_putc(c);
if (c == '\n') uart_putc('\r');
}
int serial_getc( void )
{
return uart_getc();
}