--- v9
+++ v7
@@ -1,7 +1,9 @@
-From: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
-
Parse device tree to detect IMC units. Traverse through each IMC unit
node to find supported events and corresponding unit/scale files (if any).
+
+Here is the DTS file for reference:
+
+ https://github.com/open-power/ima-catalog/blob/master/81E00612.4E0100.dts
The device tree for IMC counters starts at the node "imc-counters".
This node contains all the IMC PMU nodes and event nodes
@@ -14,7 +16,7 @@
nodes themselves contain an "events-prefix" property. The value for this
property concatenated to the event name, forms the actual event
name. Also, the PMU have a "reg" field as the base offset for the events
-which belong to this PMU. This "reg" field is added to event's "reg" field
+which belong to this PMU. This "reg" field is added to event's "reg" field
in the "events" node, which gives us the location of the counter data. Kernel
code uses this offset as event configuration value.
@@ -32,36 +34,19 @@
Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
---
- arch/powerpc/include/asm/opal-api.h | 6 +
- arch/powerpc/platforms/powernv/opal-imc.c | 459 +++++++++++++++++++++++++++++-
- 2 files changed, 464 insertions(+), 1 deletion(-)
-
-diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h
-index cb3e624..aa150f0 100644
---- a/arch/powerpc/include/asm/opal-api.h
-+++ b/arch/powerpc/include/asm/opal-api.h
-@@ -1003,6 +1003,12 @@ enum {
- XIVE_DUMP_EMU_STATE = 5,
- };
-
-+/* In-Memory Collection Counters Type */
-+enum {
-+ IMC_COUNTER_PER_CHIP = 0x10,
-+ IMC_COUNTER_PER_SOCKET = 0x20,
-+};
-+
- #endif /* __ASSEMBLY__ */
-
- #endif /* __OPAL_API_H */
+ arch/powerpc/platforms/powernv/opal-imc.c | 413 ++++++++++++++++++++++++++++++
+ 1 file changed, 413 insertions(+)
+
diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c
-index 5b1045c..b20cfaf 100644
+index 3a87000..0ddaf7d 100644
--- a/arch/powerpc/platforms/powernv/opal-imc.c
+++ b/arch/powerpc/platforms/powernv/opal-imc.c
-@@ -34,9 +34,457 @@
+@@ -33,15 +33,428 @@
#include <asm/cputable.h>
#include <asm/imc-pmu.h>
+u64 nest_max_offset;
+ struct perchip_nest_info nest_perchip_info[IMC_MAX_CHIPS];
+struct imc_pmu *per_nest_pmu_arr[IMC_MAX_PMUS];
+
+static int imc_event_prop_update(char *name, struct imc_events *events)
@@ -171,7 +156,7 @@
+ if (!dev)
+ goto fail;
+
-+ /* Check for "event-name" property, which is the perfix for event names */
++ /* Find the event name */
+ name = of_find_property(dev, "event-name", NULL);
+ if (!name)
+ return -ENODEV;
@@ -199,7 +184,7 @@
+ /*
+ * If there is an issue in parsing a single property of
+ * this event, we just clean up the buffers, but we still
-+ * continue to parse. TODO: This could be rewritten to skip the
++ * continue to parse. XXX: This could be rewritten to skip the
+ * entire event node incase of parsing issues, but that can be
+ * done later.
+ */
@@ -261,8 +246,7 @@
+}
+
+/*
-+ * get_nr_children : Returns the number of events(along with scale and unit)
-+ * for a pmu device node.
++ * get_nr_children : Returns the number of children for a pmu device node.
+ */
+static int get_nr_children(struct device_node *pmu_node)
+{
@@ -297,10 +281,11 @@
+
+/*
+ * imc_events_setup() : First finds the event node for the pmu and
-+ * gets the number of supported events, then
-+ * allocates memory for the same and parse the events.
-+ */
-+static int imc_events_setup(struct device_node *parent,
++ * gets the number of supported events and then
++ * allocates memory for the same. Finally returns the address of events
++ * memory allocated.
++ */
++static struct imc_events *imc_events_setup(struct device_node *parent,
+ int pmu_index,
+ struct imc_pmu *pmu_ptr,
+ u32 prop,
@@ -308,6 +293,7 @@
+{
+ struct device_node *ev_node = NULL, *dir = NULL;
+ u32 reg;
++ struct imc_events *events;
+ struct property *scale_pp, *unit_pp, *name_prefix;
+ int ret = 0, nr_children = 0;
+
@@ -316,7 +302,7 @@
+ */
+ dir = of_find_node_by_phandle(prop);
+ if (!dir)
-+ return -1;
++ return NULL;
+ /*
+ * Get the maximum no. of events in this node.
+ * Multiply by 3 to account for .scale and .unit properties
@@ -325,10 +311,10 @@
+ */
+ nr_children = get_nr_children(dir) * 3;
+
-+ pmu_ptr->events = kzalloc((sizeof(struct imc_events) * nr_children),
++ events = kzalloc((sizeof(struct imc_events) * nr_children),
+ GFP_KERNEL);
-+ if (!pmu_ptr->events)
-+ return -ENOMEM;
++ if (!events)
++ return NULL;
+
+ /*
+ * Check if there is a common "scale" and "unit" properties inside
@@ -358,7 +344,7 @@
+
+ /* Loop through event nodes */
+ for_each_child_of_node(dir, ev_node) {
-+ ret = imc_events_node_parser(ev_node, &pmu_ptr->events[*idx], scale_pp,
++ ret = imc_events_node_parser(ev_node, &events[*idx], scale_pp,
+ unit_pp, name_prefix, reg, pmu_ptr->domain);
+ if (ret < 0) {
+ /* Unable to parse this event */
@@ -373,76 +359,33 @@
+ * event scale and unit files also.
+ */
+ *idx += ret;
-+ }
-+ return 0;
++ }
++ return events;
+
+free_events:
-+ imc_free_events(pmu_ptr->events, *idx);
-+ return -1;
-+
-+}
-+
-+/* imc_get_mem_addr_nest: Function to get nest counter memory region for each chip */
-+static int imc_get_mem_addr_nest(struct device_node *node,
-+ struct imc_pmu *pmu_ptr,
-+ u32 offset)
-+{
-+ int nr_chips = 0, i, j;
-+ u64 *base_addr_arr = NULL, baddr;
-+ u32 *chipid_arr = NULL, size = pmu_ptr->counter_mem_size, pages;
-+ struct imc_mem_info *l_mem_info;
-+
-+ nr_chips = of_property_count_u32_elems(node, "chip-id");
-+ if (!nr_chips)
-+ return -1;
-+
-+ base_addr_arr = kzalloc((sizeof(u64) * nr_chips), GFP_KERNEL);
-+ chipid_arr = kzalloc((sizeof(u32) * nr_chips), GFP_KERNEL);
-+ if (!base_addr_arr || !chipid_arr)
-+ return -1;
-+
-+ of_property_read_u32_array(node, "chip-id", chipid_arr, nr_chips);
-+ of_property_read_u64_array(node, "base_addr", base_addr_arr, nr_chips);
-+
-+ l_mem_info = kzalloc((sizeof(struct imc_mem_info) * nr_chips), GFP_KERNEL);
-+ if (!l_mem_info) {
-+ if (base_addr_arr)
-+ kfree(base_addr_arr);
-+ if (chipid_arr)
-+ kfree(chipid_arr);
-+
-+ return -1;
-+ }
-+
-+ for (i = 0; i < nr_chips; i++) {
-+ l_mem_info->id = chipid_arr[i];
-+ baddr = base_addr_arr[i] + offset;
-+ for (j = 0; j < (size/PAGE_SIZE); j++) {
-+ pages = PAGE_SIZE * j;
-+ l_mem_info->vbase[j] = (u64)phys_to_virt(baddr + pages);
-+ }
-+ }
-+ return 0;
-+}
-+
-+/*
-+ * imc_pmu_create : Takes the parent device which is the pmu unit, pmu_index
-+ * and domain as the inputs.
++ imc_free_events(events, *idx);
++ return NULL;
++
++}
++
++/*
++ * imc_pmu_create : Takes the parent device which is the pmu unit and a
++ * pmu_index as the inputs.
+ * Allocates memory for the pmu, sets up its domain (NEST), and
+ * calls imc_events_setup() to allocate memory for the events supported
-+ * by this pmu. Assigns a name for the pmu.
-+ *
++ * by this pmu. Assigns a name for the pmu. Calls imc_events_node_parser()
++ * to setup the individual events.
+ * If everything goes fine, it calls, init_imc_pmu() to setup the pmu device
+ * and register it.
+ */
+static int imc_pmu_create(struct device_node *parent, int pmu_index, int domain)
+{
++ struct imc_events *events = NULL;
++ struct imc_pmu *pmu_ptr;
+ u32 prop = 0;
+ struct property *pp;
+ char *buf;
+ int idx = 0, ret = 0;
-+ struct imc_pmu *pmu_ptr;
-+ u32 offset;
+
+ if (!parent)
+ return -EINVAL;
@@ -453,6 +396,8 @@
+ return -ENOMEM;
+
+ pmu_ptr->domain = domain;
++ if (pmu_ptr->domain == IMC_DOMAIN_UNKNOWN)
++ goto free_pmu;
+
+ /* Needed for hotplug/migration */
+ per_nest_pmu_arr[pmu_index] = pmu_ptr;
@@ -479,15 +424,6 @@
+ sprintf(buf, "nest_%s", (char *)pp->value);
+ pmu_ptr->pmu.name = (char *)buf;
+
-+ if (of_property_read_u32(parent, "size", &pmu_ptr->counter_mem_size))
-+ pmu_ptr->counter_mem_size = 0;
-+
-+ if (!of_property_read_u32(parent, "offset", &offset)) {
-+ if (imc_get_mem_addr_nest(parent, pmu_ptr, offset))
-+ goto free_pmu;
-+ pmu_ptr->imc_counter_mmaped = 1;
-+ }
-+
+ /*
+ * "events" property inside a PMU node contains the phandle value
+ * for the actual events node. The "events" node for the IMC PMU
@@ -495,43 +431,44 @@
+ * we want to factor out the common events (thereby, reducing the
+ * size of the device tree)
+ */
-+ if (!of_property_read_u32(parent, "events", &prop)) {
-+ if (prop)
-+ imc_events_setup(parent, pmu_index, pmu_ptr, prop, &idx);
-+ }
++ of_property_read_u32(parent, "events", &prop);
++ if (prop)
++ events = imc_events_setup(parent, pmu_index, pmu_ptr,
++ prop, &idx);
+ return 0;
+
+free_pmu:
-+ if (pmu_ptr)
-+ kfree(pmu_ptr);
++ kfree(pmu_ptr);
+ return ret;
+}
+
- static int opal_imc_counters_probe(struct platform_device *pdev)
+
+ /*
+ * imc_pmu_setup : Setup the IMC PMUs (children of "parent").
++ *
++ * Top level "imc-counters" node contains both event-nodes and pmu
++ * unit nodes. We only consider the pmu unit node here.
+ */
+ static void __init imc_pmu_setup(struct device_node *parent)
{
- struct device_node *imc_dev = NULL;
-+ int pmu_count = 0, domain;
-+ u32 type;
-
- if (!pdev || !pdev->dev.of_node)
- return -ENODEV;
-@@ -50,7 +498,16 @@ static int opal_imc_counters_probe(struct platform_device *pdev)
- imc_dev = pdev->dev.of_node;
- if (!imc_dev)
- return -ENODEV;
--
-+ for_each_compatible_node(imc_dev, NULL, IMC_DTB_UNIT_COMPAT) {
-+ if (of_property_read_u32(imc_dev, "type", &type))
-+ continue;
-+ if (type == IMC_COUNTER_PER_CHIP)
-+ domain = IMC_DOMAIN_NEST;
-+ else
-+ continue;
-+ if (!imc_pmu_create(imc_dev, pmu_count, domain))
-+ pmu_count++;
-+ }
- return 0;
++ struct device_node *child;
++ int pmu_count = 0, rc = 0, domain;
++
+ if (!parent)
+ return;
++ /*
++ * Loop through the imc-counters tree for each compatible
++ * "ibm,imc-counters-nest", and update "struct imc_pmu".
++ */
++ for_each_compatible_node(child, NULL, IMC_DTB_NEST_COMPAT) {
++ domain = IMC_DOMAIN_NEST;
++ rc = imc_pmu_create(child, pmu_count, domain);
++ if (rc)
++ return;
++ pmu_count++;
++ }
}
+ static int opal_imc_counters_probe(struct platform_device *pdev)
--
2.7.4