summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/fdt/files.fdt5
-rw-r--r--sys/dev/fdt/ohci_fdt.c199
2 files changed, 203 insertions, 1 deletions
diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt
index 5276439c306..6092bd64396 100644
--- a/sys/dev/fdt/files.fdt
+++ b/sys/dev/fdt/files.fdt
@@ -1,4 +1,4 @@
-# $OpenBSD: files.fdt,v 1.74 2018/08/27 21:09:47 kettenis Exp $
+# $OpenBSD: files.fdt,v 1.75 2019/01/04 23:55:29 dlg Exp $
#
# Config file and device description for machine-independent FDT code.
# Included by ports that need it.
@@ -109,6 +109,9 @@ file dev/fdt/if_dwge_fdt.c dwge_fdt
attach ehci at fdt with ehci_fdt
file dev/fdt/ehci_fdt.c ehci_fdt
+attach ohci at fdt with ohci_fdt
+file dev/fdt/ohci_fdt.c ohci_fdt
+
attach sdhc at fdt with sdhc_fdt
file dev/fdt/sdhc_fdt.c sdhc_fdt
diff --git a/sys/dev/fdt/ohci_fdt.c b/sys/dev/fdt/ohci_fdt.c
new file mode 100644
index 00000000000..12550f199f4
--- /dev/null
+++ b/sys/dev/fdt/ohci_fdt.c
@@ -0,0 +1,199 @@
+/* $OpenBSD: ohci_fdt.c,v 1.1 2019/01/04 23:55:29 dlg Exp $ */
+
+/*
+ * Copyright (c) 2005, 2019 David Gwynne <dlg@openbsd.org>
+ * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * Permission to use, copy, modify, and 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/types.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/ofw_pinctrl.h>
+#include <dev/ofw/ofw_regulator.h>
+#include <dev/ofw/fdt.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/ohcireg.h>
+#include <dev/usb/ohcivar.h>
+
+struct ohci_fdt_softc {
+ struct ohci_softc sc;
+ int sc_node;
+ void *sc_ih;
+};
+
+int ohci_fdt_match(struct device *, void *, void *);
+void ohci_fdt_attach(struct device *, struct device *, void *);
+int ohci_fdt_detach(struct device *, int);
+
+struct cfattach ohci_fdt_ca = {
+ sizeof(struct ohci_fdt_softc),
+ ohci_fdt_match,
+ ohci_fdt_attach,
+ ohci_fdt_detach,
+ ohci_activate
+};
+
+void ohci_fdt_attach_deferred(struct device *);
+
+int
+ohci_fdt_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "generic-ohci");
+}
+
+void
+ohci_fdt_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self;
+ struct fdt_attach_args *faa = aux;
+ char *devname = sc->sc.sc_bus.bdev.dv_xname;
+
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
+ return;
+ }
+
+ sc->sc_node = faa->fa_node;
+ sc->sc.iot = faa->fa_iot;
+ sc->sc.sc_bus.dmatag = faa->fa_dmat;
+ sc->sc.sc_size = faa->fa_reg[0].size;
+
+ if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
+ faa->fa_reg[0].size, 0, &sc->sc.ioh)) {
+ printf(": can't map registers\n");
+ goto out;
+ }
+
+ pinctrl_byname(sc->sc_node, "default");
+
+ clock_enable_all(sc->sc_node);
+ reset_deassert_all(sc->sc_node);
+
+ /* Record what interrupts were enabled by SMM/BIOS. */
+ sc->sc.sc_intre = bus_space_read_4(sc->sc.iot, sc->sc.ioh,
+ OHCI_INTERRUPT_ENABLE);
+
+ /* Disable interrupts, so we don't get any spurious ones. */
+ bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE,
+ OHCI_MIE);
+
+ bus_space_barrier(sc->sc.iot, sc->sc.ioh, 0, sc->sc.sc_size,
+ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
+ bus_space_write_4(sc->sc.iot, sc->sc.ioh,
+ OHCI_INTERRUPT_DISABLE, OHCI_MIE);
+
+ /* Map and establish the interrupt. */
+ splassert(IPL_USB);
+ sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB,
+ ohci_intr, &sc->sc, devname);
+ if (sc->sc_ih == NULL) {
+ printf(": can't establish interrupt\n");
+ goto disable_clocks;
+ }
+ printf(": fdt");
+
+ strlcpy(sc->sc.sc_vendor, "Generic", sizeof(sc->sc.sc_vendor));
+
+ /* Display revision. Legacy handover isn't needed as there's no smm */
+ if (ohci_checkrev(&sc->sc) != USBD_NORMAL_COMPLETION) {
+ goto disestablish_intr;
+ }
+
+ /* Ignore interrupts for now */
+ sc->sc.sc_bus.dying = 1;
+
+ config_defer(self, ohci_fdt_attach_deferred);
+
+ return;
+
+disestablish_intr:
+ fdt_intr_disestablish(sc->sc_ih);
+ sc->sc_ih = NULL;
+disable_clocks:
+ clock_disable_all(sc->sc_node);
+
+ bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
+ sc->sc.sc_size = 0;
+out:
+ return;
+}
+
+void
+ohci_fdt_attach_deferred(struct device *self)
+{
+ struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self;
+ usbd_status r;
+ int s;
+
+ s = splusb();
+
+ sc->sc.sc_bus.dying = 0;
+
+ r = ohci_init(&sc->sc);
+
+ splx(s);
+ if (r != USBD_NORMAL_COMPLETION) {
+ printf("%s: init failed, error=%d\n",
+ sc->sc.sc_bus.bdev.dv_xname, r);
+ fdt_intr_disestablish(sc->sc_ih);
+ sc->sc_ih = NULL;
+ clock_disable_all(sc->sc_node);
+ bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
+ sc->sc.sc_size = 0;
+ return;
+ }
+
+ /* Attach usb device. */
+ config_found(self, &sc->sc.sc_bus, usbctlprint);
+}
+
+int
+ohci_fdt_detach(struct device *self, int flags)
+{
+ struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self;
+ int rv;
+
+ rv = ohci_detach(self, flags);
+ if (rv)
+ return rv;
+
+ if (sc->sc_ih != NULL) {
+ fdt_intr_disestablish(sc->sc_ih);
+ sc->sc_ih = NULL;
+ }
+
+ if (sc->sc.sc_size) {
+ bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
+ sc->sc.sc_size = 0;
+ }
+
+ clock_disable_all(sc->sc_node);
+ return 0;
+}