#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <dev/ofw/fdt.h>
#include <dev/ofw/openfirm.h>
#include <machine/octeonreg.h>
#include <machine/octeonvar.h>
#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/ic/comreg.h>
#include <dev/ic/comvar.h>
#include <dev/cons.h>
#include <octeon/dev/iobusvar.h>
#include <octeon/dev/cn30xxuartreg.h>
#define OCTEON_UART_FIFO_SIZE 64
int cn30xxuart_probe(struct device *, void *, void *);
void cn30xxuart_attach(struct device *, struct device *, void *);
int cn30xxuart_intr(void *);
const struct cfattach octuart_ca = {
sizeof(struct com_softc), cn30xxuart_probe, cn30xxuart_attach
};
extern struct cfdriver com_cd;
cons_decl(octuart);
#define USR_TXFIFO_NOTFULL 2
static int delay_changed = 1;
int cn30xxuart_delay(void);
void cn30xxuart_wait_txhr_empty(int);
uint8_t uartbus_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
void uartbus_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t,
uint8_t);
bus_space_t uartbus_tag = {
.bus_base = PHYS_TO_XKPHYS(0, CCA_NC),
.bus_private = NULL,
._space_read_1 = uartbus_read_1,
._space_write_1 = uartbus_write_1,
._space_map = iobus_space_map,
._space_unmap = iobus_space_unmap
};
void
com_fdt_init_cons(void)
{
comconsiot = &uartbus_tag;
comconsaddr = OCTEON_UART0_BASE;
comconsfreq = octeon_ioclock_speed();
comconsrate = B115200;
comconscflag = (TTYDEF_CFLAG & ~(CSIZE | PARENB)) | CS8;
}
int
cn30xxuart_probe(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "cavium,octeon-3860-uart");
}
void
cn30xxuart_attach(struct device *parent, struct device *self, void *aux)
{
struct fdt_attach_args *faa = aux;
struct com_softc *sc = (void *)self;
int console = 0;
if (faa->fa_nreg != 1)
return;
if (comconsiot == &uartbus_tag && comconsaddr == faa->fa_reg[0].addr)
console = 1;
sc->sc_hwflags = 0;
sc->sc_swflags = 0;
sc->sc_frequency = octeon_ioclock_speed();
sc->sc_uarttype = COM_UART_16550A;
sc->sc_fifolen = OCTEON_UART_FIFO_SIZE;
if (!console || comconsattached) {
sc->sc_iot = &uartbus_tag;
sc->sc_iobase = faa->fa_reg[0].addr;
if (bus_space_map(sc->sc_iot, sc->sc_iobase, COM_NPORTS, 0,
&sc->sc_ioh)) {
printf(": could not map UART registers\n");
return;
}
} else {
sc->sc_iot = comconsiot;
sc->sc_iobase = comconsaddr;
if (comcnattach(sc->sc_iot, sc->sc_iobase, comconsrate,
sc->sc_frequency, comconscflag))
panic("could not set up serial console");
sc->sc_ioh = comconsioh;
}
com_attach_subr(sc);
octeon_intr_establish_fdt(faa->fa_node, IPL_TTY, cn30xxuart_intr, sc,
sc->sc_dev.dv_xname);
}
int
cn30xxuart_intr(void *arg)
{
comintr(arg);
return 1;
}
int
cn30xxuart_delay(void)
{
int divisor;
u_char lcr;
static int d = 0;
if (!delay_changed)
return d;
delay_changed = 0;
lcr = octeon_xkphys_read_8(MIO_UART0_LCR);
octeon_xkphys_write_8(MIO_UART0_LCR, lcr | LCR_DLAB);
divisor = octeon_xkphys_read_8(MIO_UART0_DLL) |
octeon_xkphys_read_8(MIO_UART0_DLH) << 8;
octeon_xkphys_write_8(MIO_UART0_LCR, lcr);
return 10;
}
void
cn30xxuart_wait_txhr_empty(int d)
{
while (((octeon_xkphys_read_8(MIO_UART0_LSR) & LSR_TXRDY) == 0) &&
((octeon_xkphys_read_8(MIO_UART0_USR) & USR_TXFIFO_NOTFULL) == 0))
delay(d);
}
void
octuartcninit(struct consdev *consdev)
{
}
void
octuartcnprobe(struct consdev *consdev)
{
}
void
octuartcnpollc(dev_t dev, int c)
{
}
void
octuartcnputc(dev_t dev, int c)
{
int d;
d = cn30xxuart_delay();
cn30xxuart_wait_txhr_empty(d);
octeon_xkphys_write_8(MIO_UART0_RBR, (uint8_t)c);
cn30xxuart_wait_txhr_empty(d);
}
int
octuartcngetc(dev_t dev)
{
int c, d;
d = cn30xxuart_delay();
while ((octeon_xkphys_read_8(MIO_UART0_LSR) & LSR_RXRDY) == 0)
delay(d);
c = (uint8_t)octeon_xkphys_read_8(MIO_UART0_RBR);
return (c);
}
uint8_t
uartbus_read_1(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t off)
{
return *(volatile uint64_t *)(handle + (off << 3));
}
void
uartbus_write_1(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t off,
uint8_t value)
{
volatile uint64_t *reg = (uint64_t *)(handle + (off << 3));
*reg = value;
(void)*reg;
}