@@ -29,6 +29,7 @@
#include <console.h>
#include <fsi-master.h>
#include <centaur.h>
+#include <ocmb.h>
#include <libfdt/libfdt.h>
#include <timer.h>
#include <ipmi.h>
@@ -1190,6 +1191,9 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
/* Grab centaurs from device-tree if present (only on FSP-less) */
centaur_init();
+ /* initialize ocmb scom-controller */
+ ocmb_init();
+
/* Initialize PSI (depends on probe_platform being called) */
psi_init();
@@ -9,6 +9,7 @@ HW_OBJS += fake-nvram.o lpc-mbox.o npu2.o npu2-hw-procedures.o
HW_OBJS += npu2-common.o npu2-opencapi.o phys-map.o sbe-p9.o capp.o
HW_OBJS += occ-sensor.o vas.o sbe-p8.o dio-p9.o lpc-port80h.o cache-p9.o
HW_OBJS += npu-opal.o npu3.o npu3-nvlink.o npu3-hw-procedures.o
+HW_OBJS += ocmb.o
HW=hw/built-in.a
include $(SRC)/hw/fsp/Makefile.inc
new file mode 100644
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Open Capi Memory Buffer chip
+ *
+ * Copyright 2020 IBM Corp.
+ */
+
+
+#define pr_fmt(fmt) "OCMB: " fmt
+
+#include <skiboot.h>
+#include <xscom.h>
+#include <device.h>
+#include <ocmb.h>
+#include <io.h>
+#include <inttypes.h>
+
+struct ocmb_range {
+ uint64_t start;
+ uint64_t end;
+ uint64_t flags;
+
+ /* flags come from hdat */
+#define ACCESS_8B PPC_BIT(0)
+#define ACCESS_4B PPC_BIT(1)
+#define ACCESS_SIZE_MASK (ACCESS_8B | ACCESS_4B)
+};
+
+struct ocmb {
+ struct scom_controller scom;
+ int range_count;
+ struct ocmb_range ranges[];
+};
+
+static const struct ocmb_range *find_range(const struct ocmb *o, uint64_t offset)
+{
+ int i;
+
+ for (i = 0; i < o->range_count; i++) {
+ uint64_t start = o->ranges[i].start;
+ uint64_t end = o->ranges[i].end;
+
+ if (offset >= start && offset <= end)
+ return &o->ranges[i];
+ }
+
+ return NULL;
+}
+
+static int64_t ocmb_fake_scom_write(struct scom_controller *f,
+ uint32_t __unused chip_id,
+ uint64_t offset, uint64_t val)
+{
+ const struct ocmb *o = f->private;
+ const struct ocmb_range *r;
+
+ r = find_range(o, offset);
+ if (!r) {
+ prerror("no matching address range!\n");
+ return OPAL_XSCOM_ADDR_ERROR;
+ }
+
+ switch (r->flags & ACCESS_SIZE_MASK) {
+ case ACCESS_8B:
+ if (offset & 0x7)
+ return OPAL_XSCOM_ADDR_ERROR;
+ out_be64((void *) offset, val);
+ break;
+
+ case ACCESS_4B:
+ if (offset & 0x3)
+ return OPAL_XSCOM_ADDR_ERROR;
+ out_be32((void *) offset, val);
+ break;
+ default:
+ prerror("bad flags? %llx\n", r->flags);
+ return OPAL_XSCOM_ADDR_ERROR;
+ }
+
+ return OPAL_SUCCESS;
+}
+
+static int64_t ocmb_fake_scom_read(struct scom_controller *f,
+ uint32_t chip_id __unused,
+ uint64_t offset, uint64_t *val)
+{
+ const struct ocmb *o = f->private;
+ const struct ocmb_range *r = NULL;
+
+ r = find_range(o, offset);
+ if (!r) {
+ prerror("no matching address range!\n");
+ return OPAL_XSCOM_ADDR_ERROR;
+ }
+
+
+ switch (r->flags & ACCESS_SIZE_MASK) {
+ case ACCESS_8B:
+ if (offset & 0x7)
+ return OPAL_XSCOM_ADDR_ERROR;
+ *val = in_be64((void *) offset);
+ break;
+
+ case ACCESS_4B:
+ if (offset & 0x3)
+ return OPAL_XSCOM_ADDR_ERROR;
+ *val = in_be32((void *) offset);
+ break;
+ default:
+ prerror("bad flags? %llx\n", r->flags);
+ return OPAL_XSCOM_ADDR_ERROR;
+ }
+
+ return OPAL_SUCCESS;
+}
+
+static bool ocmb_probe_one(struct dt_node *ocmb_node)
+{
+ uint64_t chip_id = dt_prop_get_u32(ocmb_node, "ibm,chip-id");
+ const struct dt_property *flags;
+ int i = 0, num = 0;
+ struct ocmb *ocmb;
+
+ num = dt_count_addresses(ocmb_node);
+
+ ocmb = zalloc(sizeof(*ocmb) + sizeof(*ocmb->ranges) * num);
+ if (!ocmb)
+ return false;
+
+ ocmb->scom.private = ocmb;
+ ocmb->scom.part_id = chip_id;
+ ocmb->scom.write = ocmb_fake_scom_write;
+ ocmb->scom.read = ocmb_fake_scom_read;
+ ocmb->range_count = num;
+
+ flags = dt_require_property(ocmb_node, "flags", sizeof(u64) * num);
+
+ for (i = 0; i < num; i++) {
+ uint64_t start, size;
+
+ start = dt_get_address(ocmb_node, i, &size);
+
+ ocmb->ranges[i].start = start;
+ ocmb->ranges[i].end = start + size - 1;
+ ocmb->ranges[i].flags = dt_property_get_u64(flags, i);
+
+ prlog(PR_DEBUG, "Added range: %" PRIx64 " - [%llx - %llx]\n",
+ chip_id, start, start + size - 1);
+ }
+
+ if (scom_register(&ocmb->scom))
+ prerror("error registienr fake socm\n");
+
+ dt_add_property(ocmb_node, "scom-controller", NULL, 0);
+
+ prerror("XXX: Added scom controller for %s\n", ocmb_node->name);
+
+ return true;
+}
+
+void ocmb_init(void)
+{
+ struct dt_node *dn;
+
+ dt_for_each_compatible(dt_root, dn, "ibm,explorer")
+ ocmb_probe_one(dn);
+}
new file mode 100644
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Open Capi Memory Buffer chip
+ *
+ * Copyright 2020 IBM Corp.
+ */
+
+#ifndef __OCMB_H
+#define __OCMB_H
+
+extern void ocmb_init(void);
+
+#endif /* __OCMB_H */
\ No newline at end of file
Add a driver for the SCOM ranges of the OCMB. Unlike most chips the OCMB has two different (three if you count OpenCAPI config space) register spaces and we need to ensure that the right access size is used on each. Additionally the SCOM interface is a bit non-standard in that a full physical address is passed as the SCOM address rather than a register number so we don't need to perform any address transformations, we just need to verify that the address falls into one of the nominated address ranges. Cc: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> Signed-off-by: Oliver O'Halloran <oohall@gmail.com> --- core/init.c | 4 ++ hw/Makefile.inc | 1 + hw/ocmb.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ include/ocmb.h | 13 ++++ 4 files changed, 185 insertions(+) create mode 100644 hw/ocmb.c create mode 100644 include/ocmb.h