1
0
Files
2022-09-29 17:59:04 +03:00

327 lines
6.8 KiB
C

/***********************************************************************\
* File: ioc3uart.c *
* *
* IOC3 ASIC UART routines. The IOC3 ASIC resides on the IO6 *
* card and forms the basis for a system console. *
* *
\***********************************************************************/
#include <sys/types.h>
#include <sys/ns16550.h>
#include <sys/SN/agent.h>
#include <sys/SN/klconfig.h>
#include <sys/PCI/ioc3.h>
#include <sys/PCI/PCI_defs.h>
#include "ip27prom.h"
#include "hub.h"
#include "ioc3uart.h"
#include "libasm.h"
#include "rtc.h"
#include "pod.h"
#define INIT_TIMEOUT 10000 /* microseconds */
#define PUTC_TIMEOUT 10000 /* microseconds */
#if 0
/* Junk bus needs its registers 64-bit aligned. */
typedef __uint64_t prom_uart_reg_t;
#define PROM_SER_CLK_SPEED 12000000
#define PROM_SER_DIVISOR(x) (PROM_SER_CLK_SPEED / ((x) * 16))
#else
#define PROM_SER_CLK_SPEED (SER_XIN_CLK / SER_PREDIVISOR)
#define PROM_SER_DIVISOR(y) SER_DIVISOR(y, PROM_SER_CLK_SPEED)
/* IOC3 likes its registers 32-bit aligned. */
typedef uart_reg_t prom_uart_reg_t;
#endif
/*
* ioc3uart_init
*/
static int baud_table[] = {
/* 110, 300, 1200, 2400, 4800, 9600, 19200, 38400 */
300, 1200, 2400, 9600, 19200, 38400
};
#define NBAUD (sizeof (baud_table) / sizeof (baud_table[0]))
static int
valid_baud(int baud)
{
int i;
for (i = 0; i < NBAUD; i++)
if (baud == baud_table[i])
return 1;
return 0;
}
static int
next_baud(int baud)
{
int i;
for (i = 1; i < NBAUD; i++)
if (baud_table[i] > baud)
return baud_table[i];
return baud_table[0];
}
#if 0
#define IOC3DELAY delay(2)
#else
#define IOC3DELAY delay(200)
#endif
static void
configure_port(volatile prom_uart_reg_t *cntrl, int baud)
{
int clkdiv;
rtc_time_t expire;
clkdiv = PROM_SER_DIVISOR(baud);
hubprintf("About to read %lx\n", cntrl);
expire = rtc_time() + INIT_TIMEOUT;
/* make sure the transmit FIFO is empty */
while ((RD_REG(cntrl, LINE_STATUS_REG) & LSR_XMIT_EMPTY) == 0 &&
rtc_time() < expire)
IOC3DELAY;
WR_REG(cntrl, LINE_CNTRL_REG, LCR_DLAB);
IOC3DELAY;
WR_REG(cntrl, DIVISOR_LATCH_MSB_REG, (clkdiv >> 8) & 0xff);
IOC3DELAY;
WR_REG(cntrl, DIVISOR_LATCH_LSB_REG, clkdiv & 0xff);
IOC3DELAY;
/* while dlab is set program the pre-divisor */
WR_REG(cntrl, SCRATCH_PAD_REG, SER_PREDIVISOR * 2);
IOC3DELAY;
/* set operating parameters and set DLAB to 0 */
WR_REG(cntrl, LINE_CNTRL_REG, LCR_8_BITS_CHAR | LCR_1_STOP_BITS);
IOC3DELAY;
/* no interrupt */
WR_REG(cntrl, INTR_ENABLE_REG, 0x0);
IOC3DELAY;
/* enable FIFO mode and reset both FIFOs */
WR_REG(cntrl, FIFO_CNTRL_REG, FCR_ENABLE_FIFO);
IOC3DELAY;
WR_REG(cntrl, FIFO_CNTRL_REG,
FCR_ENABLE_FIFO | FCR_RCV_FIFO_RESET | FCR_XMIT_FIFO_RESET);
/* turn on RTS and DTR in case the console terminal expects flow
* control signals to make sense
*/
WR_REG(cntrl, MODEM_CNTRL_REG, MCR_DTR | MCR_RTS);
}
void
ioc3_chip_init(console_t *console)
{
struct ioc3_configregs *cb = (struct ioc3_configregs*)console->config_base;
PCI_OUTW(&cb->pci_scr,
PCI_INW(&cb->pci_scr) |
PCI_CMD_BUS_MASTER | PCI_CMD_MEM_SPACE);
PCI_OUTW(&cb->pci_lat, 0xff00);
}
void ioc3uart_init(void *init_data)
{
volatile prom_uart_reg_t *cntrl;
int baud;
console_t *console = init_data;
set_ioc3uart_base(console->uart_base);
cntrl = (volatile prom_uart_reg_t *) (console->uart_base);
baud = console->baud ;
hubprintf("IOC3_SIO_CR address == 0x%lx\n",
console->memory_base + IOC3_SIO_CR);
ioc3_chip_init(console);
SW(console->memory_base + IOC3_SIO_CR,
((UARTA_BASE >> 3) << SIO_CR_SER_A_BASE_SHIFT) |
((UARTB_BASE >> 3) << SIO_CR_SER_B_BASE_SHIFT) |
(4 << SIO_CR_CMD_PULSE_SHIFT));
hubprintf("IOC3_SIO_CR == 0x%x\n", LW(console->memory_base + IOC3_SIO_CR));
SW(console->memory_base + IOC3_GPDR, 0);
SW(console->memory_base + IOC3_GPCR_S,
GPCR_INT_OUT_EN | GPCR_MLAN_EN |
GPCR_DIR_SERA_XCVR | GPCR_DIR_SERB_XCVR);
configure_port(cntrl, valid_baud(baud) ? baud : 9600);
}
/*
* ioc3uart_poll
*
* Returns 1 if a character is available, 0 if not.
*/
int ioc3uart_poll(void)
{
volatile prom_uart_reg_t *cntrl;
uchar_t status;
cntrl = (volatile prom_uart_reg_t *) get_ioc3uart_base();
status = RD_REG(cntrl, LINE_STATUS_REG);
#if 0
if (status & LSR_BREAK) {
int baud;
/* pop the NULL char associated with the BREAK off the FIFO */
RD_REG(cntrl, RCV_BUF_REG);
/* cycle baud rate */
baud = next_baud(get_ioc3uart_baud());
set_ioc3uart_baud(baud);
configure_port(cntrl, baud);
ioc3uart_puts("\nBaud changed\n");
/* XXX set new baud rate in NVRAM variable, see main.c */
return 0;
}
#endif
if (status & (LSR_OVERRUN_ERR | LSR_PARITY_ERR | LSR_FRAMING_ERR)) {
if (status & LSR_OVERRUN_ERR)
ioc3uart_puts("\nNS16550 overrun error\n");
else if (status & LSR_FRAMING_ERR)
ioc3uart_puts("\nNS16550 framing error\n");
else
ioc3uart_puts("\nNS16550 parity error\n");
return 0;
}
return ((status & LSR_DATA_READY) != 0);
}
/*
* ioc3uart_readc
*
* Reads a character. May only be called after ioc3uart_poll has
* indicated a character is available.
*/
int ioc3uart_readc(void)
{
volatile prom_uart_reg_t *cntrl;
cntrl = (volatile prom_uart_reg_t *) get_ioc3uart_base();
return RD_REG(cntrl, RCV_BUF_REG);
}
/*
* ioc3uart_getc
*
* Waits for a character and returns it.
* Flashes hub LEDs while waiting.
*/
#define FLASH_CYCLES 2000
int ioc3uart_getc(void)
{
int f, i, c;
uchar_t status;
for (f = 0xc0; ; f ^= IOC3UART_FLASH) {
for (i = 0; i < FLASH_CYCLES; i++)
if (ioc3uart_poll()){
c = ioc3uart_readc();
return c;
}
hub_led_set(f);
}
/*NOTREACHED*/
return 0;
}
/*
* ioc3uart_putc
*
* Writes a single character when ready.
* Returns 0 if successful, -1 if timed out.
*
* Converts \n to \r\n (can add 256 to quote a plain \n).
*/
int ioc3uart_putc(int c)
{
volatile prom_uart_reg_t *cntrl;
rtc_time_t expire;
if (c > 255)
c -= 256;
else if (c == '\n' && ioc3uart_putc('\r') < 0)
return -1;
cntrl = (volatile prom_uart_reg_t *) get_ioc3uart_base();
expire = rtc_time() + PUTC_TIMEOUT;
while (! (RD_REG(cntrl, LINE_STATUS_REG) & LSR_XMIT_BUF_EMPTY))
if (rtc_time() > expire)
return -1;
WR_REG(cntrl, XMIT_BUF_REG, c);
return 0;
}
/*
* ioc3uart_puts
*
* Writes a null-terminated string.
* Returns 0 if successful, -1 if ioc3_putc failed.
* Contains special support so the string may reside in the PROM.
*/
int ioc3uart_puts(char *s)
{
int c;
if (s == 0)
s = "<NULL>";
while ((c = LBYTE(s)) != 0) {
if (ioc3uart_putc(c) < 0)
return -1;
s++;
}
return 0;
}