diff options
Diffstat (limited to 'drivers/perf')
| -rw-r--r-- | drivers/perf/arm-cci.c | 12 | ||||
| -rw-r--r-- | drivers/perf/arm-ccn.c | 31 | ||||
| -rw-r--r-- | drivers/perf/arm-cmn.c | 22 | ||||
| -rw-r--r-- | drivers/perf/arm_dmc620_pmu.c | 2 | ||||
| -rw-r--r-- | drivers/perf/arm_dsu_pmu.c | 5 | ||||
| -rw-r--r-- | drivers/perf/arm_pmu.c | 30 | ||||
| -rw-r--r-- | drivers/perf/arm_pmu_platform.c | 54 | ||||
| -rw-r--r-- | drivers/perf/arm_smmuv3_pmu.c | 36 | ||||
| -rw-r--r-- | drivers/perf/arm_spe_pmu.c | 3 | ||||
| -rw-r--r-- | drivers/perf/fsl_imx8_ddr_perf.c | 7 | ||||
| -rw-r--r-- | drivers/perf/hisilicon/Makefile | 3 | ||||
| -rw-r--r-- | drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 348 | ||||
| -rw-r--r-- | drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 301 | ||||
| -rw-r--r-- | drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 355 | ||||
| -rw-r--r-- | drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 500 | ||||
| -rw-r--r-- | drivers/perf/hisilicon/hisi_uncore_pmu.c | 79 | ||||
| -rw-r--r-- | drivers/perf/hisilicon/hisi_uncore_pmu.h | 20 | ||||
| -rw-r--r-- | drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c | 530 | ||||
| -rw-r--r-- | drivers/perf/qcom_l2_pmu.c | 2 | ||||
| -rw-r--r-- | drivers/perf/qcom_l3_pmu.c | 4 | ||||
| -rw-r--r-- | drivers/perf/thunderx2_pmu.c | 4 | ||||
| -rw-r--r-- | drivers/perf/xgene_pmu.c | 4 | 
22 files changed, 1901 insertions, 451 deletions
| diff --git a/drivers/perf/arm-cci.c b/drivers/perf/arm-cci.c index f81e2ec90005..666d8a9b557f 100644 --- a/drivers/perf/arm-cci.c +++ b/drivers/perf/arm-cci.c @@ -306,7 +306,7 @@ static ssize_t cci400_pmu_cycle_event_show(struct device *dev,  {  	struct dev_ext_attribute *eattr = container_of(attr,  				struct dev_ext_attribute, attr); -	return snprintf(buf, PAGE_SIZE, "config=0x%lx\n", (unsigned long)eattr->var); +	return sysfs_emit(buf, "config=0x%lx\n", (unsigned long)eattr->var);  }  static int cci400_get_event_idx(struct cci_pmu *cci_pmu, @@ -525,8 +525,8 @@ static ssize_t cci5xx_pmu_global_event_show(struct device *dev,  	struct dev_ext_attribute *eattr = container_of(attr,  					struct dev_ext_attribute, attr);  	/* Global events have single fixed source code */ -	return snprintf(buf, PAGE_SIZE, "event=0x%lx,source=0x%x\n", -				(unsigned long)eattr->var, CCI5xx_PORT_GLOBAL); +	return sysfs_emit(buf, "event=0x%lx,source=0x%x\n", +			  (unsigned long)eattr->var, CCI5xx_PORT_GLOBAL);  }  /* @@ -696,7 +696,7 @@ static ssize_t cci_pmu_format_show(struct device *dev,  {  	struct dev_ext_attribute *eattr = container_of(attr,  				struct dev_ext_attribute, attr); -	return snprintf(buf, PAGE_SIZE, "%s\n", (char *)eattr->var); +	return sysfs_emit(buf, "%s\n", (char *)eattr->var);  }  static ssize_t cci_pmu_event_show(struct device *dev, @@ -705,8 +705,8 @@ static ssize_t cci_pmu_event_show(struct device *dev,  	struct dev_ext_attribute *eattr = container_of(attr,  				struct dev_ext_attribute, attr);  	/* source parameter is mandatory for normal PMU events */ -	return snprintf(buf, PAGE_SIZE, "source=?,event=0x%lx\n", -					 (unsigned long)eattr->var); +	return sysfs_emit(buf, "source=?,event=0x%lx\n", +			  (unsigned long)eattr->var);  }  static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) diff --git a/drivers/perf/arm-ccn.c b/drivers/perf/arm-ccn.c index a0a71c1df042..96d47cb302dd 100644 --- a/drivers/perf/arm-ccn.c +++ b/drivers/perf/arm-ccn.c @@ -221,7 +221,7 @@ static ssize_t arm_ccn_pmu_format_show(struct device *dev,  	struct dev_ext_attribute *ea = container_of(attr,  			struct dev_ext_attribute, attr); -	return snprintf(buf, PAGE_SIZE, "%s\n", (char *)ea->var); +	return sysfs_emit(buf, "%s\n", (char *)ea->var);  }  #define CCN_FORMAT_ATTR(_name, _config) \ @@ -326,43 +326,38 @@ static ssize_t arm_ccn_pmu_event_show(struct device *dev,  	struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));  	struct arm_ccn_pmu_event *event = container_of(attr,  			struct arm_ccn_pmu_event, attr); -	ssize_t res; +	int res; -	res = scnprintf(buf, PAGE_SIZE, "type=0x%x", event->type); +	res = sysfs_emit(buf, "type=0x%x", event->type);  	if (event->event) -		res += scnprintf(buf + res, PAGE_SIZE - res, ",event=0x%x", -				event->event); +		res += sysfs_emit_at(buf, res, ",event=0x%x", event->event);  	if (event->def) -		res += scnprintf(buf + res, PAGE_SIZE - res, ",%s", -				event->def); +		res += sysfs_emit_at(buf, res, ",%s", event->def);  	if (event->mask) -		res += scnprintf(buf + res, PAGE_SIZE - res, ",mask=0x%x", -				event->mask); +		res += sysfs_emit_at(buf, res, ",mask=0x%x", event->mask);  	/* Arguments required by an event */  	switch (event->type) {  	case CCN_TYPE_CYCLES:  		break;  	case CCN_TYPE_XP: -		res += scnprintf(buf + res, PAGE_SIZE - res, -				",xp=?,vc=?"); +		res += sysfs_emit_at(buf, res, ",xp=?,vc=?");  		if (event->event == CCN_EVENT_WATCHPOINT) -			res += scnprintf(buf + res, PAGE_SIZE - res, +			res += sysfs_emit_at(buf, res,  					",port=?,dir=?,cmp_l=?,cmp_h=?,mask=?");  		else -			res += scnprintf(buf + res, PAGE_SIZE - res, -					",bus=?"); +			res += sysfs_emit_at(buf, res, ",bus=?");  		break;  	case CCN_TYPE_MN: -		res += scnprintf(buf + res, PAGE_SIZE - res, ",node=%d", ccn->mn_id); +		res += sysfs_emit_at(buf, res, ",node=%d", ccn->mn_id);  		break;  	default: -		res += scnprintf(buf + res, PAGE_SIZE - res, ",node=?"); +		res += sysfs_emit_at(buf, res, ",node=?");  		break;  	} -	res += scnprintf(buf + res, PAGE_SIZE - res, "\n"); +	res += sysfs_emit_at(buf, res, "\n");  	return res;  } @@ -476,7 +471,7 @@ static ssize_t arm_ccn_pmu_cmp_mask_show(struct device *dev,  	struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));  	u64 *mask = arm_ccn_pmu_get_cmp_mask(ccn, attr->attr.name); -	return mask ? snprintf(buf, PAGE_SIZE, "0x%016llx\n", *mask) : -EINVAL; +	return mask ? sysfs_emit(buf, "0x%016llx\n", *mask) : -EINVAL;  }  static ssize_t arm_ccn_pmu_cmp_mask_store(struct device *dev, diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 1328159fe564..56a5c355701d 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -348,19 +348,19 @@ static ssize_t arm_cmn_event_show(struct device *dev,  	eattr = container_of(attr, typeof(*eattr), attr);  	if (eattr->type == CMN_TYPE_DTC) -		return snprintf(buf, PAGE_SIZE, "type=0x%x\n", eattr->type); +		return sysfs_emit(buf, "type=0x%x\n", eattr->type);  	if (eattr->type == CMN_TYPE_WP) -		return snprintf(buf, PAGE_SIZE, -				"type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n", -				eattr->type, eattr->eventid); +		return sysfs_emit(buf, +				  "type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n", +				  eattr->type, eattr->eventid);  	if (arm_cmn_is_occup_event(eattr->type, eattr->eventid)) -		return snprintf(buf, PAGE_SIZE, "type=0x%x,eventid=0x%x,occupid=0x%x\n", -				eattr->type, eattr->eventid, eattr->occupid); +		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,occupid=0x%x\n", +				  eattr->type, eattr->eventid, eattr->occupid); -	return snprintf(buf, PAGE_SIZE, "type=0x%x,eventid=0x%x\n", -			eattr->type, eattr->eventid); +	return sysfs_emit(buf, "type=0x%x,eventid=0x%x\n", eattr->type, +			  eattr->eventid);  }  static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, @@ -560,12 +560,12 @@ static ssize_t arm_cmn_format_show(struct device *dev,  	int lo = __ffs(fmt->field), hi = __fls(fmt->field);  	if (lo == hi) -		return snprintf(buf, PAGE_SIZE, "config:%d\n", lo); +		return sysfs_emit(buf, "config:%d\n", lo);  	if (!fmt->config) -		return snprintf(buf, PAGE_SIZE, "config:%d-%d\n", lo, hi); +		return sysfs_emit(buf, "config:%d-%d\n", lo, hi); -	return snprintf(buf, PAGE_SIZE, "config%d:%d-%d\n", fmt->config, lo, hi); +	return sysfs_emit(buf, "config%d:%d-%d\n", fmt->config, lo, hi);  }  #define _CMN_FORMAT_ATTR(_name, _cfg, _fld)				\ diff --git a/drivers/perf/arm_dmc620_pmu.c b/drivers/perf/arm_dmc620_pmu.c index f2a85500258d..b6c2511d59af 100644 --- a/drivers/perf/arm_dmc620_pmu.c +++ b/drivers/perf/arm_dmc620_pmu.c @@ -113,7 +113,7 @@ dmc620_pmu_event_show(struct device *dev,  	eattr = container_of(attr, typeof(*eattr), attr); -	return sprintf(page, "event=0x%x,clkdiv2=0x%x\n", eattr->eventid, eattr->clkdiv2); +	return sysfs_emit(page, "event=0x%x,clkdiv2=0x%x\n", eattr->eventid, eattr->clkdiv2);  }  #define DMC620_PMU_EVENT_ATTR(_name, _eventid, _clkdiv2)		\ diff --git a/drivers/perf/arm_dsu_pmu.c b/drivers/perf/arm_dsu_pmu.c index 0459a3403469..196faea074d0 100644 --- a/drivers/perf/arm_dsu_pmu.c +++ b/drivers/perf/arm_dsu_pmu.c @@ -136,8 +136,7 @@ static ssize_t dsu_pmu_sysfs_event_show(struct device *dev,  {  	struct dev_ext_attribute *eattr = container_of(attr,  					struct dev_ext_attribute, attr); -	return snprintf(buf, PAGE_SIZE, "event=0x%lx\n", -					 (unsigned long)eattr->var); +	return sysfs_emit(buf, "event=0x%lx\n", (unsigned long)eattr->var);  }  static ssize_t dsu_pmu_sysfs_format_show(struct device *dev, @@ -146,7 +145,7 @@ static ssize_t dsu_pmu_sysfs_format_show(struct device *dev,  {  	struct dev_ext_attribute *eattr = container_of(attr,  					struct dev_ext_attribute, attr); -	return snprintf(buf, PAGE_SIZE, "%s\n", (char *)eattr->var); +	return sysfs_emit(buf, "%s\n", (char *)eattr->var);  }  static ssize_t dsu_pmu_cpumask_show(struct device *dev, diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 2d10d84fb79c..d4f7f1f9cc77 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -581,33 +581,6 @@ static const struct attribute_group armpmu_common_attr_group = {  	.attrs = armpmu_common_attrs,  }; -/* Set at runtime when we know what CPU type we are. */ -static struct arm_pmu *__oprofile_cpu_pmu; - -/* - * Despite the names, these two functions are CPU-specific and are used - * by the OProfile/perf code. - */ -const char *perf_pmu_name(void) -{ -	if (!__oprofile_cpu_pmu) -		return NULL; - -	return __oprofile_cpu_pmu->name; -} -EXPORT_SYMBOL_GPL(perf_pmu_name); - -int perf_num_counters(void) -{ -	int max_events = 0; - -	if (__oprofile_cpu_pmu != NULL) -		max_events = __oprofile_cpu_pmu->num_events; - -	return max_events; -} -EXPORT_SYMBOL_GPL(perf_num_counters); -  static int armpmu_count_irq_users(const int irq)  {  	int cpu, count = 0; @@ -979,9 +952,6 @@ int armpmu_register(struct arm_pmu *pmu)  	if (ret)  		goto out_destroy; -	if (!__oprofile_cpu_pmu) -		__oprofile_cpu_pmu = pmu; -  	pr_info("enabled with %s PMU driver, %d counters available%s\n",  		pmu->name, pmu->num_events,  		has_nmi ? ", using NMIs" : ""); diff --git a/drivers/perf/arm_pmu_platform.c b/drivers/perf/arm_pmu_platform.c index 933bd8410fc2..513de1f54e2d 100644 --- a/drivers/perf/arm_pmu_platform.c +++ b/drivers/perf/arm_pmu_platform.c @@ -6,6 +6,7 @@   * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>   */  #define pr_fmt(fmt) "hw perfevents: " fmt +#define dev_fmt pr_fmt  #include <linux/bug.h>  #include <linux/cpumask.h> @@ -62,7 +63,7 @@ static bool pmu_has_irq_affinity(struct device_node *node)  	return !!of_find_property(node, "interrupt-affinity", NULL);  } -static int pmu_parse_irq_affinity(struct device_node *node, int i) +static int pmu_parse_irq_affinity(struct device *dev, int i)  {  	struct device_node *dn;  	int cpu; @@ -72,19 +73,18 @@ static int pmu_parse_irq_affinity(struct device_node *node, int i)  	 * affinity matches our logical CPU order, as we used to assume.  	 * This is fragile, so we'll warn in pmu_parse_irqs().  	 */ -	if (!pmu_has_irq_affinity(node)) +	if (!pmu_has_irq_affinity(dev->of_node))  		return i; -	dn = of_parse_phandle(node, "interrupt-affinity", i); +	dn = of_parse_phandle(dev->of_node, "interrupt-affinity", i);  	if (!dn) { -		pr_warn("failed to parse interrupt-affinity[%d] for %pOFn\n", -			i, node); +		dev_warn(dev, "failed to parse interrupt-affinity[%d]\n", i);  		return -EINVAL;  	}  	cpu = of_cpu_node_to_id(dn);  	if (cpu < 0) { -		pr_warn("failed to find logical CPU for %pOFn\n", dn); +		dev_warn(dev, "failed to find logical CPU for %pOFn\n", dn);  		cpu = nr_cpu_ids;  	} @@ -98,19 +98,18 @@ static int pmu_parse_irqs(struct arm_pmu *pmu)  	int i = 0, num_irqs;  	struct platform_device *pdev = pmu->plat_device;  	struct pmu_hw_events __percpu *hw_events = pmu->hw_events; +	struct device *dev = &pdev->dev;  	num_irqs = platform_irq_count(pdev); -	if (num_irqs < 0) { -		pr_err("unable to count PMU IRQs\n"); -		return num_irqs; -	} +	if (num_irqs < 0) +		return dev_err_probe(dev, num_irqs, "unable to count PMU IRQs\n");  	/*  	 * In this case we have no idea which CPUs are covered by the PMU.  	 * To match our prior behaviour, we assume all CPUs in this case.  	 */  	if (num_irqs == 0) { -		pr_warn("no irqs for PMU, sampling events not supported\n"); +		dev_warn(dev, "no irqs for PMU, sampling events not supported\n");  		pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;  		cpumask_setall(&pmu->supported_cpus);  		return 0; @@ -122,10 +121,8 @@ static int pmu_parse_irqs(struct arm_pmu *pmu)  			return pmu_parse_percpu_irq(pmu, irq);  	} -	if (nr_cpu_ids != 1 && !pmu_has_irq_affinity(pdev->dev.of_node)) { -		pr_warn("no interrupt-affinity property for %pOF, guessing.\n", -			pdev->dev.of_node); -	} +	if (nr_cpu_ids != 1 && !pmu_has_irq_affinity(dev->of_node)) +		dev_warn(dev, "no interrupt-affinity property, guessing.\n");  	for (i = 0; i < num_irqs; i++) {  		int cpu, irq; @@ -135,18 +132,18 @@ static int pmu_parse_irqs(struct arm_pmu *pmu)  			continue;  		if (irq_is_percpu_devid(irq)) { -			pr_warn("multiple PPIs or mismatched SPI/PPI detected\n"); +			dev_warn(dev, "multiple PPIs or mismatched SPI/PPI detected\n");  			return -EINVAL;  		} -		cpu = pmu_parse_irq_affinity(pdev->dev.of_node, i); +		cpu = pmu_parse_irq_affinity(dev, i);  		if (cpu < 0)  			return cpu;  		if (cpu >= nr_cpu_ids)  			continue;  		if (per_cpu(hw_events->irq, cpu)) { -			pr_warn("multiple PMU IRQs for the same CPU detected\n"); +			dev_warn(dev, "multiple PMU IRQs for the same CPU detected\n");  			return -EINVAL;  		} @@ -191,9 +188,8 @@ int arm_pmu_device_probe(struct platform_device *pdev,  			 const struct of_device_id *of_table,  			 const struct pmu_probe_info *probe_table)  { -	const struct of_device_id *of_id;  	armpmu_init_fn init_fn; -	struct device_node *node = pdev->dev.of_node; +	struct device *dev = &pdev->dev;  	struct arm_pmu *pmu;  	int ret = -ENODEV; @@ -207,15 +203,14 @@ int arm_pmu_device_probe(struct platform_device *pdev,  	if (ret)  		goto out_free; -	if (node && (of_id = of_match_node(of_table, pdev->dev.of_node))) { -		init_fn = of_id->data; - -		pmu->secure_access = of_property_read_bool(pdev->dev.of_node, +	init_fn = of_device_get_match_data(dev); +	if (init_fn) { +		pmu->secure_access = of_property_read_bool(dev->of_node,  							   "secure-reg-access");  		/* arm64 systems boot only as non-secure */  		if (IS_ENABLED(CONFIG_ARM64) && pmu->secure_access) { -			pr_warn("ignoring \"secure-reg-access\" property for arm64\n"); +			dev_warn(dev, "ignoring \"secure-reg-access\" property for arm64\n");  			pmu->secure_access = false;  		} @@ -226,7 +221,7 @@ int arm_pmu_device_probe(struct platform_device *pdev,  	}  	if (ret) { -		pr_info("%pOF: failed to probe PMU!\n", node); +		dev_err(dev, "failed to probe PMU!\n");  		goto out_free;  	} @@ -235,15 +230,16 @@ int arm_pmu_device_probe(struct platform_device *pdev,  		goto out_free_irqs;  	ret = armpmu_register(pmu); -	if (ret) -		goto out_free; +	if (ret) { +		dev_err(dev, "failed to register PMU devices!\n"); +		goto out_free_irqs; +	}  	return 0;  out_free_irqs:  	armpmu_free_irqs(pmu);  out_free: -	pr_info("%pOF: failed to register PMU devices!\n", node);  	armpmu_free(pmu);  	return ret;  } diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c index 8ff7a67f691c..ff6fab4bae30 100644 --- a/drivers/perf/arm_smmuv3_pmu.c +++ b/drivers/perf/arm_smmuv3_pmu.c @@ -506,30 +506,24 @@ static ssize_t smmu_pmu_event_show(struct device *dev,  	pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); -	return sprintf(page, "event=0x%02llx\n", pmu_attr->id); +	return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id);  } -#define SMMU_EVENT_ATTR(name, config) \ -	PMU_EVENT_ATTR(name, smmu_event_attr_##name, \ -		       config, smmu_pmu_event_show) -SMMU_EVENT_ATTR(cycles, 0); -SMMU_EVENT_ATTR(transaction, 1); -SMMU_EVENT_ATTR(tlb_miss, 2); -SMMU_EVENT_ATTR(config_cache_miss, 3); -SMMU_EVENT_ATTR(trans_table_walk_access, 4); -SMMU_EVENT_ATTR(config_struct_access, 5); -SMMU_EVENT_ATTR(pcie_ats_trans_rq, 6); -SMMU_EVENT_ATTR(pcie_ats_trans_passed, 7); +#define SMMU_EVENT_ATTR(name, config)					\ +	(&((struct perf_pmu_events_attr) {				\ +		.attr = __ATTR(name, 0444, smmu_pmu_event_show, NULL),	\ +		.id = config,						\ +	}).attr.attr)  static struct attribute *smmu_pmu_events[] = { -	&smmu_event_attr_cycles.attr.attr, -	&smmu_event_attr_transaction.attr.attr, -	&smmu_event_attr_tlb_miss.attr.attr, -	&smmu_event_attr_config_cache_miss.attr.attr, -	&smmu_event_attr_trans_table_walk_access.attr.attr, -	&smmu_event_attr_config_struct_access.attr.attr, -	&smmu_event_attr_pcie_ats_trans_rq.attr.attr, -	&smmu_event_attr_pcie_ats_trans_passed.attr.attr, +	SMMU_EVENT_ATTR(cycles, 0), +	SMMU_EVENT_ATTR(transaction, 1), +	SMMU_EVENT_ATTR(tlb_miss, 2), +	SMMU_EVENT_ATTR(config_cache_miss, 3), +	SMMU_EVENT_ATTR(trans_table_walk_access, 4), +	SMMU_EVENT_ATTR(config_struct_access, 5), +	SMMU_EVENT_ATTR(pcie_ats_trans_rq, 6), +	SMMU_EVENT_ATTR(pcie_ats_trans_passed, 7),  	NULL  }; @@ -560,7 +554,7 @@ static ssize_t smmu_pmu_identifier_attr_show(struct device *dev,  {  	struct smmu_pmu *smmu_pmu = to_smmu_pmu(dev_get_drvdata(dev)); -	return snprintf(page, PAGE_SIZE, "0x%08x\n", smmu_pmu->iidr); +	return sysfs_emit(page, "0x%08x\n", smmu_pmu->iidr);  }  static umode_t smmu_pmu_identifier_attr_visible(struct kobject *kobj, diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c index d3929ccebfd2..8a1e86ab2d8e 100644 --- a/drivers/perf/arm_spe_pmu.c +++ b/drivers/perf/arm_spe_pmu.c @@ -126,8 +126,7 @@ static ssize_t arm_spe_pmu_cap_show(struct device *dev,  		container_of(attr, struct dev_ext_attribute, attr);  	int cap = (long)ea->var; -	return snprintf(buf, PAGE_SIZE, "%u\n", -		arm_spe_pmu_cap_get(spe_pmu, cap)); +	return sysfs_emit(buf, "%u\n", arm_spe_pmu_cap_get(spe_pmu, cap));  }  #define SPE_EXT_ATTR_ENTRY(_name, _func, _var)				\ diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c index be1f26b62ddb..2bbb93188064 100644 --- a/drivers/perf/fsl_imx8_ddr_perf.c +++ b/drivers/perf/fsl_imx8_ddr_perf.c @@ -110,7 +110,7 @@ static ssize_t ddr_perf_identifier_show(struct device *dev,  {  	struct ddr_pmu *pmu = dev_get_drvdata(dev); -	return sprintf(page, "%s\n", pmu->devtype_data->identifier); +	return sysfs_emit(page, "%s\n", pmu->devtype_data->identifier);  }  static umode_t ddr_perf_identifier_attr_visible(struct kobject *kobj, @@ -170,8 +170,7 @@ static ssize_t ddr_perf_filter_cap_show(struct device *dev,  		container_of(attr, struct dev_ext_attribute, attr);  	int cap = (long)ea->var; -	return snprintf(buf, PAGE_SIZE, "%u\n", -			ddr_perf_filter_cap_get(pmu, cap)); +	return sysfs_emit(buf, "%u\n", ddr_perf_filter_cap_get(pmu, cap));  }  #define PERF_EXT_ATTR_ENTRY(_name, _func, _var)				\ @@ -220,7 +219,7 @@ ddr_pmu_event_show(struct device *dev, struct device_attribute *attr,  	struct perf_pmu_events_attr *pmu_attr;  	pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); -	return sprintf(page, "event=0x%02llx\n", pmu_attr->id); +	return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id);  }  #define IMX8_DDR_PMU_EVENT_ATTR(_name, _id)				\ diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index e8377061845f..7643c9f93e36 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -1,3 +1,4 @@  # SPDX-License-Identifier: GPL-2.0-only  obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o \ -			  hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o +			  hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o hisi_uncore_sllc_pmu.o \ +			  hisi_uncore_pa_pmu.o diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index ac1a8c120a00..7c8a4bc21db4 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -14,12 +14,11 @@  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/list.h> -#include <linux/platform_device.h>  #include <linux/smp.h>  #include "hisi_uncore_pmu.h" -/* DDRC register definition */ +/* DDRC register definition in v1 */  #define DDRC_PERF_CTRL		0x010  #define DDRC_FLUX_WR		0x380  #define DDRC_FLUX_RD		0x384 @@ -35,12 +34,24 @@  #define DDRC_INT_CLEAR		0x6d0  #define DDRC_VERSION		0x710 +/* DDRC register definition in v2 */ +#define DDRC_V2_INT_MASK	0x528 +#define DDRC_V2_INT_STATUS	0x52c +#define DDRC_V2_INT_CLEAR	0x530 +#define DDRC_V2_EVENT_CNT	0xe00 +#define DDRC_V2_EVENT_CTRL	0xe70 +#define DDRC_V2_EVENT_TYPE	0xe74 +#define DDRC_V2_PERF_CTRL	0xeA0 +  /* DDRC has 8-counters */  #define DDRC_NR_COUNTERS	0x8 -#define DDRC_PERF_CTRL_EN	0x2 +#define DDRC_V1_PERF_CTRL_EN	0x2 +#define DDRC_V2_PERF_CTRL_EN	0x1 +#define DDRC_V1_NR_EVENTS	0x7 +#define DDRC_V2_NR_EVENTS	0x90  /* - * For DDRC PMU, there are eight-events and every event has been mapped + * For PMU v1, there are eight-events and every event has been mapped   * to fixed-purpose counters which register offset is not consistent.   * Therefore there is no write event type and we assume that event   * code (0 to 7) is equal to counter index in PMU driver. @@ -54,73 +65,85 @@ static const u32 ddrc_reg_off[] = {  /*   * Select the counter register offset using the counter index. - * In DDRC there are no programmable counter, the count - * is readed form the statistics counter register itself. + * In PMU v1, there are no programmable counter, the count + * is read form the statistics counter register itself.   */ -static u32 hisi_ddrc_pmu_get_counter_offset(int cntr_idx) +static u32 hisi_ddrc_pmu_v1_get_counter_offset(int cntr_idx)  {  	return ddrc_reg_off[cntr_idx];  } -static u64 hisi_ddrc_pmu_read_counter(struct hisi_pmu *ddrc_pmu, -				      struct hw_perf_event *hwc) +static u32 hisi_ddrc_pmu_v2_get_counter_offset(int cntr_idx)  { -	/* Use event code as counter index */ -	u32 idx = GET_DDRC_EVENTID(hwc); - -	if (!hisi_uncore_pmu_counter_valid(ddrc_pmu, idx)) { -		dev_err(ddrc_pmu->dev, "Unsupported event index:%d!\n", idx); -		return 0; -	} +	return DDRC_V2_EVENT_CNT + cntr_idx * 8; +} -	return readl(ddrc_pmu->base + hisi_ddrc_pmu_get_counter_offset(idx)); +static u64 hisi_ddrc_pmu_v1_read_counter(struct hisi_pmu *ddrc_pmu, +				      struct hw_perf_event *hwc) +{ +	return readl(ddrc_pmu->base + +		     hisi_ddrc_pmu_v1_get_counter_offset(hwc->idx));  } -static void hisi_ddrc_pmu_write_counter(struct hisi_pmu *ddrc_pmu, +static void hisi_ddrc_pmu_v1_write_counter(struct hisi_pmu *ddrc_pmu,  					struct hw_perf_event *hwc, u64 val)  { -	u32 idx = GET_DDRC_EVENTID(hwc); +	writel((u32)val, +	       ddrc_pmu->base + hisi_ddrc_pmu_v1_get_counter_offset(hwc->idx)); +} -	if (!hisi_uncore_pmu_counter_valid(ddrc_pmu, idx)) { -		dev_err(ddrc_pmu->dev, "Unsupported event index:%d!\n", idx); -		return; -	} +static u64 hisi_ddrc_pmu_v2_read_counter(struct hisi_pmu *ddrc_pmu, +					 struct hw_perf_event *hwc) +{ +	return readq(ddrc_pmu->base + +		     hisi_ddrc_pmu_v2_get_counter_offset(hwc->idx)); +} -	writel((u32)val, -	       ddrc_pmu->base + hisi_ddrc_pmu_get_counter_offset(idx)); +static void hisi_ddrc_pmu_v2_write_counter(struct hisi_pmu *ddrc_pmu, +					   struct hw_perf_event *hwc, u64 val) +{ +	writeq(val, +	       ddrc_pmu->base + hisi_ddrc_pmu_v2_get_counter_offset(hwc->idx));  }  /* - * For DDRC PMU, event has been mapped to fixed-purpose counter by hardware, - * so there is no need to write event type. + * For DDRC PMU v1, event has been mapped to fixed-purpose counter by hardware, + * so there is no need to write event type, while it is programmable counter in + * PMU v2.   */  static void hisi_ddrc_pmu_write_evtype(struct hisi_pmu *hha_pmu, int idx,  				       u32 type)  { +	u32 offset; + +	if (hha_pmu->identifier >= HISI_PMU_V2) { +		offset = DDRC_V2_EVENT_TYPE + 4 * idx; +		writel(type, hha_pmu->base + offset); +	}  } -static void hisi_ddrc_pmu_start_counters(struct hisi_pmu *ddrc_pmu) +static void hisi_ddrc_pmu_v1_start_counters(struct hisi_pmu *ddrc_pmu)  {  	u32 val;  	/* Set perf_enable in DDRC_PERF_CTRL to start event counting */  	val = readl(ddrc_pmu->base + DDRC_PERF_CTRL); -	val |= DDRC_PERF_CTRL_EN; +	val |= DDRC_V1_PERF_CTRL_EN;  	writel(val, ddrc_pmu->base + DDRC_PERF_CTRL);  } -static void hisi_ddrc_pmu_stop_counters(struct hisi_pmu *ddrc_pmu) +static void hisi_ddrc_pmu_v1_stop_counters(struct hisi_pmu *ddrc_pmu)  {  	u32 val;  	/* Clear perf_enable in DDRC_PERF_CTRL to stop event counting */  	val = readl(ddrc_pmu->base + DDRC_PERF_CTRL); -	val &= ~DDRC_PERF_CTRL_EN; +	val &= ~DDRC_V1_PERF_CTRL_EN;  	writel(val, ddrc_pmu->base + DDRC_PERF_CTRL);  } -static void hisi_ddrc_pmu_enable_counter(struct hisi_pmu *ddrc_pmu, -					 struct hw_perf_event *hwc) +static void hisi_ddrc_pmu_v1_enable_counter(struct hisi_pmu *ddrc_pmu, +					    struct hw_perf_event *hwc)  {  	u32 val; @@ -130,8 +153,8 @@ static void hisi_ddrc_pmu_enable_counter(struct hisi_pmu *ddrc_pmu,  	writel(val, ddrc_pmu->base + DDRC_EVENT_CTRL);  } -static void hisi_ddrc_pmu_disable_counter(struct hisi_pmu *ddrc_pmu, -					  struct hw_perf_event *hwc) +static void hisi_ddrc_pmu_v1_disable_counter(struct hisi_pmu *ddrc_pmu, +					     struct hw_perf_event *hwc)  {  	u32 val; @@ -141,7 +164,7 @@ static void hisi_ddrc_pmu_disable_counter(struct hisi_pmu *ddrc_pmu,  	writel(val, ddrc_pmu->base + DDRC_EVENT_CTRL);  } -static int hisi_ddrc_pmu_get_event_idx(struct perf_event *event) +static int hisi_ddrc_pmu_v1_get_event_idx(struct perf_event *event)  {  	struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);  	unsigned long *used_mask = ddrc_pmu->pmu_events.used_mask; @@ -157,87 +180,117 @@ static int hisi_ddrc_pmu_get_event_idx(struct perf_event *event)  	return idx;  } -static void hisi_ddrc_pmu_enable_counter_int(struct hisi_pmu *ddrc_pmu, +static int hisi_ddrc_pmu_v2_get_event_idx(struct perf_event *event) +{ +	return hisi_uncore_pmu_get_event_idx(event); +} + +static void hisi_ddrc_pmu_v2_start_counters(struct hisi_pmu *ddrc_pmu) +{ +	u32 val; + +	val = readl(ddrc_pmu->base + DDRC_V2_PERF_CTRL); +	val |= DDRC_V2_PERF_CTRL_EN; +	writel(val, ddrc_pmu->base + DDRC_V2_PERF_CTRL); +} + +static void hisi_ddrc_pmu_v2_stop_counters(struct hisi_pmu *ddrc_pmu) +{ +	u32 val; + +	val = readl(ddrc_pmu->base + DDRC_V2_PERF_CTRL); +	val &= ~DDRC_V2_PERF_CTRL_EN; +	writel(val, ddrc_pmu->base + DDRC_V2_PERF_CTRL); +} + +static void hisi_ddrc_pmu_v2_enable_counter(struct hisi_pmu *ddrc_pmu, +					    struct hw_perf_event *hwc) +{ +	u32 val; + +	val = readl(ddrc_pmu->base + DDRC_V2_EVENT_CTRL); +	val |= 1 << hwc->idx; +	writel(val, ddrc_pmu->base + DDRC_V2_EVENT_CTRL); +} + +static void hisi_ddrc_pmu_v2_disable_counter(struct hisi_pmu *ddrc_pmu,  					     struct hw_perf_event *hwc)  {  	u32 val; +	val = readl(ddrc_pmu->base + DDRC_V2_EVENT_CTRL); +	val &= ~(1 << hwc->idx); +	writel(val, ddrc_pmu->base + DDRC_V2_EVENT_CTRL); +} + +static void hisi_ddrc_pmu_v1_enable_counter_int(struct hisi_pmu *ddrc_pmu, +						struct hw_perf_event *hwc) +{ +	u32 val; +  	/* Write 0 to enable interrupt */  	val = readl(ddrc_pmu->base + DDRC_INT_MASK); -	val &= ~(1 << GET_DDRC_EVENTID(hwc)); +	val &= ~(1 << hwc->idx);  	writel(val, ddrc_pmu->base + DDRC_INT_MASK);  } -static void hisi_ddrc_pmu_disable_counter_int(struct hisi_pmu *ddrc_pmu, -					      struct hw_perf_event *hwc) +static void hisi_ddrc_pmu_v1_disable_counter_int(struct hisi_pmu *ddrc_pmu, +						 struct hw_perf_event *hwc)  {  	u32 val;  	/* Write 1 to mask interrupt */  	val = readl(ddrc_pmu->base + DDRC_INT_MASK); -	val |= (1 << GET_DDRC_EVENTID(hwc)); +	val |= 1 << hwc->idx;  	writel(val, ddrc_pmu->base + DDRC_INT_MASK);  } -static irqreturn_t hisi_ddrc_pmu_isr(int irq, void *dev_id) +static void hisi_ddrc_pmu_v2_enable_counter_int(struct hisi_pmu *ddrc_pmu, +						struct hw_perf_event *hwc)  { -	struct hisi_pmu *ddrc_pmu = dev_id; -	struct perf_event *event; -	unsigned long overflown; -	int idx; - -	/* Read the DDRC_INT_STATUS register */ -	overflown = readl(ddrc_pmu->base + DDRC_INT_STATUS); -	if (!overflown) -		return IRQ_NONE; +	u32 val; -	/* -	 * Find the counter index which overflowed if the bit was set -	 * and handle it -	 */ -	for_each_set_bit(idx, &overflown, DDRC_NR_COUNTERS) { -		/* Write 1 to clear the IRQ status flag */ -		writel((1 << idx), ddrc_pmu->base + DDRC_INT_CLEAR); +	val = readl(ddrc_pmu->base + DDRC_V2_INT_MASK); +	val &= ~(1 << hwc->idx); +	writel(val, ddrc_pmu->base + DDRC_V2_INT_MASK); +} -		/* Get the corresponding event struct */ -		event = ddrc_pmu->pmu_events.hw_events[idx]; -		if (!event) -			continue; +static void hisi_ddrc_pmu_v2_disable_counter_int(struct hisi_pmu *ddrc_pmu, +						struct hw_perf_event *hwc) +{ +	u32 val; -		hisi_uncore_pmu_event_update(event); -		hisi_uncore_pmu_set_event_period(event); -	} +	val = readl(ddrc_pmu->base + DDRC_V2_INT_MASK); +	val |= 1 << hwc->idx; +	writel(val, ddrc_pmu->base + DDRC_V2_INT_MASK); +} -	return IRQ_HANDLED; +static u32 hisi_ddrc_pmu_v1_get_int_status(struct hisi_pmu *ddrc_pmu) +{ +	return readl(ddrc_pmu->base + DDRC_INT_STATUS);  } -static int hisi_ddrc_pmu_init_irq(struct hisi_pmu *ddrc_pmu, -				  struct platform_device *pdev) +static void hisi_ddrc_pmu_v1_clear_int_status(struct hisi_pmu *ddrc_pmu, +					      int idx)  { -	int irq, ret; - -	/* Read and init IRQ */ -	irq = platform_get_irq(pdev, 0); -	if (irq < 0) -		return irq; - -	ret = devm_request_irq(&pdev->dev, irq, hisi_ddrc_pmu_isr, -			       IRQF_NOBALANCING | IRQF_NO_THREAD, -			       dev_name(&pdev->dev), ddrc_pmu); -	if (ret < 0) { -		dev_err(&pdev->dev, -			"Fail to request IRQ:%d ret:%d\n", irq, ret); -		return ret; -	} +	writel(1 << idx, ddrc_pmu->base + DDRC_INT_CLEAR); +} -	ddrc_pmu->irq = irq; +static u32 hisi_ddrc_pmu_v2_get_int_status(struct hisi_pmu *ddrc_pmu) +{ +	return readl(ddrc_pmu->base + DDRC_V2_INT_STATUS); +} -	return 0; +static void hisi_ddrc_pmu_v2_clear_int_status(struct hisi_pmu *ddrc_pmu, +					      int idx) +{ +	writel(1 << idx, ddrc_pmu->base + DDRC_V2_INT_CLEAR);  }  static const struct acpi_device_id hisi_ddrc_pmu_acpi_match[] = {  	{ "HISI0233", }, -	{}, +	{ "HISI0234", }, +	{}  };  MODULE_DEVICE_TABLE(acpi, hisi_ddrc_pmu_acpi_match); @@ -269,21 +322,38 @@ static int hisi_ddrc_pmu_init_data(struct platform_device *pdev,  	}  	ddrc_pmu->identifier = readl(ddrc_pmu->base + DDRC_VERSION); +	if (ddrc_pmu->identifier >= HISI_PMU_V2) { +		if (device_property_read_u32(&pdev->dev, "hisilicon,sub-id", +					     &ddrc_pmu->sub_id)) { +			dev_err(&pdev->dev, "Can not read sub-id!\n"); +			return -EINVAL; +		} +	}  	return 0;  } -static struct attribute *hisi_ddrc_pmu_format_attr[] = { +static struct attribute *hisi_ddrc_pmu_v1_format_attr[] = {  	HISI_PMU_FORMAT_ATTR(event, "config:0-4"),  	NULL,  }; -static const struct attribute_group hisi_ddrc_pmu_format_group = { +static const struct attribute_group hisi_ddrc_pmu_v1_format_group = { +	.name = "format", +	.attrs = hisi_ddrc_pmu_v1_format_attr, +}; + +static struct attribute *hisi_ddrc_pmu_v2_format_attr[] = { +	HISI_PMU_FORMAT_ATTR(event, "config:0-7"), +	NULL +}; + +static const struct attribute_group hisi_ddrc_pmu_v2_format_group = {  	.name = "format", -	.attrs = hisi_ddrc_pmu_format_attr, +	.attrs = hisi_ddrc_pmu_v2_format_attr,  }; -static struct attribute *hisi_ddrc_pmu_events_attr[] = { +static struct attribute *hisi_ddrc_pmu_v1_events_attr[] = {  	HISI_PMU_EVENT_ATTR(flux_wr,		0x00),  	HISI_PMU_EVENT_ATTR(flux_rd,		0x01),  	HISI_PMU_EVENT_ATTR(flux_wcmd,		0x02), @@ -295,9 +365,21 @@ static struct attribute *hisi_ddrc_pmu_events_attr[] = {  	NULL,  }; -static const struct attribute_group hisi_ddrc_pmu_events_group = { +static const struct attribute_group hisi_ddrc_pmu_v1_events_group = {  	.name = "events", -	.attrs = hisi_ddrc_pmu_events_attr, +	.attrs = hisi_ddrc_pmu_v1_events_attr, +}; + +static struct attribute *hisi_ddrc_pmu_v2_events_attr[] = { +	HISI_PMU_EVENT_ATTR(cycles,		0x00), +	HISI_PMU_EVENT_ATTR(flux_wr,		0x83), +	HISI_PMU_EVENT_ATTR(flux_rd,		0x84), +	NULL +}; + +static const struct attribute_group hisi_ddrc_pmu_v2_events_group = { +	.name = "events", +	.attrs = hisi_ddrc_pmu_v2_events_attr,  };  static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); @@ -323,25 +405,50 @@ static const struct attribute_group hisi_ddrc_pmu_identifier_group = {  	.attrs = hisi_ddrc_pmu_identifier_attrs,  }; -static const struct attribute_group *hisi_ddrc_pmu_attr_groups[] = { -	&hisi_ddrc_pmu_format_group, -	&hisi_ddrc_pmu_events_group, +static const struct attribute_group *hisi_ddrc_pmu_v1_attr_groups[] = { +	&hisi_ddrc_pmu_v1_format_group, +	&hisi_ddrc_pmu_v1_events_group,  	&hisi_ddrc_pmu_cpumask_attr_group,  	&hisi_ddrc_pmu_identifier_group,  	NULL,  }; -static const struct hisi_uncore_ops hisi_uncore_ddrc_ops = { +static const struct attribute_group *hisi_ddrc_pmu_v2_attr_groups[] = { +	&hisi_ddrc_pmu_v2_format_group, +	&hisi_ddrc_pmu_v2_events_group, +	&hisi_ddrc_pmu_cpumask_attr_group, +	&hisi_ddrc_pmu_identifier_group, +	NULL +}; + +static const struct hisi_uncore_ops hisi_uncore_ddrc_v1_ops = { +	.write_evtype           = hisi_ddrc_pmu_write_evtype, +	.get_event_idx		= hisi_ddrc_pmu_v1_get_event_idx, +	.start_counters		= hisi_ddrc_pmu_v1_start_counters, +	.stop_counters		= hisi_ddrc_pmu_v1_stop_counters, +	.enable_counter		= hisi_ddrc_pmu_v1_enable_counter, +	.disable_counter	= hisi_ddrc_pmu_v1_disable_counter, +	.enable_counter_int	= hisi_ddrc_pmu_v1_enable_counter_int, +	.disable_counter_int	= hisi_ddrc_pmu_v1_disable_counter_int, +	.write_counter		= hisi_ddrc_pmu_v1_write_counter, +	.read_counter		= hisi_ddrc_pmu_v1_read_counter, +	.get_int_status		= hisi_ddrc_pmu_v1_get_int_status, +	.clear_int_status	= hisi_ddrc_pmu_v1_clear_int_status, +}; + +static const struct hisi_uncore_ops hisi_uncore_ddrc_v2_ops = {  	.write_evtype           = hisi_ddrc_pmu_write_evtype, -	.get_event_idx		= hisi_ddrc_pmu_get_event_idx, -	.start_counters		= hisi_ddrc_pmu_start_counters, -	.stop_counters		= hisi_ddrc_pmu_stop_counters, -	.enable_counter		= hisi_ddrc_pmu_enable_counter, -	.disable_counter	= hisi_ddrc_pmu_disable_counter, -	.enable_counter_int	= hisi_ddrc_pmu_enable_counter_int, -	.disable_counter_int	= hisi_ddrc_pmu_disable_counter_int, -	.write_counter		= hisi_ddrc_pmu_write_counter, -	.read_counter		= hisi_ddrc_pmu_read_counter, +	.get_event_idx		= hisi_ddrc_pmu_v2_get_event_idx, +	.start_counters		= hisi_ddrc_pmu_v2_start_counters, +	.stop_counters		= hisi_ddrc_pmu_v2_stop_counters, +	.enable_counter		= hisi_ddrc_pmu_v2_enable_counter, +	.disable_counter	= hisi_ddrc_pmu_v2_disable_counter, +	.enable_counter_int	= hisi_ddrc_pmu_v2_enable_counter_int, +	.disable_counter_int	= hisi_ddrc_pmu_v2_disable_counter_int, +	.write_counter		= hisi_ddrc_pmu_v2_write_counter, +	.read_counter		= hisi_ddrc_pmu_v2_read_counter, +	.get_int_status		= hisi_ddrc_pmu_v2_get_int_status, +	.clear_int_status	= hisi_ddrc_pmu_v2_clear_int_status,  };  static int hisi_ddrc_pmu_dev_probe(struct platform_device *pdev, @@ -353,16 +460,25 @@ static int hisi_ddrc_pmu_dev_probe(struct platform_device *pdev,  	if (ret)  		return ret; -	ret = hisi_ddrc_pmu_init_irq(ddrc_pmu, pdev); +	ret = hisi_uncore_pmu_init_irq(ddrc_pmu, pdev);  	if (ret)  		return ret; +	if (ddrc_pmu->identifier >= HISI_PMU_V2) { +		ddrc_pmu->counter_bits = 48; +		ddrc_pmu->check_event = DDRC_V2_NR_EVENTS; +		ddrc_pmu->pmu_events.attr_groups = hisi_ddrc_pmu_v2_attr_groups; +		ddrc_pmu->ops = &hisi_uncore_ddrc_v2_ops; +	} else { +		ddrc_pmu->counter_bits = 32; +		ddrc_pmu->check_event = DDRC_V1_NR_EVENTS; +		ddrc_pmu->pmu_events.attr_groups = hisi_ddrc_pmu_v1_attr_groups; +		ddrc_pmu->ops = &hisi_uncore_ddrc_v1_ops; +	} +  	ddrc_pmu->num_counters = DDRC_NR_COUNTERS; -	ddrc_pmu->counter_bits = 32; -	ddrc_pmu->ops = &hisi_uncore_ddrc_ops;  	ddrc_pmu->dev = &pdev->dev;  	ddrc_pmu->on_cpu = -1; -	ddrc_pmu->check_event = 7;  	return 0;  } @@ -390,8 +506,16 @@ static int hisi_ddrc_pmu_probe(struct platform_device *pdev)  		return ret;  	} -	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_ddrc%u", -			      ddrc_pmu->sccl_id, ddrc_pmu->index_id); +	if (ddrc_pmu->identifier >= HISI_PMU_V2) +		name = devm_kasprintf(&pdev->dev, GFP_KERNEL, +				      "hisi_sccl%u_ddrc%u_%u", +				      ddrc_pmu->sccl_id, ddrc_pmu->index_id, +				      ddrc_pmu->sub_id); +	else +		name = devm_kasprintf(&pdev->dev, GFP_KERNEL, +				      "hisi_sccl%u_ddrc%u", ddrc_pmu->sccl_id, +				      ddrc_pmu->index_id); +  	ddrc_pmu->pmu = (struct pmu) {  		.name		= name,  		.module		= THIS_MODULE, @@ -404,7 +528,7 @@ static int hisi_ddrc_pmu_probe(struct platform_device *pdev)  		.start		= hisi_uncore_pmu_start,  		.stop		= hisi_uncore_pmu_stop,  		.read		= hisi_uncore_pmu_read, -		.attr_groups	= hisi_ddrc_pmu_attr_groups, +		.attr_groups	= ddrc_pmu->pmu_events.attr_groups,  		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,  	}; diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c index 3402f1a395a8..0316fabe32f1 100644 --- a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -14,7 +14,6 @@  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/list.h> -#include <linux/platform_device.h>  #include <linux/smp.h>  #include "hisi_uncore_pmu.h" @@ -26,18 +25,136 @@  #define HHA_VERSION		0x1cf0  #define HHA_PERF_CTRL		0x1E00  #define HHA_EVENT_CTRL		0x1E04 +#define HHA_SRCID_CTRL		0x1E08 +#define HHA_DATSRC_CTRL		0x1BF0  #define HHA_EVENT_TYPE0		0x1E80  /* - * Each counter is 48-bits and [48:63] are reserved - * which are Read-As-Zero and Writes-Ignored. + * If the HW version only supports a 48-bit counter, then + * bits [63:48] are reserved, which are Read-As-Zero and + * Writes-Ignored.   */  #define HHA_CNT0_LOWER		0x1F00 -/* HHA has 16-counters */ -#define HHA_NR_COUNTERS		0x10 +/* HHA PMU v1 has 16 counters and v2 only has 8 counters */ +#define HHA_V1_NR_COUNTERS	0x10 +#define HHA_V2_NR_COUNTERS	0x8  #define HHA_PERF_CTRL_EN	0x1 +#define HHA_TRACETAG_EN		BIT(31) +#define HHA_SRCID_EN		BIT(2) +#define HHA_SRCID_CMD_SHIFT	6 +#define HHA_SRCID_MSK_SHIFT	20 +#define HHA_SRCID_CMD		GENMASK(16, 6) +#define HHA_SRCID_MSK		GENMASK(30, 20) +#define HHA_DATSRC_SKT_EN	BIT(23)  #define HHA_EVTYPE_NONE		0xff +#define HHA_V1_NR_EVENT		0x65 +#define HHA_V2_NR_EVENT		0xCE + +HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 10, 0); +HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 21, 11); +HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 22, 22); +HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_skt, config1, 23, 23); + +static void hisi_hha_pmu_enable_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); +	u32 tt_en = hisi_get_tracetag_en(event); + +	if (tt_en) { +		u32 val; + +		val = readl(hha_pmu->base + HHA_SRCID_CTRL); +		val |= HHA_TRACETAG_EN; +		writel(val, hha_pmu->base + HHA_SRCID_CTRL); +	} +} + +static void hisi_hha_pmu_clear_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); +	u32 val; + +	val = readl(hha_pmu->base + HHA_SRCID_CTRL); +	val &= ~HHA_TRACETAG_EN; +	writel(val, hha_pmu->base + HHA_SRCID_CTRL); +} + +static void hisi_hha_pmu_config_ds(struct perf_event *event) +{ +	struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); +	u32 ds_skt = hisi_get_datasrc_skt(event); + +	if (ds_skt) { +		u32 val; + +		val = readl(hha_pmu->base + HHA_DATSRC_CTRL); +		val |= HHA_DATSRC_SKT_EN; +		writel(ds_skt, hha_pmu->base + HHA_DATSRC_CTRL); +	} +} + +static void hisi_hha_pmu_clear_ds(struct perf_event *event) +{ +	struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); +	u32 ds_skt = hisi_get_datasrc_skt(event); + +	if (ds_skt) { +		u32 val; + +		val = readl(hha_pmu->base + HHA_DATSRC_CTRL); +		val &= ~HHA_DATSRC_SKT_EN; +		writel(ds_skt, hha_pmu->base + HHA_DATSRC_CTRL); +	} +} + +static void hisi_hha_pmu_config_srcid(struct perf_event *event) +{ +	struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); +	u32 cmd = hisi_get_srcid_cmd(event); + +	if (cmd) { +		u32 val, msk; + +		msk = hisi_get_srcid_msk(event); +		val = readl(hha_pmu->base + HHA_SRCID_CTRL); +		val |= HHA_SRCID_EN | (cmd << HHA_SRCID_CMD_SHIFT) | +			(msk << HHA_SRCID_MSK_SHIFT); +		writel(val, hha_pmu->base + HHA_SRCID_CTRL); +	} +} + +static void hisi_hha_pmu_disable_srcid(struct perf_event *event) +{ +	struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); +	u32 cmd = hisi_get_srcid_cmd(event); + +	if (cmd) { +		u32 val; + +		val = readl(hha_pmu->base + HHA_SRCID_CTRL); +		val &= ~(HHA_SRCID_EN | HHA_SRCID_MSK | HHA_SRCID_CMD); +		writel(val, hha_pmu->base + HHA_SRCID_CTRL); +	} +} + +static void hisi_hha_pmu_enable_filter(struct perf_event *event) +{ +	if (event->attr.config1 != 0x0) { +		hisi_hha_pmu_enable_tracetag(event); +		hisi_hha_pmu_config_ds(event); +		hisi_hha_pmu_config_srcid(event); +	} +} + +static void hisi_hha_pmu_disable_filter(struct perf_event *event) +{ +	if (event->attr.config1 != 0x0) { +		hisi_hha_pmu_disable_srcid(event); +		hisi_hha_pmu_clear_ds(event); +		hisi_hha_pmu_clear_tracetag(event); +	} +}  /*   * Select the counter register offset using the counter index @@ -51,29 +168,15 @@ static u32 hisi_hha_pmu_get_counter_offset(int cntr_idx)  static u64 hisi_hha_pmu_read_counter(struct hisi_pmu *hha_pmu,  				     struct hw_perf_event *hwc)  { -	u32 idx = hwc->idx; - -	if (!hisi_uncore_pmu_counter_valid(hha_pmu, idx)) { -		dev_err(hha_pmu->dev, "Unsupported event index:%d!\n", idx); -		return 0; -	} -  	/* Read 64 bits and like L3C, top 16 bits are RAZ */ -	return readq(hha_pmu->base + hisi_hha_pmu_get_counter_offset(idx)); +	return readq(hha_pmu->base + hisi_hha_pmu_get_counter_offset(hwc->idx));  }  static void hisi_hha_pmu_write_counter(struct hisi_pmu *hha_pmu,  				       struct hw_perf_event *hwc, u64 val)  { -	u32 idx = hwc->idx; - -	if (!hisi_uncore_pmu_counter_valid(hha_pmu, idx)) { -		dev_err(hha_pmu->dev, "Unsupported event index:%d!\n", idx); -		return; -	} -  	/* Write 64 bits and like L3C, top 16 bits are WI */ -	writeq(val, hha_pmu->base + hisi_hha_pmu_get_counter_offset(idx)); +	writeq(val, hha_pmu->base + hisi_hha_pmu_get_counter_offset(hwc->idx));  }  static void hisi_hha_pmu_write_evtype(struct hisi_pmu *hha_pmu, int idx, @@ -169,65 +272,20 @@ static void hisi_hha_pmu_disable_counter_int(struct hisi_pmu *hha_pmu,  	writel(val, hha_pmu->base + HHA_INT_MASK);  } -static irqreturn_t hisi_hha_pmu_isr(int irq, void *dev_id) +static u32 hisi_hha_pmu_get_int_status(struct hisi_pmu *hha_pmu)  { -	struct hisi_pmu *hha_pmu = dev_id; -	struct perf_event *event; -	unsigned long overflown; -	int idx; - -	/* Read HHA_INT_STATUS register */ -	overflown = readl(hha_pmu->base + HHA_INT_STATUS); -	if (!overflown) -		return IRQ_NONE; - -	/* -	 * Find the counter index which overflowed if the bit was set -	 * and handle it -	 */ -	for_each_set_bit(idx, &overflown, HHA_NR_COUNTERS) { -		/* Write 1 to clear the IRQ status flag */ -		writel((1 << idx), hha_pmu->base + HHA_INT_CLEAR); - -		/* Get the corresponding event struct */ -		event = hha_pmu->pmu_events.hw_events[idx]; -		if (!event) -			continue; - -		hisi_uncore_pmu_event_update(event); -		hisi_uncore_pmu_set_event_period(event); -	} - -	return IRQ_HANDLED; +	return readl(hha_pmu->base + HHA_INT_STATUS);  } -static int hisi_hha_pmu_init_irq(struct hisi_pmu *hha_pmu, -				 struct platform_device *pdev) +static void hisi_hha_pmu_clear_int_status(struct hisi_pmu *hha_pmu, int idx)  { -	int irq, ret; - -	/* Read and init IRQ */ -	irq = platform_get_irq(pdev, 0); -	if (irq < 0) -		return irq; - -	ret = devm_request_irq(&pdev->dev, irq, hisi_hha_pmu_isr, -			      IRQF_NOBALANCING | IRQF_NO_THREAD, -			      dev_name(&pdev->dev), hha_pmu); -	if (ret < 0) { -		dev_err(&pdev->dev, -			"Fail to request IRQ:%d ret:%d\n", irq, ret); -		return ret; -	} - -	hha_pmu->irq = irq; - -	return 0; +	writel(1 << idx, hha_pmu->base + HHA_INT_CLEAR);  }  static const struct acpi_device_id hisi_hha_pmu_acpi_match[] = {  	{ "HISI0243", }, -	{}, +	{ "HISI0244", }, +	{}  };  MODULE_DEVICE_TABLE(acpi, hisi_hha_pmu_acpi_match); @@ -237,13 +295,6 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev,  	unsigned long long id;  	acpi_status status; -	status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), -				       "_UID", NULL, &id); -	if (ACPI_FAILURE(status)) -		return -EINVAL; - -	hha_pmu->index_id = id; -  	/*  	 * Use SCCL_ID and UID to identify the HHA PMU, while  	 * SCCL_ID is in MPIDR[aff2]. @@ -253,6 +304,22 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev,  		dev_err(&pdev->dev, "Can not read hha sccl-id!\n");  		return -EINVAL;  	} + +	/* +	 * Early versions of BIOS support _UID by mistake, so we support +	 * both "hisilicon, idx-id" as preference, if available. +	 */ +	if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", +				     &hha_pmu->index_id)) { +		status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), +					       "_UID", NULL, &id); +		if (ACPI_FAILURE(status)) { +			dev_err(&pdev->dev, "Cannot read idx-id!\n"); +			return -EINVAL; +		} + +		hha_pmu->index_id = id; +	}  	/* HHA PMUs only share the same SCCL */  	hha_pmu->ccl_id = -1; @@ -267,17 +334,31 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev,  	return 0;  } -static struct attribute *hisi_hha_pmu_format_attr[] = { +static struct attribute *hisi_hha_pmu_v1_format_attr[] = {  	HISI_PMU_FORMAT_ATTR(event, "config:0-7"),  	NULL,  }; -static const struct attribute_group hisi_hha_pmu_format_group = { +static const struct attribute_group hisi_hha_pmu_v1_format_group = { +	.name = "format", +	.attrs = hisi_hha_pmu_v1_format_attr, +}; + +static struct attribute *hisi_hha_pmu_v2_format_attr[] = { +	HISI_PMU_FORMAT_ATTR(event, "config:0-7"), +	HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:0-10"), +	HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:11-21"), +	HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:22"), +	HISI_PMU_FORMAT_ATTR(datasrc_skt, "config1:23"), +	NULL +}; + +static const struct attribute_group hisi_hha_pmu_v2_format_group = {  	.name = "format", -	.attrs = hisi_hha_pmu_format_attr, +	.attrs = hisi_hha_pmu_v2_format_attr,  }; -static struct attribute *hisi_hha_pmu_events_attr[] = { +static struct attribute *hisi_hha_pmu_v1_events_attr[] = {  	HISI_PMU_EVENT_ATTR(rx_ops_num,		0x00),  	HISI_PMU_EVENT_ATTR(rx_outer,		0x01),  	HISI_PMU_EVENT_ATTR(rx_sccl,		0x02), @@ -307,9 +388,23 @@ static struct attribute *hisi_hha_pmu_events_attr[] = {  	NULL,  }; -static const struct attribute_group hisi_hha_pmu_events_group = { +static const struct attribute_group hisi_hha_pmu_v1_events_group = {  	.name = "events", -	.attrs = hisi_hha_pmu_events_attr, +	.attrs = hisi_hha_pmu_v1_events_attr, +}; + +static struct attribute *hisi_hha_pmu_v2_events_attr[] = { +	HISI_PMU_EVENT_ATTR(rx_ops_num,		0x00), +	HISI_PMU_EVENT_ATTR(rx_outer,		0x01), +	HISI_PMU_EVENT_ATTR(rx_sccl,		0x02), +	HISI_PMU_EVENT_ATTR(hha_retry,		0x2e), +	HISI_PMU_EVENT_ATTR(cycles,		0x55), +	NULL +}; + +static const struct attribute_group hisi_hha_pmu_v2_events_group = { +	.name = "events", +	.attrs = hisi_hha_pmu_v2_events_attr,  };  static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); @@ -335,14 +430,22 @@ static const struct attribute_group hisi_hha_pmu_identifier_group = {  	.attrs = hisi_hha_pmu_identifier_attrs,  }; -static const struct attribute_group *hisi_hha_pmu_attr_groups[] = { -	&hisi_hha_pmu_format_group, -	&hisi_hha_pmu_events_group, +static const struct attribute_group *hisi_hha_pmu_v1_attr_groups[] = { +	&hisi_hha_pmu_v1_format_group, +	&hisi_hha_pmu_v1_events_group,  	&hisi_hha_pmu_cpumask_attr_group,  	&hisi_hha_pmu_identifier_group,  	NULL,  }; +static const struct attribute_group *hisi_hha_pmu_v2_attr_groups[] = { +	&hisi_hha_pmu_v2_format_group, +	&hisi_hha_pmu_v2_events_group, +	&hisi_hha_pmu_cpumask_attr_group, +	&hisi_hha_pmu_identifier_group, +	NULL +}; +  static const struct hisi_uncore_ops hisi_uncore_hha_ops = {  	.write_evtype		= hisi_hha_pmu_write_evtype,  	.get_event_idx		= hisi_uncore_pmu_get_event_idx, @@ -354,6 +457,10 @@ static const struct hisi_uncore_ops hisi_uncore_hha_ops = {  	.disable_counter_int	= hisi_hha_pmu_disable_counter_int,  	.write_counter		= hisi_hha_pmu_write_counter,  	.read_counter		= hisi_hha_pmu_read_counter, +	.get_int_status		= hisi_hha_pmu_get_int_status, +	.clear_int_status	= hisi_hha_pmu_clear_int_status, +	.enable_filter		= hisi_hha_pmu_enable_filter, +	.disable_filter		= hisi_hha_pmu_disable_filter,  };  static int hisi_hha_pmu_dev_probe(struct platform_device *pdev, @@ -365,16 +472,24 @@ static int hisi_hha_pmu_dev_probe(struct platform_device *pdev,  	if (ret)  		return ret; -	ret = hisi_hha_pmu_init_irq(hha_pmu, pdev); +	ret = hisi_uncore_pmu_init_irq(hha_pmu, pdev);  	if (ret)  		return ret; -	hha_pmu->num_counters = HHA_NR_COUNTERS; -	hha_pmu->counter_bits = 48; +	if (hha_pmu->identifier >= HISI_PMU_V2) { +		hha_pmu->counter_bits = 64; +		hha_pmu->check_event = HHA_V2_NR_EVENT; +		hha_pmu->pmu_events.attr_groups = hisi_hha_pmu_v2_attr_groups; +		hha_pmu->num_counters = HHA_V2_NR_COUNTERS; +	} else { +		hha_pmu->counter_bits = 48; +		hha_pmu->check_event = HHA_V1_NR_EVENT; +		hha_pmu->pmu_events.attr_groups = hisi_hha_pmu_v1_attr_groups; +		hha_pmu->num_counters = HHA_V1_NR_COUNTERS; +	}  	hha_pmu->ops = &hisi_uncore_hha_ops;  	hha_pmu->dev = &pdev->dev;  	hha_pmu->on_cpu = -1; -	hha_pmu->check_event = 0x65;  	return 0;  } @@ -416,7 +531,7 @@ static int hisi_hha_pmu_probe(struct platform_device *pdev)  		.start		= hisi_uncore_pmu_start,  		.stop		= hisi_uncore_pmu_stop,  		.read		= hisi_uncore_pmu_read, -		.attr_groups	= hisi_hha_pmu_attr_groups, +		.attr_groups	= hha_pmu->pmu_events.attr_groups,  		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,  	}; diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 7d792435c2aa..bf9f7772cac9 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -14,7 +14,6 @@  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/list.h> -#include <linux/platform_device.h>  #include <linux/smp.h>  #include "hisi_uncore_pmu.h" @@ -24,12 +23,17 @@  #define L3C_INT_MASK		0x0800  #define L3C_INT_STATUS		0x0808  #define L3C_INT_CLEAR		0x080c +#define L3C_CORE_CTRL           0x1b04 +#define L3C_TRACETAG_CTRL       0x1b20 +#define L3C_DATSRC_TYPE         0x1b48 +#define L3C_DATSRC_CTRL         0x1bf0  #define L3C_EVENT_CTRL	        0x1c00  #define L3C_VERSION		0x1cf0  #define L3C_EVENT_TYPE0		0x1d00  /* - * Each counter is 48-bits and [48:63] are reserved - * which are Read-As-Zero and Writes-Ignored. + * If the HW version only supports a 48-bit counter, then + * bits [63:48] are reserved, which are Read-As-Zero and + * Writes-Ignored.   */  #define L3C_CNTR0_LOWER		0x1e00 @@ -37,7 +41,186 @@  #define L3C_NR_COUNTERS		0x8  #define L3C_PERF_CTRL_EN	0x10000 +#define L3C_TRACETAG_EN		BIT(31) +#define L3C_TRACETAG_REQ_SHIFT	7 +#define L3C_TRACETAG_MARK_EN	BIT(0) +#define L3C_TRACETAG_REQ_EN	(L3C_TRACETAG_MARK_EN | BIT(2)) +#define L3C_TRACETAG_CORE_EN	(L3C_TRACETAG_MARK_EN | BIT(3)) +#define L3C_CORE_EN		BIT(20) +#define L3C_COER_NONE		0x0 +#define L3C_DATSRC_MASK		0xFF +#define L3C_DATSRC_SKT_EN	BIT(23) +#define L3C_DATSRC_NONE		0x0  #define L3C_EVTYPE_NONE		0xff +#define L3C_V1_NR_EVENTS	0x59 +#define L3C_V2_NR_EVENTS	0xFF + +HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_core, config1, 7, 0); +HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_req, config1, 10, 8); +HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_cfg, config1, 15, 11); +HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_skt, config1, 16, 16); + +static void hisi_l3c_pmu_config_req_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); +	u32 tt_req = hisi_get_tt_req(event); + +	if (tt_req) { +		u32 val; + +		/* Set request-type for tracetag */ +		val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL); +		val |= tt_req << L3C_TRACETAG_REQ_SHIFT; +		val |= L3C_TRACETAG_REQ_EN; +		writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL); + +		/* Enable request-tracetag statistics */ +		val = readl(l3c_pmu->base + L3C_PERF_CTRL); +		val |= L3C_TRACETAG_EN; +		writel(val, l3c_pmu->base + L3C_PERF_CTRL); +	} +} + +static void hisi_l3c_pmu_clear_req_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); +	u32 tt_req = hisi_get_tt_req(event); + +	if (tt_req) { +		u32 val; + +		/* Clear request-type */ +		val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL); +		val &= ~(tt_req << L3C_TRACETAG_REQ_SHIFT); +		val &= ~L3C_TRACETAG_REQ_EN; +		writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL); + +		/* Disable request-tracetag statistics */ +		val = readl(l3c_pmu->base + L3C_PERF_CTRL); +		val &= ~L3C_TRACETAG_EN; +		writel(val, l3c_pmu->base + L3C_PERF_CTRL); +	} +} + +static void hisi_l3c_pmu_write_ds(struct perf_event *event, u32 ds_cfg) +{ +	struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); +	struct hw_perf_event *hwc = &event->hw; +	u32 reg, reg_idx, shift, val; +	int idx = hwc->idx; + +	/* +	 * Select the appropriate datasource register(L3C_DATSRC_TYPE0/1). +	 * There are 2 datasource ctrl register for the 8 hardware counters. +	 * Datasrc is 8-bits and for the former 4 hardware counters, +	 * L3C_DATSRC_TYPE0 is chosen. For the latter 4 hardware counters, +	 * L3C_DATSRC_TYPE1 is chosen. +	 */ +	reg = L3C_DATSRC_TYPE + (idx / 4) * 4; +	reg_idx = idx % 4; +	shift = 8 * reg_idx; + +	val = readl(l3c_pmu->base + reg); +	val &= ~(L3C_DATSRC_MASK << shift); +	val |= ds_cfg << shift; +	writel(val, l3c_pmu->base + reg); +} + +static void hisi_l3c_pmu_config_ds(struct perf_event *event) +{ +	struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); +	u32 ds_cfg = hisi_get_datasrc_cfg(event); +	u32 ds_skt = hisi_get_datasrc_skt(event); + +	if (ds_cfg) +		hisi_l3c_pmu_write_ds(event, ds_cfg); + +	if (ds_skt) { +		u32 val; + +		val = readl(l3c_pmu->base + L3C_DATSRC_CTRL); +		val |= L3C_DATSRC_SKT_EN; +		writel(val, l3c_pmu->base + L3C_DATSRC_CTRL); +	} +} + +static void hisi_l3c_pmu_clear_ds(struct perf_event *event) +{ +	struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); +	u32 ds_cfg = hisi_get_datasrc_cfg(event); +	u32 ds_skt = hisi_get_datasrc_skt(event); + +	if (ds_cfg) +		hisi_l3c_pmu_write_ds(event, L3C_DATSRC_NONE); + +	if (ds_skt) { +		u32 val; + +		val = readl(l3c_pmu->base + L3C_DATSRC_CTRL); +		val &= ~L3C_DATSRC_SKT_EN; +		writel(val, l3c_pmu->base + L3C_DATSRC_CTRL); +	} +} + +static void hisi_l3c_pmu_config_core_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); +	u32 core = hisi_get_tt_core(event); + +	if (core) { +		u32 val; + +		/* Config and enable core information */ +		writel(core, l3c_pmu->base + L3C_CORE_CTRL); +		val = readl(l3c_pmu->base + L3C_PERF_CTRL); +		val |= L3C_CORE_EN; +		writel(val, l3c_pmu->base + L3C_PERF_CTRL); + +		/* Enable core-tracetag statistics */ +		val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL); +		val |= L3C_TRACETAG_CORE_EN; +		writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL); +	} +} + +static void hisi_l3c_pmu_clear_core_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); +	u32 core = hisi_get_tt_core(event); + +	if (core) { +		u32 val; + +		/* Clear core information */ +		writel(L3C_COER_NONE, l3c_pmu->base + L3C_CORE_CTRL); +		val = readl(l3c_pmu->base + L3C_PERF_CTRL); +		val &= ~L3C_CORE_EN; +		writel(val, l3c_pmu->base + L3C_PERF_CTRL); + +		/* Disable core-tracetag statistics */ +		val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL); +		val &= ~L3C_TRACETAG_CORE_EN; +		writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL); +	} +} + +static void hisi_l3c_pmu_enable_filter(struct perf_event *event) +{ +	if (event->attr.config1 != 0x0) { +		hisi_l3c_pmu_config_req_tracetag(event); +		hisi_l3c_pmu_config_core_tracetag(event); +		hisi_l3c_pmu_config_ds(event); +	} +} + +static void hisi_l3c_pmu_disable_filter(struct perf_event *event) +{ +	if (event->attr.config1 != 0x0) { +		hisi_l3c_pmu_clear_ds(event); +		hisi_l3c_pmu_clear_core_tracetag(event); +		hisi_l3c_pmu_clear_req_tracetag(event); +	} +}  /*   * Select the counter register offset using the counter index @@ -50,29 +233,13 @@ static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx)  static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu,  				     struct hw_perf_event *hwc)  { -	u32 idx = hwc->idx; - -	if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) { -		dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx); -		return 0; -	} - -	/* Read 64-bits and the upper 16 bits are RAZ */ -	return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx)); +	return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(hwc->idx));  }  static void hisi_l3c_pmu_write_counter(struct hisi_pmu *l3c_pmu,  				       struct hw_perf_event *hwc, u64 val)  { -	u32 idx = hwc->idx; - -	if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) { -		dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx); -		return; -	} - -	/* Write 64-bits and the upper 16 bits are WI */ -	writeq(val, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx)); +	writeq(val, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(hwc->idx));  }  static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx, @@ -168,81 +335,26 @@ static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu,  	writel(val, l3c_pmu->base + L3C_INT_MASK);  } -static irqreturn_t hisi_l3c_pmu_isr(int irq, void *dev_id) +static u32 hisi_l3c_pmu_get_int_status(struct hisi_pmu *l3c_pmu)  { -	struct hisi_pmu *l3c_pmu = dev_id; -	struct perf_event *event; -	unsigned long overflown; -	int idx; - -	/* Read L3C_INT_STATUS register */ -	overflown = readl(l3c_pmu->base + L3C_INT_STATUS); -	if (!overflown) -		return IRQ_NONE; - -	/* -	 * Find the counter index which overflowed if the bit was set -	 * and handle it. -	 */ -	for_each_set_bit(idx, &overflown, L3C_NR_COUNTERS) { -		/* Write 1 to clear the IRQ status flag */ -		writel((1 << idx), l3c_pmu->base + L3C_INT_CLEAR); - -		/* Get the corresponding event struct */ -		event = l3c_pmu->pmu_events.hw_events[idx]; -		if (!event) -			continue; - -		hisi_uncore_pmu_event_update(event); -		hisi_uncore_pmu_set_event_period(event); -	} - -	return IRQ_HANDLED; +	return readl(l3c_pmu->base + L3C_INT_STATUS);  } -static int hisi_l3c_pmu_init_irq(struct hisi_pmu *l3c_pmu, -				 struct platform_device *pdev) +static void hisi_l3c_pmu_clear_int_status(struct hisi_pmu *l3c_pmu, int idx)  { -	int irq, ret; - -	/* Read and init IRQ */ -	irq = platform_get_irq(pdev, 0); -	if (irq < 0) -		return irq; - -	ret = devm_request_irq(&pdev->dev, irq, hisi_l3c_pmu_isr, -			       IRQF_NOBALANCING | IRQF_NO_THREAD, -			       dev_name(&pdev->dev), l3c_pmu); -	if (ret < 0) { -		dev_err(&pdev->dev, -			"Fail to request IRQ:%d ret:%d\n", irq, ret); -		return ret; -	} - -	l3c_pmu->irq = irq; - -	return 0; +	writel(1 << idx, l3c_pmu->base + L3C_INT_CLEAR);  }  static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = {  	{ "HISI0213", }, -	{}, +	{ "HISI0214", }, +	{}  };  MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match);  static int hisi_l3c_pmu_init_data(struct platform_device *pdev,  				  struct hisi_pmu *l3c_pmu)  { -	unsigned long long id; -	acpi_status status; - -	status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), -				       "_UID", NULL, &id); -	if (ACPI_FAILURE(status)) -		return -EINVAL; - -	l3c_pmu->index_id = id; -  	/*  	 * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while  	 * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1]. @@ -270,17 +382,31 @@ static int hisi_l3c_pmu_init_data(struct platform_device *pdev,  	return 0;  } -static struct attribute *hisi_l3c_pmu_format_attr[] = { +static struct attribute *hisi_l3c_pmu_v1_format_attr[] = {  	HISI_PMU_FORMAT_ATTR(event, "config:0-7"),  	NULL,  }; -static const struct attribute_group hisi_l3c_pmu_format_group = { +static const struct attribute_group hisi_l3c_pmu_v1_format_group = { +	.name = "format", +	.attrs = hisi_l3c_pmu_v1_format_attr, +}; + +static struct attribute *hisi_l3c_pmu_v2_format_attr[] = { +	HISI_PMU_FORMAT_ATTR(event, "config:0-7"), +	HISI_PMU_FORMAT_ATTR(tt_core, "config1:0-7"), +	HISI_PMU_FORMAT_ATTR(tt_req, "config1:8-10"), +	HISI_PMU_FORMAT_ATTR(datasrc_cfg, "config1:11-15"), +	HISI_PMU_FORMAT_ATTR(datasrc_skt, "config1:16"), +	NULL +}; + +static const struct attribute_group hisi_l3c_pmu_v2_format_group = {  	.name = "format", -	.attrs = hisi_l3c_pmu_format_attr, +	.attrs = hisi_l3c_pmu_v2_format_attr,  }; -static struct attribute *hisi_l3c_pmu_events_attr[] = { +static struct attribute *hisi_l3c_pmu_v1_events_attr[] = {  	HISI_PMU_EVENT_ATTR(rd_cpipe,		0x00),  	HISI_PMU_EVENT_ATTR(wr_cpipe,		0x01),  	HISI_PMU_EVENT_ATTR(rd_hit_cpipe,	0x02), @@ -297,9 +423,22 @@ static struct attribute *hisi_l3c_pmu_events_attr[] = {  	NULL,  }; -static const struct attribute_group hisi_l3c_pmu_events_group = { +static const struct attribute_group hisi_l3c_pmu_v1_events_group = { +	.name = "events", +	.attrs = hisi_l3c_pmu_v1_events_attr, +}; + +static struct attribute *hisi_l3c_pmu_v2_events_attr[] = { +	HISI_PMU_EVENT_ATTR(l3c_hit,		0x48), +	HISI_PMU_EVENT_ATTR(cycles,		0x7f), +	HISI_PMU_EVENT_ATTR(l3c_ref,		0xb8), +	HISI_PMU_EVENT_ATTR(dat_access,		0xb9), +	NULL +}; + +static const struct attribute_group hisi_l3c_pmu_v2_events_group = {  	.name = "events", -	.attrs = hisi_l3c_pmu_events_attr, +	.attrs = hisi_l3c_pmu_v2_events_attr,  };  static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); @@ -325,14 +464,22 @@ static const struct attribute_group hisi_l3c_pmu_identifier_group = {  	.attrs = hisi_l3c_pmu_identifier_attrs,  }; -static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = { -	&hisi_l3c_pmu_format_group, -	&hisi_l3c_pmu_events_group, +static const struct attribute_group *hisi_l3c_pmu_v1_attr_groups[] = { +	&hisi_l3c_pmu_v1_format_group, +	&hisi_l3c_pmu_v1_events_group,  	&hisi_l3c_pmu_cpumask_attr_group,  	&hisi_l3c_pmu_identifier_group,  	NULL,  }; +static const struct attribute_group *hisi_l3c_pmu_v2_attr_groups[] = { +	&hisi_l3c_pmu_v2_format_group, +	&hisi_l3c_pmu_v2_events_group, +	&hisi_l3c_pmu_cpumask_attr_group, +	&hisi_l3c_pmu_identifier_group, +	NULL +}; +  static const struct hisi_uncore_ops hisi_uncore_l3c_ops = {  	.write_evtype		= hisi_l3c_pmu_write_evtype,  	.get_event_idx		= hisi_uncore_pmu_get_event_idx, @@ -344,6 +491,10 @@ static const struct hisi_uncore_ops hisi_uncore_l3c_ops = {  	.disable_counter_int	= hisi_l3c_pmu_disable_counter_int,  	.write_counter		= hisi_l3c_pmu_write_counter,  	.read_counter		= hisi_l3c_pmu_read_counter, +	.get_int_status		= hisi_l3c_pmu_get_int_status, +	.clear_int_status	= hisi_l3c_pmu_clear_int_status, +	.enable_filter		= hisi_l3c_pmu_enable_filter, +	.disable_filter		= hisi_l3c_pmu_disable_filter,  };  static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev, @@ -355,16 +506,24 @@ static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev,  	if (ret)  		return ret; -	ret = hisi_l3c_pmu_init_irq(l3c_pmu, pdev); +	ret = hisi_uncore_pmu_init_irq(l3c_pmu, pdev);  	if (ret)  		return ret; +	if (l3c_pmu->identifier >= HISI_PMU_V2) { +		l3c_pmu->counter_bits = 64; +		l3c_pmu->check_event = L3C_V2_NR_EVENTS; +		l3c_pmu->pmu_events.attr_groups = hisi_l3c_pmu_v2_attr_groups; +	} else { +		l3c_pmu->counter_bits = 48; +		l3c_pmu->check_event = L3C_V1_NR_EVENTS; +		l3c_pmu->pmu_events.attr_groups = hisi_l3c_pmu_v1_attr_groups; +	} +  	l3c_pmu->num_counters = L3C_NR_COUNTERS; -	l3c_pmu->counter_bits = 48;  	l3c_pmu->ops = &hisi_uncore_l3c_ops;  	l3c_pmu->dev = &pdev->dev;  	l3c_pmu->on_cpu = -1; -	l3c_pmu->check_event = 0x59;  	return 0;  } @@ -392,8 +551,12 @@ static int hisi_l3c_pmu_probe(struct platform_device *pdev)  		return ret;  	} +	/* +	 * CCL_ID is used to identify the L3C in the same SCCL which was +	 * used _UID by mistake. +	 */  	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_l3c%u", -			      l3c_pmu->sccl_id, l3c_pmu->index_id); +			      l3c_pmu->sccl_id, l3c_pmu->ccl_id);  	l3c_pmu->pmu = (struct pmu) {  		.name		= name,  		.module		= THIS_MODULE, @@ -406,7 +569,7 @@ static int hisi_l3c_pmu_probe(struct platform_device *pdev)  		.start		= hisi_uncore_pmu_start,  		.stop		= hisi_uncore_pmu_stop,  		.read		= hisi_uncore_pmu_read, -		.attr_groups	= hisi_l3c_pmu_attr_groups, +		.attr_groups	= l3c_pmu->pmu_events.attr_groups,  		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,  	}; diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c new file mode 100644 index 000000000000..14f23eb31248 --- /dev/null +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HiSilicon PA uncore Hardware event counters support + * + * Copyright (C) 2020 HiSilicon Limited + * Author: Shaokun Zhang <zhangshaokun@hisilicon.com> + * + * This code is based on the uncore PMUs like arm-cci and arm-ccn. + */ +#include <linux/acpi.h> +#include <linux/cpuhotplug.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/list.h> +#include <linux/smp.h> + +#include "hisi_uncore_pmu.h" + +/* PA register definition */ +#define PA_PERF_CTRL			0x1c00 +#define PA_EVENT_CTRL			0x1c04 +#define PA_TT_CTRL			0x1c08 +#define PA_TGTID_CTRL			0x1c14 +#define PA_SRCID_CTRL			0x1c18 +#define PA_INT_MASK			0x1c70 +#define PA_INT_STATUS			0x1c78 +#define PA_INT_CLEAR			0x1c7c +#define PA_EVENT_TYPE0			0x1c80 +#define PA_PMU_VERSION			0x1cf0 +#define PA_EVENT_CNT0_L			0x1f00 + +#define PA_EVTYPE_MASK			0xff +#define PA_NR_COUNTERS			0x8 +#define PA_PERF_CTRL_EN			BIT(0) +#define PA_TRACETAG_EN			BIT(4) +#define PA_TGTID_EN			BIT(11) +#define PA_SRCID_EN			BIT(11) +#define PA_TGTID_NONE			0 +#define PA_SRCID_NONE			0 +#define PA_TGTID_MSK_SHIFT		12 +#define PA_SRCID_MSK_SHIFT		12 + +HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_cmd, config1, 10, 0); +HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_msk, config1, 21, 11); +HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22); +HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33); +HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44); + +static void hisi_pa_pmu_enable_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); +	u32 tt_en = hisi_get_tracetag_en(event); + +	if (tt_en) { +		u32 val; + +		val = readl(pa_pmu->base + PA_TT_CTRL); +		val |= PA_TRACETAG_EN; +		writel(val, pa_pmu->base + PA_TT_CTRL); +	} +} + +static void hisi_pa_pmu_clear_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); +	u32 tt_en = hisi_get_tracetag_en(event); + +	if (tt_en) { +		u32 val; + +		val = readl(pa_pmu->base + PA_TT_CTRL); +		val &= ~PA_TRACETAG_EN; +		writel(val, pa_pmu->base + PA_TT_CTRL); +	} +} + +static void hisi_pa_pmu_config_tgtid(struct perf_event *event) +{ +	struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); +	u32 cmd = hisi_get_tgtid_cmd(event); + +	if (cmd) { +		u32 msk = hisi_get_tgtid_msk(event); +		u32 val = cmd | PA_TGTID_EN | (msk << PA_TGTID_MSK_SHIFT); + +		writel(val, pa_pmu->base + PA_TGTID_CTRL); +	} +} + +static void hisi_pa_pmu_clear_tgtid(struct perf_event *event) +{ +	struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); +	u32 cmd = hisi_get_tgtid_cmd(event); + +	if (cmd) +		writel(PA_TGTID_NONE, pa_pmu->base + PA_TGTID_CTRL); +} + +static void hisi_pa_pmu_config_srcid(struct perf_event *event) +{ +	struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); +	u32 cmd = hisi_get_srcid_cmd(event); + +	if (cmd) { +		u32 msk = hisi_get_srcid_msk(event); +		u32 val = cmd | PA_SRCID_EN | (msk << PA_SRCID_MSK_SHIFT); + +		writel(val, pa_pmu->base + PA_SRCID_CTRL); +	} +} + +static void hisi_pa_pmu_clear_srcid(struct perf_event *event) +{ +	struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); +	u32 cmd = hisi_get_srcid_cmd(event); + +	if (cmd) +		writel(PA_SRCID_NONE, pa_pmu->base + PA_SRCID_CTRL); +} + +static void hisi_pa_pmu_enable_filter(struct perf_event *event) +{ +	if (event->attr.config1 != 0x0) { +		hisi_pa_pmu_enable_tracetag(event); +		hisi_pa_pmu_config_srcid(event); +		hisi_pa_pmu_config_tgtid(event); +	} +} + +static void hisi_pa_pmu_disable_filter(struct perf_event *event) +{ +	if (event->attr.config1 != 0x0) { +		hisi_pa_pmu_clear_tgtid(event); +		hisi_pa_pmu_clear_srcid(event); +		hisi_pa_pmu_clear_tracetag(event); +	} +} + +static u32 hisi_pa_pmu_get_counter_offset(int idx) +{ +	return (PA_EVENT_CNT0_L + idx * 8); +} + +static u64 hisi_pa_pmu_read_counter(struct hisi_pmu *pa_pmu, +				    struct hw_perf_event *hwc) +{ +	return readq(pa_pmu->base + hisi_pa_pmu_get_counter_offset(hwc->idx)); +} + +static void hisi_pa_pmu_write_counter(struct hisi_pmu *pa_pmu, +				      struct hw_perf_event *hwc, u64 val) +{ +	writeq(val, pa_pmu->base + hisi_pa_pmu_get_counter_offset(hwc->idx)); +} + +static void hisi_pa_pmu_write_evtype(struct hisi_pmu *pa_pmu, int idx, +				     u32 type) +{ +	u32 reg, reg_idx, shift, val; + +	/* +	 * Select the appropriate event select register(PA_EVENT_TYPE0/1). +	 * There are 2 event select registers for the 8 hardware counters. +	 * Event code is 8-bits and for the former 4 hardware counters, +	 * PA_EVENT_TYPE0 is chosen. For the latter 4 hardware counters, +	 * PA_EVENT_TYPE1 is chosen. +	 */ +	reg = PA_EVENT_TYPE0 + (idx / 4) * 4; +	reg_idx = idx % 4; +	shift = 8 * reg_idx; + +	/* Write event code to pa_EVENT_TYPEx Register */ +	val = readl(pa_pmu->base + reg); +	val &= ~(PA_EVTYPE_MASK << shift); +	val |= (type << shift); +	writel(val, pa_pmu->base + reg); +} + +static void hisi_pa_pmu_start_counters(struct hisi_pmu *pa_pmu) +{ +	u32 val; + +	val = readl(pa_pmu->base + PA_PERF_CTRL); +	val |= PA_PERF_CTRL_EN; +	writel(val, pa_pmu->base + PA_PERF_CTRL); +} + +static void hisi_pa_pmu_stop_counters(struct hisi_pmu *pa_pmu) +{ +	u32 val; + +	val = readl(pa_pmu->base + PA_PERF_CTRL); +	val &= ~(PA_PERF_CTRL_EN); +	writel(val, pa_pmu->base + PA_PERF_CTRL); +} + +static void hisi_pa_pmu_enable_counter(struct hisi_pmu *pa_pmu, +				       struct hw_perf_event *hwc) +{ +	u32 val; + +	/* Enable counter index in PA_EVENT_CTRL register */ +	val = readl(pa_pmu->base + PA_EVENT_CTRL); +	val |= 1 << hwc->idx; +	writel(val, pa_pmu->base + PA_EVENT_CTRL); +} + +static void hisi_pa_pmu_disable_counter(struct hisi_pmu *pa_pmu, +					struct hw_perf_event *hwc) +{ +	u32 val; + +	/* Clear counter index in PA_EVENT_CTRL register */ +	val = readl(pa_pmu->base + PA_EVENT_CTRL); +	val &= ~(1 << hwc->idx); +	writel(val, pa_pmu->base + PA_EVENT_CTRL); +} + +static void hisi_pa_pmu_enable_counter_int(struct hisi_pmu *pa_pmu, +					   struct hw_perf_event *hwc) +{ +	u32 val; + +	/* Write 0 to enable interrupt */ +	val = readl(pa_pmu->base + PA_INT_MASK); +	val &= ~(1 << hwc->idx); +	writel(val, pa_pmu->base + PA_INT_MASK); +} + +static void hisi_pa_pmu_disable_counter_int(struct hisi_pmu *pa_pmu, +					    struct hw_perf_event *hwc) +{ +	u32 val; + +	/* Write 1 to mask interrupt */ +	val = readl(pa_pmu->base + PA_INT_MASK); +	val |= 1 << hwc->idx; +	writel(val, pa_pmu->base + PA_INT_MASK); +} + +static u32 hisi_pa_pmu_get_int_status(struct hisi_pmu *pa_pmu) +{ +	return readl(pa_pmu->base + PA_INT_STATUS); +} + +static void hisi_pa_pmu_clear_int_status(struct hisi_pmu *pa_pmu, int idx) +{ +	writel(1 << idx, pa_pmu->base + PA_INT_CLEAR); +} + +static const struct acpi_device_id hisi_pa_pmu_acpi_match[] = { +	{ "HISI0273", }, +	{} +}; +MODULE_DEVICE_TABLE(acpi, hisi_pa_pmu_acpi_match); + +static int hisi_pa_pmu_init_data(struct platform_device *pdev, +				   struct hisi_pmu *pa_pmu) +{ +	/* +	 * Use the SCCL_ID and the index ID to identify the PA PMU, +	 * while SCCL_ID is the nearst SCCL_ID from this SICL and +	 * CPU core is chosen from this SCCL to manage this PMU. +	 */ +	if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", +				     &pa_pmu->sccl_id)) { +		dev_err(&pdev->dev, "Cannot read sccl-id!\n"); +		return -EINVAL; +	} + +	if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", +				     &pa_pmu->index_id)) { +		dev_err(&pdev->dev, "Cannot read idx-id!\n"); +		return -EINVAL; +	} + +	pa_pmu->ccl_id = -1; + +	pa_pmu->base = devm_platform_ioremap_resource(pdev, 0); +	if (IS_ERR(pa_pmu->base)) { +		dev_err(&pdev->dev, "ioremap failed for pa_pmu resource.\n"); +		return PTR_ERR(pa_pmu->base); +	} + +	pa_pmu->identifier = readl(pa_pmu->base + PA_PMU_VERSION); + +	return 0; +} + +static struct attribute *hisi_pa_pmu_v2_format_attr[] = { +	HISI_PMU_FORMAT_ATTR(event, "config:0-7"), +	HISI_PMU_FORMAT_ATTR(tgtid_cmd, "config1:0-10"), +	HISI_PMU_FORMAT_ATTR(tgtid_msk, "config1:11-21"), +	HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:22-32"), +	HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:33-43"), +	HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:44"), +	NULL, +}; + +static const struct attribute_group hisi_pa_pmu_v2_format_group = { +	.name = "format", +	.attrs = hisi_pa_pmu_v2_format_attr, +}; + +static struct attribute *hisi_pa_pmu_v2_events_attr[] = { +	HISI_PMU_EVENT_ATTR(rx_req,		0x40), +	HISI_PMU_EVENT_ATTR(tx_req,             0x5c), +	HISI_PMU_EVENT_ATTR(cycle,		0x78), +	NULL +}; + +static const struct attribute_group hisi_pa_pmu_v2_events_group = { +	.name = "events", +	.attrs = hisi_pa_pmu_v2_events_attr, +}; + +static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); + +static struct attribute *hisi_pa_pmu_cpumask_attrs[] = { +	&dev_attr_cpumask.attr, +	NULL +}; + +static const struct attribute_group hisi_pa_pmu_cpumask_attr_group = { +	.attrs = hisi_pa_pmu_cpumask_attrs, +}; + +static struct device_attribute hisi_pa_pmu_identifier_attr = +	__ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); + +static struct attribute *hisi_pa_pmu_identifier_attrs[] = { +	&hisi_pa_pmu_identifier_attr.attr, +	NULL +}; + +static struct attribute_group hisi_pa_pmu_identifier_group = { +	.attrs = hisi_pa_pmu_identifier_attrs, +}; + +static const struct attribute_group *hisi_pa_pmu_v2_attr_groups[] = { +	&hisi_pa_pmu_v2_format_group, +	&hisi_pa_pmu_v2_events_group, +	&hisi_pa_pmu_cpumask_attr_group, +	&hisi_pa_pmu_identifier_group, +	NULL +}; + +static const struct hisi_uncore_ops hisi_uncore_pa_ops = { +	.write_evtype		= hisi_pa_pmu_write_evtype, +	.get_event_idx		= hisi_uncore_pmu_get_event_idx, +	.start_counters		= hisi_pa_pmu_start_counters, +	.stop_counters		= hisi_pa_pmu_stop_counters, +	.enable_counter		= hisi_pa_pmu_enable_counter, +	.disable_counter	= hisi_pa_pmu_disable_counter, +	.enable_counter_int	= hisi_pa_pmu_enable_counter_int, +	.disable_counter_int	= hisi_pa_pmu_disable_counter_int, +	.write_counter		= hisi_pa_pmu_write_counter, +	.read_counter		= hisi_pa_pmu_read_counter, +	.get_int_status		= hisi_pa_pmu_get_int_status, +	.clear_int_status	= hisi_pa_pmu_clear_int_status, +	.enable_filter		= hisi_pa_pmu_enable_filter, +	.disable_filter		= hisi_pa_pmu_disable_filter, +}; + +static int hisi_pa_pmu_dev_probe(struct platform_device *pdev, +				 struct hisi_pmu *pa_pmu) +{ +	int ret; + +	ret = hisi_pa_pmu_init_data(pdev, pa_pmu); +	if (ret) +		return ret; + +	ret = hisi_uncore_pmu_init_irq(pa_pmu, pdev); +	if (ret) +		return ret; + +	pa_pmu->pmu_events.attr_groups = hisi_pa_pmu_v2_attr_groups; +	pa_pmu->num_counters = PA_NR_COUNTERS; +	pa_pmu->ops = &hisi_uncore_pa_ops; +	pa_pmu->check_event = 0xB0; +	pa_pmu->counter_bits = 64; +	pa_pmu->dev = &pdev->dev; +	pa_pmu->on_cpu = -1; + +	return 0; +} + +static int hisi_pa_pmu_probe(struct platform_device *pdev) +{ +	struct hisi_pmu *pa_pmu; +	char *name; +	int ret; + +	pa_pmu = devm_kzalloc(&pdev->dev, sizeof(*pa_pmu), GFP_KERNEL); +	if (!pa_pmu) +		return -ENOMEM; + +	ret = hisi_pa_pmu_dev_probe(pdev, pa_pmu); +	if (ret) +		return ret; +	/* +	 * PA is attached in SICL and the CPU core is chosen to manage this +	 * PMU which is the nearest SCCL, while its SCCL_ID is greater than +	 * one with the SICL_ID. +	 */ +	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%u_pa%u", +			      pa_pmu->sccl_id - 1, pa_pmu->index_id); +	if (!name) +		return -ENOMEM; + +	ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE, +				       &pa_pmu->node); +	if (ret) { +		dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); +		return ret; +	} + +	pa_pmu->pmu = (struct pmu) { +		.module		= THIS_MODULE, +		.task_ctx_nr	= perf_invalid_context, +		.event_init	= hisi_uncore_pmu_event_init, +		.pmu_enable	= hisi_uncore_pmu_enable, +		.pmu_disable	= hisi_uncore_pmu_disable, +		.add		= hisi_uncore_pmu_add, +		.del		= hisi_uncore_pmu_del, +		.start		= hisi_uncore_pmu_start, +		.stop		= hisi_uncore_pmu_stop, +		.read		= hisi_uncore_pmu_read, +		.attr_groups    = pa_pmu->pmu_events.attr_groups, +		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE, +	}; + +	ret = perf_pmu_register(&pa_pmu->pmu, name, -1); +	if (ret) { +		dev_err(pa_pmu->dev, "PMU register failed, ret = %d\n", ret); +		cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE, +					    &pa_pmu->node); +		irq_set_affinity_hint(pa_pmu->irq, NULL); +		return ret; +	} + +	platform_set_drvdata(pdev, pa_pmu); +	return ret; +} + +static int hisi_pa_pmu_remove(struct platform_device *pdev) +{ +	struct hisi_pmu *pa_pmu = platform_get_drvdata(pdev); + +	perf_pmu_unregister(&pa_pmu->pmu); +	cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE, +					    &pa_pmu->node); +	irq_set_affinity_hint(pa_pmu->irq, NULL); + +	return 0; +} + +static struct platform_driver hisi_pa_pmu_driver = { +	.driver = { +		.name = "hisi_pa_pmu", +		.acpi_match_table = hisi_pa_pmu_acpi_match, +		.suppress_bind_attrs = true, +	}, +	.probe = hisi_pa_pmu_probe, +	.remove = hisi_pa_pmu_remove, +}; + +static int __init hisi_pa_pmu_module_init(void) +{ +	int ret; + +	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE, +				      "AP_PERF_ARM_HISI_PA_ONLINE", +				      hisi_uncore_pmu_online_cpu, +				      hisi_uncore_pmu_offline_cpu); +	if (ret) { +		pr_err("PA PMU: cpuhp state setup failed, ret = %d\n", ret); +		return ret; +	} + +	ret = platform_driver_register(&hisi_pa_pmu_driver); +	if (ret) +		cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE); + +	return ret; +} +module_init(hisi_pa_pmu_module_init); + +static void __exit hisi_pa_pmu_module_exit(void) +{ +	platform_driver_unregister(&hisi_pa_pmu_driver); +	cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE); +} +module_exit(hisi_pa_pmu_module_exit); + +MODULE_DESCRIPTION("HiSilicon Protocol Adapter uncore PMU driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>"); +MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>"); diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index 9dbdc3fc3bb4..13c68b5e39c4 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -21,7 +21,7 @@  #include "hisi_uncore_pmu.h"  #define HISI_GET_EVENTID(ev) (ev->hw.config_base & 0xff) -#define HISI_MAX_PERIOD(nr) (BIT_ULL(nr) - 1) +#define HISI_MAX_PERIOD(nr) (GENMASK_ULL((nr) - 1, 0))  /*   * PMU format attributes @@ -33,7 +33,7 @@ ssize_t hisi_format_sysfs_show(struct device *dev,  	eattr = container_of(attr, struct dev_ext_attribute, attr); -	return sprintf(buf, "%s\n", (char *)eattr->var); +	return sysfs_emit(buf, "%s\n", (char *)eattr->var);  }  EXPORT_SYMBOL_GPL(hisi_format_sysfs_show); @@ -47,7 +47,7 @@ ssize_t hisi_event_sysfs_show(struct device *dev,  	eattr = container_of(attr, struct dev_ext_attribute, attr); -	return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); +	return sysfs_emit(page, "config=0x%lx\n", (unsigned long)eattr->var);  }  EXPORT_SYMBOL_GPL(hisi_event_sysfs_show); @@ -59,7 +59,7 @@ ssize_t hisi_cpumask_sysfs_show(struct device *dev,  {  	struct hisi_pmu *hisi_pmu = to_hisi_pmu(dev_get_drvdata(dev)); -	return sprintf(buf, "%d\n", hisi_pmu->on_cpu); +	return sysfs_emit(buf, "%d\n", hisi_pmu->on_cpu);  }  EXPORT_SYMBOL_GPL(hisi_cpumask_sysfs_show); @@ -96,12 +96,6 @@ static bool hisi_validate_event_group(struct perf_event *event)  	return counters <= hisi_pmu->num_counters;  } -int hisi_uncore_pmu_counter_valid(struct hisi_pmu *hisi_pmu, int idx) -{ -	return idx >= 0 && idx < hisi_pmu->num_counters; -} -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_counter_valid); -  int hisi_uncore_pmu_get_event_idx(struct perf_event *event)  {  	struct hisi_pmu *hisi_pmu = to_hisi_pmu(event->pmu); @@ -125,19 +119,68 @@ ssize_t hisi_uncore_pmu_identifier_attr_show(struct device *dev,  {  	struct hisi_pmu *hisi_pmu = to_hisi_pmu(dev_get_drvdata(dev)); -	return snprintf(page, PAGE_SIZE, "0x%08x\n", hisi_pmu->identifier); +	return sysfs_emit(page, "0x%08x\n", hisi_pmu->identifier);  }  EXPORT_SYMBOL_GPL(hisi_uncore_pmu_identifier_attr_show);  static void hisi_uncore_pmu_clear_event_idx(struct hisi_pmu *hisi_pmu, int idx)  { -	if (!hisi_uncore_pmu_counter_valid(hisi_pmu, idx)) { -		dev_err(hisi_pmu->dev, "Unsupported event index:%d!\n", idx); -		return; +	clear_bit(idx, hisi_pmu->pmu_events.used_mask); +} + +static irqreturn_t hisi_uncore_pmu_isr(int irq, void *data) +{ +	struct hisi_pmu *hisi_pmu = data; +	struct perf_event *event; +	unsigned long overflown; +	int idx; + +	overflown = hisi_pmu->ops->get_int_status(hisi_pmu); +	if (!overflown) +		return IRQ_NONE; + +	/* +	 * Find the counter index which overflowed if the bit was set +	 * and handle it. +	 */ +	for_each_set_bit(idx, &overflown, hisi_pmu->num_counters) { +		/* Write 1 to clear the IRQ status flag */ +		hisi_pmu->ops->clear_int_status(hisi_pmu, idx); +		/* Get the corresponding event struct */ +		event = hisi_pmu->pmu_events.hw_events[idx]; +		if (!event) +			continue; + +		hisi_uncore_pmu_event_update(event); +		hisi_uncore_pmu_set_event_period(event);  	} -	clear_bit(idx, hisi_pmu->pmu_events.used_mask); +	return IRQ_HANDLED; +} + +int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu, +			     struct platform_device *pdev) +{ +	int irq, ret; + +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) +		return irq; + +	ret = devm_request_irq(&pdev->dev, irq, hisi_uncore_pmu_isr, +			       IRQF_NOBALANCING | IRQF_NO_THREAD, +			       dev_name(&pdev->dev), hisi_pmu); +	if (ret < 0) { +		dev_err(&pdev->dev, +			"Fail to request IRQ: %d ret: %d.\n", irq, ret); +		return ret; +	} + +	hisi_pmu->irq = irq; + +	return 0;  } +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_init_irq);  int hisi_uncore_pmu_event_init(struct perf_event *event)  { @@ -202,6 +245,9 @@ static void hisi_uncore_pmu_enable_event(struct perf_event *event)  	hisi_pmu->ops->write_evtype(hisi_pmu, hwc->idx,  				    HISI_GET_EVENTID(event)); +	if (hisi_pmu->ops->enable_filter) +		hisi_pmu->ops->enable_filter(event); +  	hisi_pmu->ops->enable_counter_int(hisi_pmu, hwc);  	hisi_pmu->ops->enable_counter(hisi_pmu, hwc);  } @@ -216,6 +262,9 @@ static void hisi_uncore_pmu_disable_event(struct perf_event *event)  	hisi_pmu->ops->disable_counter(hisi_pmu, hwc);  	hisi_pmu->ops->disable_counter_int(hisi_pmu, hwc); + +	if (hisi_pmu->ops->disable_filter) +		hisi_pmu->ops->disable_filter(event);  }  void hisi_uncore_pmu_set_event_period(struct perf_event *event) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 25b7cbe1f818..ea9d89bbc1ea 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -11,16 +11,19 @@  #ifndef __HISI_UNCORE_PMU_H__  #define __HISI_UNCORE_PMU_H__ +#include <linux/bitfield.h>  #include <linux/cpumask.h>  #include <linux/device.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/perf_event.h> +#include <linux/platform_device.h>  #include <linux/types.h>  #undef pr_fmt  #define pr_fmt(fmt)     "hisi_pmu: " fmt +#define HISI_PMU_V2		0x30  #define HISI_MAX_COUNTERS 0x10  #define to_hisi_pmu(p)	(container_of(p, struct hisi_pmu, pmu)) @@ -34,6 +37,12 @@  #define HISI_PMU_EVENT_ATTR(_name, _config)		\  	HISI_PMU_ATTR(_name, hisi_event_sysfs_show, (unsigned long)_config) +#define HISI_PMU_EVENT_ATTR_EXTRACTOR(name, config, hi, lo)        \ +	static inline u32 hisi_get_##name(struct perf_event *event)            \ +	{                                                                  \ +		return FIELD_GET(GENMASK_ULL(hi, lo), event->attr.config);  \ +	} +  struct hisi_pmu;  struct hisi_uncore_ops { @@ -47,11 +56,16 @@ struct hisi_uncore_ops {  	void (*disable_counter_int)(struct hisi_pmu *, struct hw_perf_event *);  	void (*start_counters)(struct hisi_pmu *);  	void (*stop_counters)(struct hisi_pmu *); +	u32 (*get_int_status)(struct hisi_pmu *hisi_pmu); +	void (*clear_int_status)(struct hisi_pmu *hisi_pmu, int idx); +	void (*enable_filter)(struct perf_event *event); +	void (*disable_filter)(struct perf_event *event);  };  struct hisi_pmu_hwevents {  	struct perf_event *hw_events[HISI_MAX_COUNTERS];  	DECLARE_BITMAP(used_mask, HISI_MAX_COUNTERS); +	const struct attribute_group **attr_groups;  };  /* Generic pmu struct for different pmu types */ @@ -71,6 +85,8 @@ struct hisi_pmu {  	void __iomem *base;  	/* the ID of the PMU modules */  	u32 index_id; +	/* For DDRC PMU v2: each DDRC has more than one DMC */ +	u32 sub_id;  	int num_counters;  	int counter_bits;  	/* check event code range */ @@ -78,7 +94,6 @@ struct hisi_pmu {  	u32 identifier;  }; -int hisi_uncore_pmu_counter_valid(struct hisi_pmu *hisi_pmu, int idx);  int hisi_uncore_pmu_get_event_idx(struct perf_event *event);  void hisi_uncore_pmu_read(struct perf_event *event);  int hisi_uncore_pmu_add(struct perf_event *event, int flags); @@ -102,6 +117,7 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node);  ssize_t hisi_uncore_pmu_identifier_attr_show(struct device *dev,  					     struct device_attribute *attr,  					     char *page); - +int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu, +			     struct platform_device *pdev);  #endif /* __HISI_UNCORE_PMU_H__ */ diff --git a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c new file mode 100644 index 000000000000..46be312fa126 --- /dev/null +++ b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HiSilicon SLLC uncore Hardware event counters support + * + * Copyright (C) 2020 Hisilicon Limited + * Author: Shaokun Zhang <zhangshaokun@hisilicon.com> + * + * This code is based on the uncore PMUs like arm-cci and arm-ccn. + */ +#include <linux/acpi.h> +#include <linux/cpuhotplug.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/list.h> +#include <linux/smp.h> + +#include "hisi_uncore_pmu.h" + +/* SLLC register definition */ +#define SLLC_INT_MASK			0x0814 +#define SLLC_INT_STATUS			0x0818 +#define SLLC_INT_CLEAR			0x081c +#define SLLC_PERF_CTRL			0x1c00 +#define SLLC_SRCID_CTRL			0x1c04 +#define SLLC_TGTID_CTRL			0x1c08 +#define SLLC_EVENT_CTRL			0x1c14 +#define SLLC_EVENT_TYPE0		0x1c18 +#define SLLC_VERSION			0x1cf0 +#define SLLC_EVENT_CNT0_L		0x1d00 + +#define SLLC_EVTYPE_MASK		0xff +#define SLLC_PERF_CTRL_EN		BIT(0) +#define SLLC_FILT_EN			BIT(1) +#define SLLC_TRACETAG_EN		BIT(2) +#define SLLC_SRCID_EN			BIT(4) +#define SLLC_SRCID_NONE			0x0 +#define SLLC_TGTID_EN			BIT(5) +#define SLLC_TGTID_NONE			0x0 +#define SLLC_TGTID_MIN_SHIFT		1 +#define SLLC_TGTID_MAX_SHIFT		12 +#define SLLC_SRCID_CMD_SHIFT		1 +#define SLLC_SRCID_MSK_SHIFT		12 +#define SLLC_NR_EVENTS			0x80 + +HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_min, config1, 10, 0); +HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_max, config1, 21, 11); +HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22); +HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33); +HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44); + +static bool tgtid_is_valid(u32 max, u32 min) +{ +	return max > 0 && max >= min; +} + +static void hisi_sllc_pmu_enable_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); +	u32 tt_en = hisi_get_tracetag_en(event); + +	if (tt_en) { +		u32 val; + +		val = readl(sllc_pmu->base + SLLC_PERF_CTRL); +		val |= SLLC_TRACETAG_EN | SLLC_FILT_EN; +		writel(val, sllc_pmu->base + SLLC_PERF_CTRL); +	} +} + +static void hisi_sllc_pmu_disable_tracetag(struct perf_event *event) +{ +	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); +	u32 tt_en = hisi_get_tracetag_en(event); + +	if (tt_en) { +		u32 val; + +		val = readl(sllc_pmu->base + SLLC_PERF_CTRL); +		val &= ~(SLLC_TRACETAG_EN | SLLC_FILT_EN); +		writel(val, sllc_pmu->base + SLLC_PERF_CTRL); +	} +} + +static void hisi_sllc_pmu_config_tgtid(struct perf_event *event) +{ +	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); +	u32 min = hisi_get_tgtid_min(event); +	u32 max = hisi_get_tgtid_max(event); + +	if (tgtid_is_valid(max, min)) { +		u32 val = (max << SLLC_TGTID_MAX_SHIFT) | (min << SLLC_TGTID_MIN_SHIFT); + +		writel(val, sllc_pmu->base + SLLC_TGTID_CTRL); +		/* Enable the tgtid */ +		val = readl(sllc_pmu->base + SLLC_PERF_CTRL); +		val |= SLLC_TGTID_EN | SLLC_FILT_EN; +		writel(val, sllc_pmu->base + SLLC_PERF_CTRL); +	} +} + +static void hisi_sllc_pmu_clear_tgtid(struct perf_event *event) +{ +	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); +	u32 min = hisi_get_tgtid_min(event); +	u32 max = hisi_get_tgtid_max(event); + +	if (tgtid_is_valid(max, min)) { +		u32 val; + +		writel(SLLC_TGTID_NONE, sllc_pmu->base + SLLC_TGTID_CTRL); +		/* Disable the tgtid */ +		val = readl(sllc_pmu->base + SLLC_PERF_CTRL); +		val &= ~(SLLC_TGTID_EN | SLLC_FILT_EN); +		writel(val, sllc_pmu->base + SLLC_PERF_CTRL); +	} +} + +static void hisi_sllc_pmu_config_srcid(struct perf_event *event) +{ +	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); +	u32 cmd = hisi_get_srcid_cmd(event); + +	if (cmd) { +		u32 val, msk; + +		msk = hisi_get_srcid_msk(event); +		val = (cmd << SLLC_SRCID_CMD_SHIFT) | (msk << SLLC_SRCID_MSK_SHIFT); +		writel(val, sllc_pmu->base + SLLC_SRCID_CTRL); +		/* Enable the srcid */ +		val = readl(sllc_pmu->base + SLLC_PERF_CTRL); +		val |= SLLC_SRCID_EN | SLLC_FILT_EN; +		writel(val, sllc_pmu->base + SLLC_PERF_CTRL); +	} +} + +static void hisi_sllc_pmu_clear_srcid(struct perf_event *event) +{ +	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); +	u32 cmd = hisi_get_srcid_cmd(event); + +	if (cmd) { +		u32 val; + +		writel(SLLC_SRCID_NONE, sllc_pmu->base + SLLC_SRCID_CTRL); +		/* Disable the srcid */ +		val = readl(sllc_pmu->base + SLLC_PERF_CTRL); +		val &= ~(SLLC_SRCID_EN | SLLC_FILT_EN); +		writel(val, sllc_pmu->base + SLLC_PERF_CTRL); +	} +} + +static void hisi_sllc_pmu_enable_filter(struct perf_event *event) +{ +	if (event->attr.config1 != 0x0) { +		hisi_sllc_pmu_enable_tracetag(event); +		hisi_sllc_pmu_config_srcid(event); +		hisi_sllc_pmu_config_tgtid(event); +	} +} + +static void hisi_sllc_pmu_clear_filter(struct perf_event *event) +{ +	if (event->attr.config1 != 0x0) { +		hisi_sllc_pmu_disable_tracetag(event); +		hisi_sllc_pmu_clear_srcid(event); +		hisi_sllc_pmu_clear_tgtid(event); +	} +} + +static u32 hisi_sllc_pmu_get_counter_offset(int idx) +{ +	return (SLLC_EVENT_CNT0_L + idx * 8); +} + +static u64 hisi_sllc_pmu_read_counter(struct hisi_pmu *sllc_pmu, +				      struct hw_perf_event *hwc) +{ +	return readq(sllc_pmu->base + +		     hisi_sllc_pmu_get_counter_offset(hwc->idx)); +} + +static void hisi_sllc_pmu_write_counter(struct hisi_pmu *sllc_pmu, +					struct hw_perf_event *hwc, u64 val) +{ +	writeq(val, sllc_pmu->base + +	       hisi_sllc_pmu_get_counter_offset(hwc->idx)); +} + +static void hisi_sllc_pmu_write_evtype(struct hisi_pmu *sllc_pmu, int idx, +				       u32 type) +{ +	u32 reg, reg_idx, shift, val; + +	/* +	 * Select the appropriate event select register(SLLC_EVENT_TYPE0/1). +	 * There are 2 event select registers for the 8 hardware counters. +	 * Event code is 8-bits and for the former 4 hardware counters, +	 * SLLC_EVENT_TYPE0 is chosen. For the latter 4 hardware counters, +	 * SLLC_EVENT_TYPE1 is chosen. +	 */ +	reg = SLLC_EVENT_TYPE0 + (idx / 4) * 4; +	reg_idx = idx % 4; +	shift = 8 * reg_idx; + +	/* Write event code to SLLC_EVENT_TYPEx Register */ +	val = readl(sllc_pmu->base + reg); +	val &= ~(SLLC_EVTYPE_MASK << shift); +	val |= (type << shift); +	writel(val, sllc_pmu->base + reg); +} + +static void hisi_sllc_pmu_start_counters(struct hisi_pmu *sllc_pmu) +{ +	u32 val; + +	val = readl(sllc_pmu->base + SLLC_PERF_CTRL); +	val |= SLLC_PERF_CTRL_EN; +	writel(val, sllc_pmu->base + SLLC_PERF_CTRL); +} + +static void hisi_sllc_pmu_stop_counters(struct hisi_pmu *sllc_pmu) +{ +	u32 val; + +	val = readl(sllc_pmu->base + SLLC_PERF_CTRL); +	val &= ~(SLLC_PERF_CTRL_EN); +	writel(val, sllc_pmu->base + SLLC_PERF_CTRL); +} + +static void hisi_sllc_pmu_enable_counter(struct hisi_pmu *sllc_pmu, +					 struct hw_perf_event *hwc) +{ +	u32 val; + +	val = readl(sllc_pmu->base + SLLC_EVENT_CTRL); +	val |= 1 << hwc->idx; +	writel(val, sllc_pmu->base + SLLC_EVENT_CTRL); +} + +static void hisi_sllc_pmu_disable_counter(struct hisi_pmu *sllc_pmu, +					  struct hw_perf_event *hwc) +{ +	u32 val; + +	val = readl(sllc_pmu->base + SLLC_EVENT_CTRL); +	val &= ~(1 << hwc->idx); +	writel(val, sllc_pmu->base + SLLC_EVENT_CTRL); +} + +static void hisi_sllc_pmu_enable_counter_int(struct hisi_pmu *sllc_pmu, +					     struct hw_perf_event *hwc) +{ +	u32 val; + +	val = readl(sllc_pmu->base + SLLC_INT_MASK); +	/* Write 0 to enable interrupt */ +	val &= ~(1 << hwc->idx); +	writel(val, sllc_pmu->base + SLLC_INT_MASK); +} + +static void hisi_sllc_pmu_disable_counter_int(struct hisi_pmu *sllc_pmu, +					      struct hw_perf_event *hwc) +{ +	u32 val; + +	val = readl(sllc_pmu->base + SLLC_INT_MASK); +	/* Write 1 to mask interrupt */ +	val |= 1 << hwc->idx; +	writel(val, sllc_pmu->base + SLLC_INT_MASK); +} + +static u32 hisi_sllc_pmu_get_int_status(struct hisi_pmu *sllc_pmu) +{ +	return readl(sllc_pmu->base + SLLC_INT_STATUS); +} + +static void hisi_sllc_pmu_clear_int_status(struct hisi_pmu *sllc_pmu, int idx) +{ +	writel(1 << idx, sllc_pmu->base + SLLC_INT_CLEAR); +} + +static const struct acpi_device_id hisi_sllc_pmu_acpi_match[] = { +	{ "HISI0263", }, +	{} +}; +MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match); + +static int hisi_sllc_pmu_init_data(struct platform_device *pdev, +				   struct hisi_pmu *sllc_pmu) +{ +	/* +	 * Use the SCCL_ID and the index ID to identify the SLLC PMU, +	 * while SCCL_ID is from MPIDR_EL1 by CPU. +	 */ +	if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", +				     &sllc_pmu->sccl_id)) { +		dev_err(&pdev->dev, "Cannot read sccl-id!\n"); +		return -EINVAL; +	} + +	if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", +				     &sllc_pmu->index_id)) { +		dev_err(&pdev->dev, "Cannot read idx-id!\n"); +		return -EINVAL; +	} + +	/* SLLC PMUs only share the same SCCL */ +	sllc_pmu->ccl_id = -1; + +	sllc_pmu->base = devm_platform_ioremap_resource(pdev, 0); +	if (IS_ERR(sllc_pmu->base)) { +		dev_err(&pdev->dev, "ioremap failed for sllc_pmu resource.\n"); +		return PTR_ERR(sllc_pmu->base); +	} + +	sllc_pmu->identifier = readl(sllc_pmu->base + SLLC_VERSION); + +	return 0; +} + +static struct attribute *hisi_sllc_pmu_v2_format_attr[] = { +	HISI_PMU_FORMAT_ATTR(event, "config:0-7"), +	HISI_PMU_FORMAT_ATTR(tgtid_min, "config1:0-10"), +	HISI_PMU_FORMAT_ATTR(tgtid_max, "config1:11-21"), +	HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:22-32"), +	HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:33-43"), +	HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:44"), +	NULL +}; + +static const struct attribute_group hisi_sllc_pmu_v2_format_group = { +	.name = "format", +	.attrs = hisi_sllc_pmu_v2_format_attr, +}; + +static struct attribute *hisi_sllc_pmu_v2_events_attr[] = { +	HISI_PMU_EVENT_ATTR(rx_req,             0x30), +	HISI_PMU_EVENT_ATTR(rx_data,            0x31), +	HISI_PMU_EVENT_ATTR(tx_req,             0x34), +	HISI_PMU_EVENT_ATTR(tx_data,            0x35), +	HISI_PMU_EVENT_ATTR(cycles,             0x09), +	NULL +}; + +static const struct attribute_group hisi_sllc_pmu_v2_events_group = { +	.name = "events", +	.attrs = hisi_sllc_pmu_v2_events_attr, +}; + +static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); + +static struct attribute *hisi_sllc_pmu_cpumask_attrs[] = { +	&dev_attr_cpumask.attr, +	NULL +}; + +static const struct attribute_group hisi_sllc_pmu_cpumask_attr_group = { +	.attrs = hisi_sllc_pmu_cpumask_attrs, +}; + +static struct device_attribute hisi_sllc_pmu_identifier_attr = +	__ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); + +static struct attribute *hisi_sllc_pmu_identifier_attrs[] = { +	&hisi_sllc_pmu_identifier_attr.attr, +	NULL +}; + +static struct attribute_group hisi_sllc_pmu_identifier_group = { +	.attrs = hisi_sllc_pmu_identifier_attrs, +}; + +static const struct attribute_group *hisi_sllc_pmu_v2_attr_groups[] = { +	&hisi_sllc_pmu_v2_format_group, +	&hisi_sllc_pmu_v2_events_group, +	&hisi_sllc_pmu_cpumask_attr_group, +	&hisi_sllc_pmu_identifier_group, +	NULL +}; + +static const struct hisi_uncore_ops hisi_uncore_sllc_ops = { +	.write_evtype		= hisi_sllc_pmu_write_evtype, +	.get_event_idx		= hisi_uncore_pmu_get_event_idx, +	.start_counters		= hisi_sllc_pmu_start_counters, +	.stop_counters		= hisi_sllc_pmu_stop_counters, +	.enable_counter		= hisi_sllc_pmu_enable_counter, +	.disable_counter	= hisi_sllc_pmu_disable_counter, +	.enable_counter_int	= hisi_sllc_pmu_enable_counter_int, +	.disable_counter_int	= hisi_sllc_pmu_disable_counter_int, +	.write_counter		= hisi_sllc_pmu_write_counter, +	.read_counter		= hisi_sllc_pmu_read_counter, +	.get_int_status		= hisi_sllc_pmu_get_int_status, +	.clear_int_status	= hisi_sllc_pmu_clear_int_status, +	.enable_filter		= hisi_sllc_pmu_enable_filter, +	.disable_filter		= hisi_sllc_pmu_clear_filter, +}; + +static int hisi_sllc_pmu_dev_probe(struct platform_device *pdev, +				   struct hisi_pmu *sllc_pmu) +{ +	int ret; + +	ret = hisi_sllc_pmu_init_data(pdev, sllc_pmu); +	if (ret) +		return ret; + +	ret = hisi_uncore_pmu_init_irq(sllc_pmu, pdev); +	if (ret) +		return ret; + +	sllc_pmu->pmu_events.attr_groups = hisi_sllc_pmu_v2_attr_groups; +	sllc_pmu->ops = &hisi_uncore_sllc_ops; +	sllc_pmu->check_event = SLLC_NR_EVENTS; +	sllc_pmu->counter_bits = 64; +	sllc_pmu->num_counters = 8; +	sllc_pmu->dev = &pdev->dev; +	sllc_pmu->on_cpu = -1; + +	return 0; +} + +static int hisi_sllc_pmu_probe(struct platform_device *pdev) +{ +	struct hisi_pmu *sllc_pmu; +	char *name; +	int ret; + +	sllc_pmu = devm_kzalloc(&pdev->dev, sizeof(*sllc_pmu), GFP_KERNEL); +	if (!sllc_pmu) +		return -ENOMEM; + +	ret = hisi_sllc_pmu_dev_probe(pdev, sllc_pmu); +	if (ret) +		return ret; + +	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_sllc%u", +			      sllc_pmu->sccl_id, sllc_pmu->index_id); +	if (!name) +		return -ENOMEM; + +	ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE, +				       &sllc_pmu->node); +	if (ret) { +		dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); +		return ret; +	} + +	sllc_pmu->pmu = (struct pmu) { +		.module		= THIS_MODULE, +		.task_ctx_nr	= perf_invalid_context, +		.event_init	= hisi_uncore_pmu_event_init, +		.pmu_enable	= hisi_uncore_pmu_enable, +		.pmu_disable	= hisi_uncore_pmu_disable, +		.add		= hisi_uncore_pmu_add, +		.del		= hisi_uncore_pmu_del, +		.start		= hisi_uncore_pmu_start, +		.stop		= hisi_uncore_pmu_stop, +		.read		= hisi_uncore_pmu_read, +		.attr_groups    = sllc_pmu->pmu_events.attr_groups, +		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE, +	}; + +	ret = perf_pmu_register(&sllc_pmu->pmu, name, -1); +	if (ret) { +		dev_err(sllc_pmu->dev, "PMU register failed, ret = %d\n", ret); +		cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE, +					    &sllc_pmu->node); +		irq_set_affinity_hint(sllc_pmu->irq, NULL); +		return ret; +	} + +	platform_set_drvdata(pdev, sllc_pmu); + +	return ret; +} + +static int hisi_sllc_pmu_remove(struct platform_device *pdev) +{ +	struct hisi_pmu *sllc_pmu = platform_get_drvdata(pdev); + +	perf_pmu_unregister(&sllc_pmu->pmu); +	cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE, +					    &sllc_pmu->node); +	irq_set_affinity_hint(sllc_pmu->irq, NULL); + +	return 0; +} + +static struct platform_driver hisi_sllc_pmu_driver = { +	.driver = { +		.name = "hisi_sllc_pmu", +		.acpi_match_table = hisi_sllc_pmu_acpi_match, +		.suppress_bind_attrs = true, +	}, +	.probe = hisi_sllc_pmu_probe, +	.remove = hisi_sllc_pmu_remove, +}; + +static int __init hisi_sllc_pmu_module_init(void) +{ +	int ret; + +	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE, +				      "AP_PERF_ARM_HISI_SLLC_ONLINE", +				      hisi_uncore_pmu_online_cpu, +				      hisi_uncore_pmu_offline_cpu); +	if (ret) { +		pr_err("SLLC PMU: cpuhp state setup failed, ret = %d\n", ret); +		return ret; +	} + +	ret = platform_driver_register(&hisi_sllc_pmu_driver); +	if (ret) +		cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE); + +	return ret; +} +module_init(hisi_sllc_pmu_module_init); + +static void __exit hisi_sllc_pmu_module_exit(void) +{ +	platform_driver_unregister(&hisi_sllc_pmu_driver); +	cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE); +} +module_exit(hisi_sllc_pmu_module_exit); + +MODULE_DESCRIPTION("HiSilicon SLLC uncore PMU driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>"); +MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>"); diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c index 8883af955a2a..fc54a80f9c5c 100644 --- a/drivers/perf/qcom_l2_pmu.c +++ b/drivers/perf/qcom_l2_pmu.c @@ -676,7 +676,7 @@ static ssize_t l2cache_pmu_event_show(struct device *dev,  	struct perf_pmu_events_attr *pmu_attr;  	pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); -	return sprintf(page, "event=0x%02llx\n", pmu_attr->id); +	return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id);  }  #define L2CACHE_EVENT_ATTR(_name, _id)					     \ diff --git a/drivers/perf/qcom_l3_pmu.c b/drivers/perf/qcom_l3_pmu.c index fb34b87b9471..bba078077c93 100644 --- a/drivers/perf/qcom_l3_pmu.c +++ b/drivers/perf/qcom_l3_pmu.c @@ -615,7 +615,7 @@ static ssize_t l3cache_pmu_format_show(struct device *dev,  	struct dev_ext_attribute *eattr;  	eattr = container_of(attr, struct dev_ext_attribute, attr); -	return sprintf(buf, "%s\n", (char *) eattr->var); +	return sysfs_emit(buf, "%s\n", (char *) eattr->var);  }  #define L3CACHE_PMU_FORMAT_ATTR(_name, _config)				      \ @@ -643,7 +643,7 @@ static ssize_t l3cache_pmu_event_show(struct device *dev,  	struct perf_pmu_events_attr *pmu_attr;  	pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); -	return sprintf(page, "event=0x%02llx\n", pmu_attr->id); +	return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id);  }  #define L3CACHE_EVENT_ATTR(_name, _id)					     \ diff --git a/drivers/perf/thunderx2_pmu.c b/drivers/perf/thunderx2_pmu.c index e116815fa809..06a6d569b0b5 100644 --- a/drivers/perf/thunderx2_pmu.c +++ b/drivers/perf/thunderx2_pmu.c @@ -128,7 +128,7 @@ __tx2_pmu_##_var##_show(struct device *dev,				\  			       char *page)				\  {									\  	BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE);			\ -	return sprintf(page, _format "\n");				\ +	return sysfs_emit(page, _format "\n");				\  }									\  									\  static struct device_attribute format_attr_##_var =			\ @@ -176,7 +176,7 @@ static ssize_t tx2_pmu_event_show(struct device *dev,  	struct dev_ext_attribute *eattr;  	eattr = container_of(attr, struct dev_ext_attribute, attr); -	return sprintf(buf, "event=0x%lx\n", (unsigned long) eattr->var); +	return sysfs_emit(buf, "event=0x%lx\n", (unsigned long) eattr->var);  }  #define TX2_EVENT_ATTR(name, config) \ diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c index 44faa51ba799..ffe3bdeec845 100644 --- a/drivers/perf/xgene_pmu.c +++ b/drivers/perf/xgene_pmu.c @@ -170,7 +170,7 @@ static ssize_t xgene_pmu_format_show(struct device *dev,  	struct dev_ext_attribute *eattr;  	eattr = container_of(attr, struct dev_ext_attribute, attr); -	return sprintf(buf, "%s\n", (char *) eattr->var); +	return sysfs_emit(buf, "%s\n", (char *) eattr->var);  }  #define XGENE_PMU_FORMAT_ATTR(_name, _config)		\ @@ -281,7 +281,7 @@ static ssize_t xgene_pmu_event_show(struct device *dev,  	struct dev_ext_attribute *eattr;  	eattr = container_of(attr, struct dev_ext_attribute, attr); -	return sprintf(buf, "config=0x%lx\n", (unsigned long) eattr->var); +	return sysfs_emit(buf, "config=0x%lx\n", (unsigned long) eattr->var);  }  #define XGENE_PMU_EVENT_ATTR(_name, _config)		\ | 
