@@ -421,6 +421,7 @@ static struct {
{ RESOURCE_ID_KERNEL, RESOURCE_SUBID_NONE, "BOOTKERNEL" },
{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE, "ROOTFS" },
{ RESOURCE_ID_CAPP, RESOURCE_SUBID_SUPPORTED, "CAPP" },
+ { RESOURCE_ID_CATALOG, RESOURCE_SUBID_SUPPORTED, "IMA_CATALOG" },
};
@@ -47,6 +47,7 @@
#include <nvram.h>
#include <libstb/stb.h>
#include <libstb/container.h>
+#include <imc.h>
enum proc_gen proc_gen;
unsigned int pcie_max_link_speed;
@@ -978,6 +979,9 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
/* Read in NVRAM and set it up */
nvram_init();
+ /* preload the IMC catalog dtb */
+ imc_catalog_preload();
+
/* Set the console level */
console_log_level();
@@ -1001,6 +1005,9 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
/* NX init */
nx_init();
+ /* Init In-Memory Collection related stuff (load the IMC dtb into memory) */
+ imc_init();
+
/* Probe IO hubs */
probe_p7ioc();
@@ -1,7 +1,7 @@
# -*-Makefile-*-
SUBDIRS += hw
HW_OBJS = xscom.o chiptod.o gx.o cec.o lpc.o lpc-uart.o psi.o
-HW_OBJS += homer.o slw.o occ.o fsi-master.o centaur.o
+HW_OBJS += homer.o slw.o occ.o fsi-master.o centaur.o imc.o
HW_OBJS += nx.o nx-rng.o nx-crypto.o nx-842.o
HW_OBJS += p7ioc.o p7ioc-inits.o p7ioc-phb.o
HW_OBJS += phb3.o sfc-ctrl.o fake-rtc.o bt.o p8-i2c.o prd.o
new file mode 100644
@@ -0,0 +1,282 @@
+/* Copyright 2016 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <skiboot.h>
+#include <xscom.h>
+#include <imc.h>
+#include <chip.h>
+#include <libxz/xz.h>
+#include <device.h>
+
+/*
+ * Nest IMC PMU names along with their bit values as represented in the
+ * imc_chip_avl_vector(in struct imc_chip_cb, look at include/imc.h).
+ * nest_pmus[] is an array containing all the possible nest IMC PMU node names.
+ */
+char const *nest_pmus[] = {
+ "powerbus0",
+ "mcs0",
+ "mcs1",
+ "mcs2",
+ "mcs3",
+ "mcs4",
+ "mcs5",
+ "mcs6",
+ "mcs7",
+ "mba0",
+ "mba1",
+ "mba2",
+ "mba3",
+ "mba4",
+ "mba5",
+ "mba6",
+ "mba7",
+ "cen0",
+ "cen1",
+ "cen2",
+ "cen3",
+ "cen4",
+ "cen5",
+ "cen6",
+ "cen7",
+ "xlink0",
+ "xlink1",
+ "xlink2",
+ "mcd0",
+ "mcd1",
+ "phb0",
+ "phb1",
+ "phb2",
+ "resvd",
+ "nx",
+ "capp0",
+ "capp1",
+ "vas",
+ "int",
+ "alink0",
+ "alink1",
+ "alink2",
+ "nvlink0",
+ "nvlink1",
+ "nvlink2",
+ "nvlink3",
+ "nvlink4",
+ "nvlink5",
+ /* reserved bits : 48 - 64 */
+};
+
+char *imc_catalog_buf;
+size_t imc_catalog_size;
+const char **imc_prop_to_fix(struct dt_node *node);
+const char *prop_to_fix[] = {"events", NULL};
+
+static struct imc_chip_cb *get_imc_cb(void)
+{
+ uint64_t cb_loc;
+ struct proc_chip *chip = get_chip(this_cpu()->chip_id);
+
+ cb_loc = chip->homer_base + P9_CB_STRUCT_OFFSET;
+ return (struct imc_chip_cb *)cb_loc;
+}
+
+/*
+ * Decompresses the blob obtained from the IMC pnor sub-partition
+ * in "buf" of size "size", assigns the uncompressed device tree
+ * binary to "fdt" and returns.
+ *
+ * Returns 0 on success and -1 on error.
+ *
+ * TODO: Ideally this should be part of generic subpartition load
+ * infrastructure. And decompression can be queued as another CPU job
+ */
+static int decompress_subpartition(char *buf, size_t size, void **fdt)
+{
+ struct xz_dec *s;
+ struct xz_buf b;
+ void *data;
+ int ret = 0;
+
+ /* Initialize the xz library first */
+ xz_crc32_init();
+ s = xz_dec_init(XZ_SINGLE, 0);
+ if (s == NULL) {
+ prerror("IMC: initialization error for xz\n");
+ return -1;
+ }
+
+ /* Allocate memory for the uncompressed data */
+ data = malloc(MAX_COMPRESSED_IMC_DTB_SIZE);
+ if (!data) {
+ prerror("IMC: memory allocation error\n");
+ ret = -1;
+ goto err;
+ }
+
+ /*
+ * Source address : buf
+ * Source size : size
+ * Destination address : data
+ * Destination size : MAX_UNCOMPRESSED_IMC_DTB_SIZE
+ */
+ b.in = buf;
+ b.in_pos = 0;
+ b.in_size = size;
+ b.out = data;
+ b.out_pos = 0;
+ b.out_size = MAX_UNCOMPRESSED_IMC_DTB_SIZE;
+
+ /* Start decompressing */
+ ret = xz_dec_run(s, &b);
+ if (ret != XZ_STREAM_END) {
+ prerror("IMC: failed to decompress subpartition\n");
+ free(data);
+ ret = -1;
+ }
+ *fdt = data;
+
+err:
+ /* Clean up memory */
+ xz_dec_end(s);
+ return ret;
+}
+
+/*
+ * Function return list of properties names for the fixup
+ */
+const char **imc_prop_to_fix(struct dt_node *node)
+{
+ if (dt_node_is_compatible(node, "ibm,imc-counters-nest"))
+ return prop_to_fix;
+
+ return NULL;
+}
+
+/*
+ * Remove the PMU device nodes from the ncoming new subtree, if they are not
+ * available in the hardware. The availability is described by the
+ * control block's imc_chip_avl_vector.
+ * Each bit represents a device unit. If the device is available, then
+ * the bit is set else its unset.
+ */
+static int disable_unavailable_units(struct dt_node *dev)
+{
+ uint64_t avl_vec;
+ struct imc_chip_cb *cb;
+ struct dt_node *target;
+ int i;
+
+ /* Fetch the IMC control block structure */
+ cb = get_imc_cb();
+
+ avl_vec = be64_to_cpu(cb->imc_chip_avl_vector);
+ for (i = 0; i < MAX_NEST_UNITS; i++) {
+ if (!(PPC_BITMASK(i, i) & avl_vec)) {
+ /* Check if the device node exists */
+ target = dt_find_by_name(dev, nest_pmus[i]);
+ if (!target)
+ continue;
+ /* Remove the device node */
+ dt_free(target);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Function to queue the loading of imc catalog data
+ * from the IMC pnor partition.
+ */
+void imc_catalog_preload(void)
+{
+ uint32_t pvr = mfspr(SPR_PVR);
+ int ret = OPAL_SUCCESS;
+ imc_catalog_size = MAX_UNCOMPRESSED_IMC_DTB_SIZE;
+
+ /* Enable only for power 9 */
+ if (proc_gen != proc_gen_p9)
+ return;
+
+ imc_catalog_buf = malloc(MAX_UNCOMPRESSED_IMC_DTB_SIZE);
+ if (!imc_catalog_buf) {
+ prerror("IMC: Memory allocation for catalog failed\n");
+ return;
+ }
+
+ ret = start_preload_resource(RESOURCE_ID_CATALOG,
+ pvr, imc_catalog_buf, &imc_catalog_size);
+ if (ret != OPAL_SUCCESS)
+ free(imc_catalog_buf);
+
+ return;
+}
+
+/*
+ * Load the IMC pnor partition and find the appropriate sub-partition
+ * based on the platform's PVR.
+ * Decompress the sub-partition and link the imc device tree to the
+ * existing device tree.
+ */
+void imc_init(void)
+{
+ void *fdt = NULL;
+ uint32_t pvr = mfspr(SPR_PVR);
+ struct dt_node *dev;
+ int ret;
+
+ /* Enable only for power 9 */
+ if (proc_gen != proc_gen_p9)
+ return;
+
+ ret = wait_for_resource_loaded(RESOURCE_ID_CATALOG, pvr);
+ if (ret != OPAL_SUCCESS) {
+ prerror("IMC Catalog load failed\n");
+ return;
+ }
+
+ /* Decompress the subpartition now */
+ ret = decompress_subpartition(imc_catalog_buf, imc_catalog_size, &fdt);
+ if (ret < 0)
+ goto err;
+
+ /* Create a device tree entry for imc counters */
+ dev = dt_new_root("imc-counters");
+ if (!dev)
+ goto err;
+
+ /* Attach the new fdt to the imc-counters node */
+ ret = dt_expand_node(dev, fdt, 0);
+ if (ret < 0) {
+ dt_free(dev);
+ goto err;
+ }
+
+ dt_adjust_subtree_phandle(dev, imc_prop_to_fix);
+
+ /* Check availability of the Nest PMU units from the availability vector */
+ disable_unavailable_units(dev);
+
+ if (!dt_attach_root(dt_root, dev)) {
+ dt_free(dev);
+ goto err;
+ }
+
+ free(imc_catalog_buf);
+ return;
+err:
+ prerror("IMC Devices not added\n");
+ free(imc_catalog_buf);
+}
@@ -116,4 +116,6 @@ struct imc_chip_cb
#define MAX_NEST_UNITS 48
+void imc_init(void);
+void imc_catalog_preload(void);
#endif /* __IMC_H */
@@ -27,6 +27,7 @@ enum resource_id {
RESOURCE_ID_KERNEL,
RESOURCE_ID_INITRAMFS,
RESOURCE_ID_CAPP,
+ RESOURCE_ID_CATALOG,
};
#define RESOURCE_SUBID_NONE 0
#define RESOURCE_SUBID_SUPPORTED 1