summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2008-12-30 21:34:08 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2008-12-30 21:34:08 +0000
commitc2b150939f32cedf17f9c2b57471107b563858a8 (patch)
tree79559be62d5e17c9fe53fca9f55bd5fbee93046b /sys/arch
parent97992d95144289ef21bb5d6533c37db0bbf096c3 (diff)
Add cbus(4), a virtual bus for devices that use logical domain channels as
found on sun4v systems with multiple domains configured.
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/sparc64/conf/files.sparc646
-rw-r--r--sys/arch/sparc64/dev/cbus.c359
-rw-r--r--sys/arch/sparc64/dev/cbusvar.h36
3 files changed, 400 insertions, 1 deletions
diff --git a/sys/arch/sparc64/conf/files.sparc64 b/sys/arch/sparc64/conf/files.sparc64
index aff4ebb7f1a..0ec0dee7297 100644
--- a/sys/arch/sparc64/conf/files.sparc64
+++ b/sys/arch/sparc64/conf/files.sparc64
@@ -1,4 +1,4 @@
-# $OpenBSD: files.sparc64,v 1.112 2008/12/29 22:07:35 miod Exp $
+# $OpenBSD: files.sparc64,v 1.113 2008/12/30 21:34:07 kettenis Exp $
# $NetBSD: files.sparc64,v 1.50 2001/08/10 20:53:50 eeh Exp $
# maxpartitions must be first item in files.${ARCH}
@@ -46,6 +46,10 @@ device vbus {}
attach vbus at mainbus
file arch/sparc64/dev/vbus.c vbus
+device cbus {}
+attach cbus at vbus
+file arch/sparc64/dev/cbus.c cbus
+
# Sun HME Ethernet controllers
device hme: ether, ifnet, mii, ifmedia
file dev/ic/hme.c hme
diff --git a/sys/arch/sparc64/dev/cbus.c b/sys/arch/sparc64/dev/cbus.c
new file mode 100644
index 00000000000..6470f78938e
--- /dev/null
+++ b/sys/arch/sparc64/dev/cbus.c
@@ -0,0 +1,359 @@
+/* $OpenBSD: cbus.c,v 1.1 2008/12/30 21:34:07 kettenis Exp $ */
+/*
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * 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/param.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+
+#include <machine/autoconf.h>
+#include <machine/hypervisor.h>
+#include <machine/openfirm.h>
+
+#include <sparc64/dev/cbusvar.h>
+#include <sparc64/dev/vbusvar.h>
+
+struct md_header {
+ uint32_t transport_version;
+ uint32_t node_blk_sz;
+ uint32_t name_blk_sz;
+ uint32_t data_blk_sz;
+};
+
+struct md_element {
+ uint8_t tag;
+ uint8_t name_len;
+ uint16_t _reserved_field;
+ uint32_t name_offset;
+ union {
+ struct {
+ uint32_t data_len;
+ uint32_t data_offset;
+ } y;
+ uint64_t val;
+ } d;
+};
+
+struct cbus_softc {
+ struct device sc_dv;
+ bus_space_tag_t sc_bustag;
+ bus_dma_tag_t sc_dmatag;
+
+ /* Machine description. */
+ caddr_t sc_md;
+ int sc_idx;
+};
+
+int cbus_match(struct device *, void *, void *);
+void cbus_attach(struct device *, struct device *, void *);
+int cbus_print(void *, const char *);
+
+struct cfattach cbus_ca = {
+ sizeof(struct cbus_softc), cbus_match, cbus_attach
+};
+
+struct cfdriver cbus_cd = {
+ NULL, "cbus", DV_DULL
+};
+
+caddr_t cbus_get_mach_desc(struct cbus_softc *);
+int cbus_get_channel_endpoint(struct cbus_softc *,
+ struct cbus_attach_args *);
+
+uint64_t sun4v_mdesc_get_prop_val(caddr_t, int, const char *);
+const char *sun4v_mdesc_get_prop_string(caddr_t, int, const char *);
+int sun4v_mdesc_find(caddr_t, const char *, uint64_t);
+int sun4v_mdesc_find_child(caddr_t, int, const char *, uint64_t);
+
+int
+cbus_match(struct device *parent, void *match, void *aux)
+{
+ struct vbus_attach_args *va = aux;
+
+ if (strcmp(va->va_name, "channel-devices") == 0)
+ return (1);
+
+ return (0);
+}
+
+void
+cbus_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct cbus_softc *sc = (struct cbus_softc *)self;
+ struct vbus_attach_args *va = aux;
+ int node;
+
+ sc->sc_bustag = va->va_bustag;
+ sc->sc_dmatag = va->va_dmatag;
+
+ sc->sc_md = cbus_get_mach_desc(sc);
+ if (sc->sc_md == NULL) {
+ printf(": can't read machine description\n");
+ return;
+ }
+
+ printf("\n");
+
+ sc->sc_idx = sun4v_mdesc_find(sc->sc_md, va->va_name, va->va_reg[0]);
+ if (sc->sc_idx == -1)
+ return;
+
+ for (node = OF_child(va->va_node); node; node = OF_peer(node)) {
+ struct cbus_attach_args ca;
+ char buf[32];
+
+ bzero(&ca, sizeof(ca));
+ ca.ca_node = node;
+ if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
+ continue;
+ ca.ca_name = buf;
+ ca.ca_bustag = sc->sc_bustag;
+ ca.ca_dmatag = sc->sc_dmatag;
+ getprop(node, "reg", sizeof(*ca.ca_reg),
+ &ca.ca_nreg, (void **)&ca.ca_reg);
+ if (cbus_get_channel_endpoint(sc, &ca) != 0)
+ continue;
+
+ config_found(self, &ca, cbus_print);
+ }
+}
+
+int
+cbus_print(void *aux, const char *name)
+{
+ struct cbus_attach_args *ca = aux;
+
+ if (name)
+ printf("\"%s\" at %s", ca->ca_name, name);
+ if (ca->ca_id != -1)
+ printf(" id 0x%llx", ca->ca_id);
+ if (ca->ca_tx_ino != -1)
+ printf(" tx-ino 0x%llx", ca->ca_tx_ino);
+ if (ca->ca_rx_ino != -1)
+ printf(" rx-ino 0x%llx", ca->ca_rx_ino);
+ return (UNCONF);
+}
+
+caddr_t
+cbus_get_mach_desc(struct cbus_softc *sc)
+{
+ bus_dmamap_t map;
+ bus_dma_segment_t seg;
+ psize_t len;
+ bus_size_t size;
+ caddr_t va;
+ int nsegs, err = 0;
+
+ len = 0;
+ hv_mach_desc((paddr_t)NULL, &len);
+ KASSERT(len != 0);
+
+again:
+ size = roundup(len, PAGE_SIZE);
+
+ if (bus_dmamap_create(sc->sc_dmatag, size, 1, size, 0,
+ BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &map) != 0)
+ return (NULL);
+
+ if (bus_dmamem_alloc(sc->sc_dmatag, size, PAGE_SIZE, 0, &seg,
+ 1, &nsegs, BUS_DMA_NOWAIT) != 0)
+ goto destroy;
+
+ if (bus_dmamem_map(sc->sc_dmatag, &seg, 1, size,
+ &va, BUS_DMA_NOWAIT) != 0)
+ goto free;
+
+ if (bus_dmamap_load(sc->sc_dmatag, map, va, size,
+ NULL, BUS_DMA_NOWAIT) != 0)
+ goto unmap;
+
+ len = size;
+ err = hv_mach_desc(map->dm_segs[0].ds_addr, &len);
+ if (err != H_EOK)
+ goto unload;
+
+ return (va);
+
+unload:
+ printf("unload\n");
+ bus_dmamap_unload(sc->sc_dmatag, map);
+unmap:
+ printf("unmap\n");
+ bus_dmamem_unmap(sc->sc_dmatag, va, size);
+free:
+ printf("free\n");
+ bus_dmamem_free(sc->sc_dmatag, &seg, 1);
+destroy:
+ printf("destroy\n");
+ bus_dmamap_destroy(sc->sc_dmatag, map);
+
+ /*
+ * If the machine description was updated while we were trying
+ * to fetch it, the allocated buffer may have been to small.
+ * Try again in that case.
+ */
+ if (err == H_EINVAL && len > size)
+ goto again;
+
+ return (NULL);
+}
+
+int
+cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca)
+{
+ struct md_header *hdr;
+ struct md_element *elem;
+ const char *name_blk;
+ const char *str;
+ int idx;
+ int arc;
+
+ idx = sun4v_mdesc_find_child(sc->sc_md, sc->sc_idx,
+ ca->ca_name, ca->ca_reg[0]);
+ if (idx == -1)
+ return (ENOENT);
+
+ hdr = (struct md_header *)sc->sc_md;
+ elem = (struct md_element *)(sc->sc_md + sizeof(struct md_header));
+ name_blk = sc->sc_md + sizeof(struct md_header) + hdr->node_blk_sz;
+
+ for (; elem[idx].tag != 'E'; idx++) {
+ str = name_blk + elem[idx].name_offset;
+ if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0)
+ continue;
+
+ arc = elem[idx].d.val;
+ str = name_blk + elem[arc].name_offset;
+ if (strcmp(str, "virtual-device-port") == 0) {
+ idx = arc;
+ continue;
+ }
+
+ if (strcmp(str, "channel-endpoint") == 0) {
+ ca->ca_id =
+ sun4v_mdesc_get_prop_val(sc->sc_md, arc, "id");
+ ca->ca_tx_ino =
+ sun4v_mdesc_get_prop_val(sc->sc_md, arc, "tx-ino");
+ ca->ca_rx_ino =
+ sun4v_mdesc_get_prop_val(sc->sc_md, arc, "rx-ino");
+ return (0);
+ }
+ }
+
+ ca->ca_id = -1;
+ ca->ca_tx_ino = -1;
+ ca->ca_rx_ino = -1;
+ return (0);
+}
+
+uint64_t
+sun4v_mdesc_get_prop_val(caddr_t md, int idx, const char *name)
+{
+ struct md_header *hdr;
+ struct md_element *elem;
+ const char *name_blk;
+ const char *str;
+
+ hdr = (struct md_header *)md;
+ elem = (struct md_element *)(md + sizeof(struct md_header));
+ name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz;
+
+ while (elem[idx].tag != 'E') {
+ str = name_blk + elem[idx].name_offset;
+ if (elem[idx].tag == 'v' && strcmp(str, name) == 0)
+ return (elem[idx].d.val);
+ idx++;
+ }
+
+ return (-1);
+}
+
+const char *
+sun4v_mdesc_get_prop_string(caddr_t md, int idx, const char *name)
+{
+ struct md_header *hdr;
+ struct md_element *elem;
+ const char *name_blk;
+ const char *data_blk;
+ const char *str;
+
+ hdr = (struct md_header *)md;
+ elem = (struct md_element *)(md + sizeof(struct md_header));
+ name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz;
+ data_blk = name_blk + hdr->name_blk_sz;
+
+ while (elem[idx].tag != 'E') {
+ str = name_blk + elem[idx].name_offset;
+ if (elem[idx].tag == 's' && strcmp(str, name) == 0)
+ return (data_blk + elem[idx].d.y.data_offset);
+ idx++;
+ }
+
+ return (NULL);
+}
+
+int
+sun4v_mdesc_find(caddr_t md, const char *name, uint64_t cfg_handle)
+{
+ struct md_header *hdr;
+ struct md_element *elem;
+ const char *str;
+ uint64_t val;
+ int idx;
+
+ hdr = (struct md_header *)md;
+ elem = (struct md_element *)(md + sizeof(struct md_header));
+
+ for (idx = 0; elem[idx].tag == 'N'; idx = elem[idx].d.val) {
+ str = sun4v_mdesc_get_prop_string(md, idx, "name");
+ val = sun4v_mdesc_get_prop_val(md, idx, "cfg-handle");
+ if (str && strcmp(str, name) == 0 && val == cfg_handle)
+ return (idx);
+ }
+
+ return (-1);
+}
+
+int
+sun4v_mdesc_find_child(caddr_t md, int idx, const char *name,
+ uint64_t cfg_handle)
+{
+ struct md_header *hdr;
+ struct md_element *elem;
+ const char *name_blk;
+ const char *str;
+ uint64_t val;
+ int arc;
+
+ hdr = (struct md_header *)md;
+ elem = (struct md_element *)(md + sizeof(struct md_header));
+ name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz;
+
+ for (; elem[idx].tag != 'E'; idx++) {
+ str = name_blk + elem[idx].name_offset;
+ if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0)
+ continue;
+
+ arc = elem[idx].d.val;
+ str = sun4v_mdesc_get_prop_string(md, arc, "name");
+ val = sun4v_mdesc_get_prop_val(md, arc, "cfg-handle");
+ if (str && strcmp(str, name) == 0 && val == cfg_handle)
+ return (arc);
+ }
+
+ return (-1);
+}
diff --git a/sys/arch/sparc64/dev/cbusvar.h b/sys/arch/sparc64/dev/cbusvar.h
new file mode 100644
index 00000000000..2502e327069
--- /dev/null
+++ b/sys/arch/sparc64/dev/cbusvar.h
@@ -0,0 +1,36 @@
+/* $OpenBSD: cbusvar.h,v 1.1 2008/12/30 21:34:07 kettenis Exp $ */
+/*
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * 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.
+ */
+
+#ifndef _SPARC64_DEV_CBUSVAR_H_
+#define _SPARC64_DEV_CBUSVAR_H_
+
+struct cbus_attach_args {
+ char *ca_name;
+ int ca_node;
+
+ bus_space_tag_t ca_bustag;
+ bus_dma_tag_t ca_dmatag;
+
+ u_int32_t *ca_reg;
+ int ca_nreg;
+
+ u_int64_t ca_id;
+ u_int64_t ca_tx_ino;
+ u_int64_t ca_rx_ino;
+};
+
+#endif