summaryrefslogtreecommitdiffstats
path: root/sys/arch/octeon/dev/octuctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/octeon/dev/octuctl.c')
-rw-r--r--sys/arch/octeon/dev/octuctl.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/sys/arch/octeon/dev/octuctl.c b/sys/arch/octeon/dev/octuctl.c
new file mode 100644
index 00000000000..d2791409bf1
--- /dev/null
+++ b/sys/arch/octeon/dev/octuctl.c
@@ -0,0 +1,250 @@
+/* $OpenBSD: octuctl.c,v 1.1 2016/03/18 05:38:10 jmatthew Exp $ */
+
+/*
+ * Copyright (c) 2015 Jonathan Matthew <jmatthew@openbsd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#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/octeon_model.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)
+{
+ int id;
+
+ id = octeon_get_chipid();
+ switch (octeon_model_family(id)) {
+ case OCTEON_MODEL_FAMILY_CN61XX:
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+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;
+
+ /* start usb controller reset */
+ 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);
+
+ /* set up for 12mhz crystal */
+ 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);
+
+ /* set clock divider */
+ 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);
+
+ /* turn hclk on */
+ 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);
+
+ /* power-on-reset finished */
+ ctl &= ~UCTL_CLK_RST_CTL_P_POR;
+ bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
+
+ delay(1000);
+
+ /* set up ohci clocks */
+ 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);
+
+ /* phy reset */
+ ctl |= UCTL_CLK_RST_CTL_P_PRST;
+ bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
+
+ delay(1);
+
+ /* clear host reset */
+ 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 octuctl_softc *sc = (struct octuctl_softc *)self;
+ struct iobus_attach_args *aa = aux;
+ struct octuctl_attach_args uaa;
+ uint64_t port_ctl;
+ uint64_t ctl;
+ uint64_t preg;
+ uint64_t txvref;
+ int rc;
+ int port;
+
+ sc->sc_iot = aa->aa_bust;
+ rc = bus_space_map(sc->sc_iot, UCTL_BASE, UCTL_SIZE,
+ 0, &sc->sc_ioh);
+ KASSERT(rc == 0);
+
+ /* do clock setup if not already done */
+ 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);
+
+ /* port phy settings */
+ 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 = aa->aa_bust;
+ uaa.aa_bust = &octuctl_tag;
+ uaa.aa_dmat = aa->aa_dmat;
+ uaa.aa_ioh = sc->sc_ioh;
+
+ uaa.aa_name = "ehci";
+ config_found(self, &uaa, octuctlprint);
+
+ uaa.aa_name = "ohci";
+ config_found(self, &uaa, octuctlprint);
+}