Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
[sfrench/cifs-2.6.git] / arch / powerpc / platforms / powernv / opal-imc.c
index 465ea105b7710ecf0ff7320dcec8fd2b9775cf2e..dd4c9b8b8a81e6967b29061014918b4f591921df 100644 (file)
 #include <asm/io.h>
 #include <asm/imc-pmu.h>
 #include <asm/cputhreads.h>
+#include <asm/debugfs.h>
+
+static struct dentry *imc_debugfs_parent;
+
+/* Helpers to export imc command and mode via debugfs */
+static int imc_mem_get(void *data, u64 *val)
+{
+       *val = cpu_to_be64(*(u64 *)data);
+       return 0;
+}
+
+static int imc_mem_set(void *data, u64 val)
+{
+       *(u64 *)data = cpu_to_be64(val);
+       return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_imc_x64, imc_mem_get, imc_mem_set, "0x%016llx\n");
+
+static struct dentry *imc_debugfs_create_x64(const char *name, umode_t mode,
+                                            struct dentry *parent, u64  *value)
+{
+       return debugfs_create_file_unsafe(name, mode, parent,
+                                         value, &fops_imc_x64);
+}
+
+/*
+ * export_imc_mode_and_cmd: Create a debugfs interface
+ *                     for imc_cmd and imc_mode
+ *                     for each node in the system.
+ *  imc_mode and imc_cmd can be changed by echo into
+ *  this interface.
+ */
+static void export_imc_mode_and_cmd(struct device_node *node,
+                                   struct imc_pmu *pmu_ptr)
+{
+       static u64 loc, *imc_mode_addr, *imc_cmd_addr;
+       int chip = 0, nid;
+       char mode[16], cmd[16];
+       u32 cb_offset;
+
+       imc_debugfs_parent = debugfs_create_dir("imc", powerpc_debugfs_root);
+
+       /*
+        * Return here, either because 'imc' directory already exists,
+        * Or failed to create a new one.
+        */
+       if (!imc_debugfs_parent)
+               return;
+
+       if (of_property_read_u32(node, "cb_offset", &cb_offset))
+               cb_offset = IMC_CNTL_BLK_OFFSET;
+
+       for_each_node(nid) {
+               loc = (u64)(pmu_ptr->mem_info[chip].vbase) + cb_offset;
+               imc_mode_addr = (u64 *)(loc + IMC_CNTL_BLK_MODE_OFFSET);
+               sprintf(mode, "imc_mode_%d", nid);
+               if (!imc_debugfs_create_x64(mode, 0600, imc_debugfs_parent,
+                                           imc_mode_addr))
+                       goto err;
+
+               imc_cmd_addr = (u64 *)(loc + IMC_CNTL_BLK_CMD_OFFSET);
+               sprintf(cmd, "imc_cmd_%d", nid);
+               if (!imc_debugfs_create_x64(cmd, 0600, imc_debugfs_parent,
+                                           imc_cmd_addr))
+                       goto err;
+               chip++;
+       }
+       return;
+
+err:
+       debugfs_remove_recursive(imc_debugfs_parent);
+}
 
 /*
  * imc_get_mem_addr_nest: Function to get nest counter memory region
@@ -65,6 +137,7 @@ static int imc_get_mem_addr_nest(struct device_node *node,
        }
 
        pmu_ptr->imc_counter_mmaped = true;
+       export_imc_mode_and_cmd(node, pmu_ptr);
        kfree(base_addr_arr);
        kfree(chipid_arr);
        return 0;
@@ -213,6 +286,10 @@ static int opal_imc_counters_probe(struct platform_device *pdev)
                }
        }
 
+       /* If none of the nest units are registered, remove debugfs interface */
+       if (pmu_count == 0)
+               debugfs_remove_recursive(imc_debugfs_parent);
+
        return 0;
 }