diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt index 67ce9fc8aae..2c47f74944f 100644 --- a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt +++ b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt @@ -18,6 +18,9 @@ Optional properties: - 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 @@ Example: 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 b44523fe503..59f58c1b51f 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 @@ struct iommu_info { * @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_pmon { 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 @@ extern struct iommu_access_ops iommu_access_ops; #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 @@ void msm_iommu_pm_free(struct device *iommu_dev); /** * 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_attached(struct device *dev); */ 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 @@ static inline void msm_iommu_pm_free(struct device *iommu_dev) 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 99887af5397..ec761a9b64a 100644 --- a/drivers/iommu/msm_iommu_dev-v1.c +++ b/drivers/iommu/msm_iommu_dev-v1.c @@ -139,23 +139,67 @@ fail: } 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 @@ static int __devinit msm_iommu_probe(struct platform_device *pdev) 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 @@ static int __devinit msm_iommu_probe(struct platform_device *pdev) 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 a83b35d43a4..97bd66011cf 100644 --- a/drivers/iommu/msm_iommu_perfmon.c +++ b/drivers/iommu/msm_iommu_perfmon.c @@ -70,9 +70,6 @@ static struct event_class pmu_event_classes[] = { { 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_is_hw_access_OK(const struct iommu_pmon *pmon) } 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 @@ static unsigned int iommu_pm_create_sup_cls_str(char **buf, 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 @@ static const char *iommu_pm_find_event_class_name(int event_class) 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 @@ static int iommu_pm_find_event_class(const char *event_class_name) } } - 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 @@ static void iommu_pm_enable(struct iommu_info *iommu) 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 @@ static void iommu_pm_off(struct iommu_pmon *pmon) 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 @@ static ssize_t iommu_pm_event_class_write(struct file *fp, 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 @@ static ssize_t iommu_pm_avail_event_cls_read(struct file *fp, 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 @@ static int iommu_pm_create_group_debugfs_hierarchy(struct iommu_info *iommu, 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 @@ out: 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 @@ int msm_iommu_pm_iommu_register(struct iommu_info *iommu) 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 @@ int msm_iommu_pm_iommu_register(struct iommu_info *iommu) 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 @@ int msm_iommu_pm_iommu_register(struct iommu_info *iommu) 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 @@ int msm_iommu_pm_iommu_register(struct iommu_info *iommu) 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 @@ remove_debugfs: } 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 @@ struct iommu_info *msm_iommu_pm_alloc(struct device *dev) 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);