iommu: msm: Read PMU configuration from DT
The Performance Monitor Unit (PMU) configuration (such as number of
performance monitor groups and number of counters) is not accurately
reflected in the registers of the PMU. Instead, store configuration in
device tree and read it from there when configuring the iommu driver.
Change-Id: I827d6a81a4dc4fef3c32b3d4957322b67f46c792
Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
index 67ce9fc..2c47f74 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
@@ -18,6 +18,9 @@
- interrupts : should contain the performance monitor overflow interrupt number.
- qcom,iommu-enable-halt : Enable halt of the IOMMU before programming certain 19
registers
+- qcom,iommu-pmu-ngroups: Number of Performance Monitor Unit (PMU) groups.
+- qcom,iommu-pmu-ncounters: Number of PMU counters per group.
+- qcom,iommu-pmu-event-classes: List of event classes supported.
- List of sub nodes, one for each of the translation context banks supported.
Each sub node has the following required properties:
@@ -56,6 +59,10 @@
qcom,iommu-bfb-regs = <0x204c 0x2050>;
qcom,iommu-bfb-data = <0xffff 0xffce>;
label = "iommu_0";
+ qcom,iommu-pmu-ngroups = <1>;
+ qcom,iommu-pmu-ncounters = <8>;
+ qcom,iommu-pmu-event-classes = <0x00,
+ 0x01>;
qcom,iommu-ctx@fda6c000 {
reg = <0xfda6c000 0x1000>;
diff --git a/arch/arm/mach-msm/include/mach/iommu_perfmon.h b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
index b44523f..59f58c1 100644
--- a/arch/arm/mach-msm/include/mach/iommu_perfmon.h
+++ b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
@@ -16,7 +16,6 @@
#ifndef MSM_IOMMU_PERFMON_H
#define MSM_IOMMU_PERFMON_H
-
/**
* struct iommu_access_ops - Callbacks for accessing IOMMU
* @iommu_power_on: Turn on clocks/power to unit
@@ -92,7 +91,9 @@
* @iommu_list: iommu_list head
* @cnt_grp: list of counter groups
* @num_groups: number of counter groups
- * @event_cls_supp_value: event classes supported for this PMU
+ * @num_counters: number of counters per group
+ * @event_cls_supported: an array of event classes supported for this PMU
+ * @nevent_cls_supported: number of event classes supported.
* @enabled: Indicates whether perf. mon is enabled or not
* @iommu_attached Indicates whether iommu is attached or not.
* @lock: mutex used to synchronize access to shared data
@@ -102,8 +103,10 @@
struct iommu_info iommu;
struct list_head iommu_list;
struct iommu_pmon_cnt_group *cnt_grp;
- unsigned int num_groups;
- unsigned int event_cls_supp_value;
+ u32 num_groups;
+ u32 num_counters;
+ u32 *event_cls_supported;
+ u32 nevent_cls_supported;
unsigned int enabled;
unsigned int iommu_attach_count;
struct mutex lock;
@@ -114,9 +117,9 @@
#ifdef CONFIG_MSM_IOMMU_PMON
/**
* Allocate memory for performance monitor structure. Must
- * be called befre iommu_pm_iommu_register
+ * be called before iommu_pm_iommu_register
*/
-struct iommu_info *msm_iommu_pm_alloc(struct device *iommu_dev);
+struct iommu_pmon *msm_iommu_pm_alloc(struct device *iommu_dev);
/**
* Free memory previously allocated with iommu_pm_alloc
@@ -126,7 +129,7 @@
/**
* Register iommu with the performance monitor module.
*/
-int msm_iommu_pm_iommu_register(struct iommu_info *info);
+int msm_iommu_pm_iommu_register(struct iommu_pmon *info);
/**
* Unregister iommu with the performance monitor module.
@@ -147,7 +150,7 @@
*/
void msm_iommu_detached(struct device *dev);
#else
-static inline struct iommu_info *msm_iommu_pm_alloc(struct device *iommu_dev)
+static inline struct iommu_pmon *msm_iommu_pm_alloc(struct device *iommu_dev)
{
return NULL;
}
@@ -157,7 +160,7 @@
return;
}
-static inline int msm_iommu_pm_iommu_register(struct iommu_info *info)
+static inline int msm_iommu_pm_iommu_register(struct iommu_pmon *info)
{
return -EIO;
}
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index 99887af..ec761a9 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -139,23 +139,67 @@
}
static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
- struct iommu_info *pmon_info)
+ struct iommu_pmon *pmon_info)
{
int ret = 0;
int irq = platform_get_irq(pdev, 0);
+ unsigned int cls_prop_size;
if (irq > 0) {
- pmon_info->evt_irq = platform_get_irq(pdev, 0);
+ pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0);
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,iommu-pmu-ngroups",
+ &pmon_info->num_groups);
+ if (ret) {
+ pr_err("Error reading qcom,iommu-pmu-ngroups\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,iommu-pmu-ncounters",
+ &pmon_info->num_counters);
+ if (ret) {
+ pr_err("Error reading qcom,iommu-pmu-ncounters\n");
+ goto fail;
+ }
+
+ if (!of_get_property(pdev->dev.of_node,
+ "qcom,iommu-pmu-event-classes",
+ &cls_prop_size)) {
+ pr_err("Error reading qcom,iommu-pmu-event-classes\n");
+ return -EINVAL;
+ }
+
+ pmon_info->event_cls_supported =
+ devm_kzalloc(&pdev->dev, cls_prop_size, GFP_KERNEL);
+
+ if (!pmon_info->event_cls_supported) {
+ pr_err("Unable to get memory for event class array\n");
+ return -ENOMEM;
+ }
+
+ pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32);
+
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,iommu-pmu-event-classes",
+ pmon_info->event_cls_supported,
+ pmon_info->nevent_cls_supported);
+ if (ret) {
+ pr_err("Error reading qcom,iommu-pmu-event-classes\n");
+ return ret;
+ }
} else {
- pmon_info->evt_irq = -1;
+ pmon_info->iommu.evt_irq = -1;
ret = irq;
}
+
+fail:
return ret;
}
static int __devinit msm_iommu_probe(struct platform_device *pdev)
{
- struct iommu_info *pmon_info;
+ struct iommu_pmon *pmon_info;
struct msm_iommu_drvdata *drvdata;
struct resource *r;
int ret, needs_alt_core_clk;
@@ -224,9 +268,9 @@
msm_iommu_pm_free(&pdev->dev);
pr_info("%s: pmon not available.\n", drvdata->name);
} else {
- pmon_info->base = drvdata->base;
- pmon_info->ops = &iommu_access_ops;
- pmon_info->iommu_name = drvdata->name;
+ pmon_info->iommu.base = drvdata->base;
+ pmon_info->iommu.ops = &iommu_access_ops;
+ pmon_info->iommu.iommu_name = drvdata->name;
ret = msm_iommu_pm_iommu_register(pmon_info);
if (ret) {
pr_err("%s iommu register fail\n",
@@ -234,7 +278,7 @@
msm_iommu_pm_free(&pdev->dev);
} else {
pr_debug("%s iommu registered for pmon\n",
- pmon_info->iommu_name);
+ pmon_info->iommu.iommu_name);
}
}
}
diff --git a/drivers/iommu/msm_iommu_perfmon.c b/drivers/iommu/msm_iommu_perfmon.c
index a83b35d..97bd660 100644
--- a/drivers/iommu/msm_iommu_perfmon.c
+++ b/drivers/iommu/msm_iommu_perfmon.c
@@ -70,9 +70,6 @@
{ 0x10, "access" },
{ 0x11, "access_read" },
{ 0x12, "access_write" },
-};
-
-static struct event_class pmu_event_classes_impl_defined[] = {
{ 0x80, "full_misses" },
{ 0x81, "partial_miss_1lbfb_hit" },
{ 0x82, "partial_miss_2lbfb_hit" },
@@ -90,22 +87,28 @@
}
static unsigned int iommu_pm_create_sup_cls_str(char **buf,
- unsigned int event_cls)
+ struct iommu_pmon *pmon)
{
- unsigned long buf_size = (ARRAY_SIZE(pmu_event_classes) +
- ARRAY_SIZE(pmu_event_classes_impl_defined)) *
- MAX_EVEN_CLASS_NAME_LEN;
+ unsigned long buf_size = ARRAY_SIZE(pmu_event_classes) *
+ MAX_EVEN_CLASS_NAME_LEN;
unsigned int pos = 0;
+ unsigned int nevent_cls = pmon->nevent_cls_supported;
*buf = kzalloc(buf_size, GFP_KERNEL);
if (*buf) {
+ unsigned int j;
int i;
struct event_class *ptr;
size_t array_len = ARRAY_SIZE(pmu_event_classes);
ptr = pmu_event_classes;
- for (i = 0; i < array_len; ++i) {
- if ((1 << ptr[i].event_number) & event_cls) {
+ for (j = 0; j < nevent_cls; ++j) {
+ for (i = 0; i < array_len; ++i) {
+
+ if (ptr[i].event_number !=
+ pmon->event_cls_supported[j])
+ continue;
+
if (pos < buf_size) {
pos += snprintf(&(*buf)[pos],
buf_size-pos,
@@ -113,25 +116,7 @@
ptr[i].event_number,
ptr[i].desc);
}
-
- }
- }
-
- /*
- * No way to read a register to check if impl. defined
- * classes are supported or not so we just assume all of them
- * are
- */
- array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
- ptr = pmu_event_classes_impl_defined;
-
- for (i = 0; i < array_len; ++i) {
- if (buf_size > pos) {
- pos += snprintf(&(*buf)[pos],
- buf_size-pos,
- "[%u] %s\n",
- ptr[i].event_number,
- ptr[i].desc);
+ break;
}
}
}
@@ -144,16 +129,11 @@
struct event_class *ptr;
int i;
const char *event_class_name = NO_EVENT_CLASS_NAME;
- if (event_class < 0) {
+ if (event_class < 0)
goto out;
- } else if (event_class < 0x80) {
- array_len = ARRAY_SIZE(pmu_event_classes);
- ptr = pmu_event_classes;
- } else {
- /* All implementation defined classes are above 0x7F */
- array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
- ptr = pmu_event_classes_impl_defined;
- }
+
+ array_len = ARRAY_SIZE(pmu_event_classes);
+ ptr = pmu_event_classes;
for (i = 0; i < array_len; ++i) {
if (ptr[i].event_number == event_class) {
@@ -186,16 +166,6 @@
}
}
- array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
- ptr = pmu_event_classes_impl_defined;
-
- for (i = 0; i < array_len; ++i) {
- if (strcmp(ptr[i].desc, event_class_name) == 0) {
- event_class = ptr[i].event_number;
- goto out;
- }
- }
-
out:
return event_class;
}
@@ -248,7 +218,7 @@
writel_relaxed(pmcr, iommu->base + PMCR);
}
-void iommu_pm_disable(struct iommu_info *iommu)
+static void iommu_pm_disable(struct iommu_info *iommu)
{
unsigned int pmcr;
pmcr = readl_relaxed(iommu->base + PMCR);
@@ -533,48 +503,6 @@
pmon->iommu.iommu_name);
}
-static unsigned int iommu_pm_get_num_groups(struct iommu_pmon *iommu_pmon)
-{
- unsigned int pmcfgr;
- unsigned int num_cntgrp;
- struct iommu_info *iommu = &iommu_pmon->iommu;
-
- pmcfgr = readl_relaxed(iommu->base + PMCFGR);
-
- /* Due to a bug in IOMMU hardware the counter register is returning
- * the wrong information. num_cntgrp should return "total number
- * of counter groups - 1". However, it returns "total number
- * of counter groups". Thus we do not add 1 to the total number of
- * counter groups. If we find that the number of counter group is 0
- * then we assume the bug has been fixed and add 1 to the count.
- */
- num_cntgrp = ((pmcfgr & PMCFGR_NCG) >> PMCFGR_NCG_SHIFT);
- if (num_cntgrp == 0)
- num_cntgrp++;
-
- return num_cntgrp;
-
-}
-
-static unsigned int iommu_pm_get_num_counters(struct iommu_pmon *iommu_pmon,
- unsigned int group_no)
-{
- unsigned int num_counters;
- unsigned int tmp;
- struct iommu_info *iommu = &iommu_pmon->iommu;
-
- tmp = readl_relaxed(iommu->base + PMCGCR_(group_no));
- num_counters = ((tmp & PMCGCR_CGNC) >> PMCGCR_CGNC_SHIFT);
-
- return num_counters;
-
-}
-
-static unsigned int iommu_pm_get_sup_ev_cls(struct iommu_info *iommu)
-{
- return readl_relaxed(iommu->base + PMCEID0);
-}
-
static int iommu_pm_debug_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -659,7 +587,7 @@
int rv;
long value;
buf[wr_cnt-1] = '\0';
- rv = kstrtol(buf, 50, &value);
+ rv = kstrtol(buf, 10, &value);
if (!rv) {
counter->current_event_class =
iommu_pm_find_event_class(
@@ -799,7 +727,7 @@
mutex_lock(&pmon->lock);
- len = iommu_pm_create_sup_cls_str(&buf, pmon->event_cls_supp_value);
+ len = iommu_pm_create_sup_cls_str(&buf, pmon);
if (buf) {
rd_cnt = simple_read_from_buffer(user_buff, count, pos,
buf, len);
@@ -877,8 +805,7 @@
for (i = 0; i < pmon_entry->num_groups; ++i) {
pmon_entry->cnt_grp[i].pmon = pmon_entry;
pmon_entry->cnt_grp[i].grp_no = i;
- pmon_entry->cnt_grp[i].num_counters =
- iommu_pm_get_num_counters(pmon_entry, i);
+ pmon_entry->cnt_grp[i].num_counters = pmon_entry->num_counters;
pmon_entry->cnt_grp[i].counters =
kzalloc(sizeof(*pmon_entry->cnt_grp[i].counters)
* pmon_entry->cnt_grp[i].num_counters, GFP_KERNEL);
@@ -907,11 +834,10 @@
return ret;
}
-int msm_iommu_pm_iommu_register(struct iommu_info *iommu)
+int msm_iommu_pm_iommu_register(struct iommu_pmon *pmon_entry)
{
- struct iommu_pmon *pmon_entry;
int ret = 0;
- struct msm_iommu_drvdata *iommu_drvdata;
+ struct iommu_info *iommu = &pmon_entry->iommu;
int i;
if (!iommu->ops || !iommu->iommu_name || !iommu->base
@@ -928,14 +854,7 @@
goto out;
}
}
- pmon_entry = (struct iommu_pmon *)container_of(iommu,
- struct iommu_pmon, iommu);
- iommu_drvdata = dev_get_drvdata(iommu->iommu_dev);
- iommu->ops->iommu_power_on(iommu_drvdata);
- iommu->ops->iommu_lock_acquire();
-
- pmon_entry->num_groups = iommu_pm_get_num_groups(pmon_entry);
pmon_entry->cnt_grp = kzalloc(sizeof(*pmon_entry->cnt_grp)
* pmon_entry->num_groups, GFP_KERNEL);
if (!pmon_entry->cnt_grp) {
@@ -943,8 +862,6 @@
ret = -ENOMEM;
goto file_err;
}
- pmon_entry->event_cls_supp_value = iommu_pm_get_sup_ev_cls(iommu);
-
pmon_entry->iommu_dir = debugfs_create_dir(iommu->iommu_name,
msm_iommu_root_debugfs_dir);
if (IS_ERR_OR_NULL(pmon_entry->iommu_dir)) {
@@ -977,9 +894,6 @@
if (ret)
goto free_mem;
- iommu->ops->iommu_lock_release();
- iommu->ops->iommu_power_off(iommu_drvdata);
-
if (iommu->evt_irq > 0) {
ret = request_threaded_irq(iommu->evt_irq, NULL,
iommu_pm_evt_ovfl_int_handler,
@@ -995,8 +909,7 @@
pr_info("%s: Overflow interrupt not available\n", __func__);
}
- dev_dbg(iommu->iommu_dev, "%s iommu registered\n",
- iommu->iommu_name);
+ dev_dbg(iommu->iommu_dev, "%s iommu registered\n", iommu->iommu_name);
goto out;
free_mem:
@@ -1042,7 +955,7 @@
}
EXPORT_SYMBOL(msm_iommu_pm_iommu_unregister);
-struct iommu_info *msm_iommu_pm_alloc(struct device *dev)
+struct iommu_pmon *msm_iommu_pm_alloc(struct device *dev)
{
struct iommu_pmon *pmon_entry;
struct iommu_info *info;
@@ -1053,7 +966,7 @@
info->iommu_dev = dev;
mutex_init(&pmon_entry->lock);
iommu_pm_add_to_iommu_list(pmon_entry);
- return &pmon_entry->iommu;
+ return pmon_entry;
}
EXPORT_SYMBOL(msm_iommu_pm_alloc);