#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <machine/octeonreg.h>
#include <machine/octeonvar.h>
#include <machine/fdt.h>
#include <octeon/dev/iobusvar.h>
#include <octeon/dev/octuctlreg.h>
#include <octeon/dev/octuctlvar.h>
struct octuctl_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
};
int octuctl_match(struct device *, void *, void *);
void octuctl_attach(struct device *, struct device *, void *);
const struct cfattach octuctl_ca = {
sizeof(struct octuctl_softc), octuctl_match, octuctl_attach,
};
struct cfdriver octuctl_cd = {
NULL, "octuctl", DV_DULL
};
uint8_t octuctl_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
uint16_t octuctl_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
uint32_t octuctl_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
void octuctl_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t);
void octuctl_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t);
void octuctl_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t);
bus_space_t octuctl_tag = {
.bus_base = PHYS_TO_XKPHYS(0, CCA_NC),
.bus_private = NULL,
._space_read_1 = octuctl_read_1,
._space_write_1 = octuctl_write_1,
._space_read_2 = octuctl_read_2,
._space_write_2 = octuctl_write_2,
._space_read_4 = octuctl_read_4,
._space_write_4 = octuctl_write_4,
._space_map = iobus_space_map,
._space_unmap = iobus_space_unmap,
._space_subregion = generic_space_region,
._space_vaddr = generic_space_vaddr
};
uint8_t
octuctl_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
return *(volatile uint8_t *)(h + (o^3));
}
uint16_t
octuctl_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
return *(volatile uint16_t *)(h + (o^2));
}
uint32_t
octuctl_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
return *(volatile uint32_t *)(h + o);
}
void
octuctl_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v)
{
*(volatile uint8_t *)(h + (o^3)) = v;
}
void
octuctl_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v)
{
*(volatile uint16_t *)(h + (o^2)) = v;
}
void
octuctl_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v)
{
*(volatile uint32_t *)(h + o) = v;
}
int
octuctl_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "cavium,octeon-6335-uctl");
}
int
octuctlprint(void *aux, const char *parentname)
{
return (QUIET);
}
void
octuctl_clock_setup(struct octuctl_softc *sc, uint64_t ctl)
{
int div;
int lastdiv;
int validdiv[] = { 1, 2, 3, 4, 6, 8, 12, INT_MAX };
int i;
div = octeon_ioclock_speed() / UCTL_CLK_TARGET_FREQ;
ctl |= UCTL_CLK_RST_CTL_P_POR;
ctl &= ~(UCTL_CLK_RST_CTL_HRST |
UCTL_CLK_RST_CTL_P_PRST |
UCTL_CLK_RST_CTL_O_CLKDIV_EN |
UCTL_CLK_RST_CTL_H_CLKDIV_EN |
UCTL_CLK_RST_CTL_H_CLKDIV_RST |
UCTL_CLK_RST_CTL_O_CLKDIV_RST);
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
ctl &= ~((3 << UCTL_CLK_RST_CTL_P_REFCLK_DIV_SHIFT) |
(3 << UCTL_CLK_RST_CTL_P_REFCLK_SEL_SHIFT));
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
lastdiv = 1;
for (i = 0; i < nitems(validdiv); i++) {
if (div < validdiv[i]) {
div = lastdiv;
break;
}
lastdiv = validdiv[i];
}
ctl &= ~(0xf << UCTL_CLK_RST_CTL_H_DIV_SHIFT);
ctl |= (div << UCTL_CLK_RST_CTL_H_DIV_SHIFT);
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh,
UCTL_CLK_RST_CTL);
ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_EN;
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_RST;
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
delay(1);
ctl &= ~UCTL_CLK_RST_CTL_P_POR;
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
delay(1000);
ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_RST;
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_EN;
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
delay(1);
ctl |= UCTL_CLK_RST_CTL_P_PRST;
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
delay(1);
ctl |= UCTL_CLK_RST_CTL_HRST;
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
}
void
octuctl_attach(struct device *parent, struct device *self, void *aux)
{
struct fdt_attach_args *faa = aux;
struct octuctl_softc *sc = (struct octuctl_softc *)self;
struct octuctl_attach_args uaa;
uint64_t port_ctl;
uint64_t ctl;
uint64_t preg;
uint64_t txvref;
uint32_t reg[4];
int port;
int node;
int rc;
if (faa->fa_nreg != 1) {
printf(": expected one IO space, got %d\n", faa->fa_nreg);
return;
}
sc->sc_iot = faa->fa_iot;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
0, &sc->sc_ioh)) {
printf(": could not map IO space\n");
return;
}
rc = OF_getpropint(faa->fa_node, "#address-cells", 0);
if (rc != 2) {
printf(": expected #address-cells 2, got %d\n", rc);
return;
}
rc = OF_getpropint(faa->fa_node, "#size-cells", 0);
if (rc != 2) {
printf(": expected #size-cells 2, got %d\n", rc);
return;
}
bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_IF_ENA,
UCTL_IF_ENA_EN);
ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL);
if ((ctl & UCTL_CLK_RST_CTL_HRST) == 0)
octuctl_clock_setup(sc, ctl);
for (port = 0; port < 2; port++) {
preg = UCTL_UPHY_PORTX_STATUS + (port * 8);
port_ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, preg);
txvref = 0xf;
port_ctl |= (UCTL_UPHY_PORTX_STATUS_TXPREEMPHTUNE |
UCTL_UPHY_PORTX_STATUS_TXRISETUNE |
(txvref << UCTL_UPHY_PORTX_STATUS_TXVREF_SHIFT));
bus_space_write_8(sc->sc_iot, sc->sc_ioh, preg, port_ctl);
}
printf("\n");
uaa.aa_octuctl_bust = sc->sc_iot;
uaa.aa_bust = &octuctl_tag;
uaa.aa_dmat = faa->fa_dmat;
uaa.aa_ioh = sc->sc_ioh;
for (node = OF_child(faa->fa_node); node != 0; node = OF_peer(node)) {
if (OF_getproplen(node, "reg") != sizeof(reg))
continue;
OF_getpropintarray(node, "reg", reg, sizeof(reg));
uaa.aa_reg.addr = (((uint64_t)reg[0]) << 32) | reg[1];
uaa.aa_reg.size = (((uint64_t)reg[2]) << 32) | reg[3];
uaa.aa_node = node;
config_found(self, &uaa, octuctlprint);
}
}