diff mbox

[v2,2/5] arm64/perf: Cavium ThunderX L2C TAD uncore support

Message ID c821d052ffc795b74b520e3cfc393929f0dcbb1e.1457539622.git.jglauber@cavium.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Glauber March 9, 2016, 4:21 p.m. UTC
Support counters of the L2 Cache tag and data units.

Also support pass2 added/modified counters by checking MIDR.

Signed-off-by: Jan Glauber <jglauber@cavium.com>
---
 drivers/perf/uncore/Makefile                |   3 +-
 drivers/perf/uncore/uncore_cavium.c         |   6 +-
 drivers/perf/uncore/uncore_cavium.h         |   7 +-
 drivers/perf/uncore/uncore_cavium_l2c_tad.c | 600 ++++++++++++++++++++++++++++
 4 files changed, 613 insertions(+), 3 deletions(-)
 create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_tad.c

Comments

Mark Rutland April 19, 2016, 3:43 p.m. UTC | #1
On Wed, Mar 09, 2016 at 05:21:04PM +0100, Jan Glauber wrote:
> Support counters of the L2 Cache tag and data units.
> 
> Also support pass2 added/modified counters by checking MIDR.
> 
> Signed-off-by: Jan Glauber <jglauber@cavium.com>
> ---
>  drivers/perf/uncore/Makefile                |   3 +-
>  drivers/perf/uncore/uncore_cavium.c         |   6 +-
>  drivers/perf/uncore/uncore_cavium.h         |   7 +-
>  drivers/perf/uncore/uncore_cavium_l2c_tad.c | 600 ++++++++++++++++++++++++++++
>  4 files changed, 613 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_tad.c
> 
> diff --git a/drivers/perf/uncore/Makefile b/drivers/perf/uncore/Makefile
> index b9c72c2..6a16caf 100644
> --- a/drivers/perf/uncore/Makefile
> +++ b/drivers/perf/uncore/Makefile
> @@ -1 +1,2 @@
> -obj-$(CONFIG_ARCH_THUNDER) += uncore_cavium.o
> +obj-$(CONFIG_ARCH_THUNDER) += uncore_cavium.o		\
> +			      uncore_cavium_l2c_tad.o
> diff --git a/drivers/perf/uncore/uncore_cavium.c b/drivers/perf/uncore/uncore_cavium.c
> index 4fd5e45..b92b2ae 100644
> --- a/drivers/perf/uncore/uncore_cavium.c
> +++ b/drivers/perf/uncore/uncore_cavium.c
> @@ -15,7 +15,10 @@ int thunder_uncore_version;
>  
>  struct thunder_uncore *event_to_thunder_uncore(struct perf_event *event)
>  {
> -	return NULL;
> +	if (event->pmu->type == thunder_l2c_tad_pmu.type)
> +		return thunder_uncore_l2c_tad;
> +	else
> +		return NULL;
>  }

If thunder_uncore contained the relevant struct pmu, you wouldn't need
this function.

You could take event->pmu, and use container_of to get the relevant
thunder_uncore.

So please do that and get rid of this function.

>  
>  void thunder_uncore_read(struct perf_event *event)
> @@ -296,6 +299,7 @@ static int __init thunder_uncore_init(void)
>  		thunder_uncore_version = 1;
>  	pr_info("PMU version: %d\n", thunder_uncore_version);
>  
> +	thunder_uncore_l2c_tad_setup();
>  	return 0;
>  }
>  late_initcall(thunder_uncore_init);
> diff --git a/drivers/perf/uncore/uncore_cavium.h b/drivers/perf/uncore/uncore_cavium.h
> index c799709..7a9c367 100644
> --- a/drivers/perf/uncore/uncore_cavium.h
> +++ b/drivers/perf/uncore/uncore_cavium.h
> @@ -7,7 +7,7 @@
>  #define pr_fmt(fmt)     "thunderx_uncore: " fmt
>  
>  enum uncore_type {
> -	NOP_TYPE,
> +	L2C_TAD_TYPE,
>  };
>  
>  extern int thunder_uncore_version;
> @@ -65,6 +65,9 @@ static inline struct thunder_uncore_node *get_node(u64 config,
>  extern struct attribute_group thunder_uncore_attr_group;
>  extern struct device_attribute format_attr_node;
>  
> +extern struct thunder_uncore *thunder_uncore_l2c_tad;
> +extern struct pmu thunder_l2c_tad_pmu;

The above hopefully means you can get rid of these.

>  /* Prototypes */
>  struct thunder_uncore *event_to_thunder_uncore(struct perf_event *event);
>  void thunder_uncore_del(struct perf_event *event, int flags);
> @@ -76,3 +79,5 @@ int thunder_uncore_setup(struct thunder_uncore *uncore, int id,
>  ssize_t thunder_events_sysfs_show(struct device *dev,
>  				  struct device_attribute *attr,
>  				  char *page);
> +
> +int thunder_uncore_l2c_tad_setup(void);
> diff --git a/drivers/perf/uncore/uncore_cavium_l2c_tad.c b/drivers/perf/uncore/uncore_cavium_l2c_tad.c
> new file mode 100644
> index 0000000..c8dc305
> --- /dev/null
> +++ b/drivers/perf/uncore/uncore_cavium_l2c_tad.c
> @@ -0,0 +1,600 @@
> +/*
> + * Cavium Thunder uncore PMU support, L2C TAD counters.

It would be good to put an explaination of the TAD unit here, even if
just expanding that to Tag And Data.

> + *
> + * Copyright 2016 Cavium Inc.
> + * Author: Jan Glauber <jan.glauber@cavium.com>
> + */
> +
> +#include <linux/slab.h>
> +#include <linux/perf_event.h>

Minor nit, but as a general note I'd recommend alphabetically sorting
your includes now. 

That way any subsequent additions/removals are less likely to cause
painful conflicts (so long as they retain that order).

> +static void thunder_uncore_start(struct perf_event *event, int flags)
> +{
> +	struct thunder_uncore *uncore = event_to_thunder_uncore(event);
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct thunder_uncore_node *node;
> +	struct thunder_uncore_unit *unit;
> +	u64 prev;
> +	int id;
> +
> +	node = get_node(hwc->config, uncore);
> +	id = get_id(hwc->config);
> +
> +	/* restore counter value divided by units into all counters */
> +	if (flags & PERF_EF_RELOAD) {
> +		prev = local64_read(&hwc->prev_count);
> +		prev = prev / node->nr_units;
> +
> +		list_for_each_entry(unit, &node->unit_list, entry)
> +			writeq(prev, hwc->event_base + unit->map);
> +	}

It would be vastly simpler to always restore zero into all counters, and
to update prev_count to account for this.

That will also save you any rounding loss from the division.

> +
> +	hwc->state = 0;
> +
> +	/* write byte in control registers for all units on the node */
> +	list_for_each_entry(unit, &node->unit_list, entry)
> +		writeb(id, hwc->config_base + unit->map);

That comment isn't very helpful. What is the intent and effect of this
write?

> +
> +	perf_event_update_userpage(event);
> +}
> +
> +static void thunder_uncore_stop(struct perf_event *event, int flags)
> +{
> +	struct thunder_uncore *uncore = event_to_thunder_uncore(event);
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct thunder_uncore_node *node;
> +	struct thunder_uncore_unit *unit;
> +
> +	/* reset selection value for all units on the node */
> +	node = get_node(hwc->config, uncore);
> +
> +	list_for_each_entry(unit, &node->unit_list, entry)
> +		writeb(L2C_TAD_EVENTS_DISABLED, hwc->config_base + unit->map);
> +	hwc->state |= PERF_HES_STOPPED;
> +
> +	if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
> +		thunder_uncore_read(event);
> +		hwc->state |= PERF_HES_UPTODATE;
> +	}
> +}
> +
> +static int thunder_uncore_add(struct perf_event *event, int flags)
> +{
> +	struct thunder_uncore *uncore = event_to_thunder_uncore(event);
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct thunder_uncore_node *node;
> +	int i;
> +
> +	WARN_ON_ONCE(!uncore);

This is trivially never possible if uncore contains the pmu (or we
couldn't have initialised the event in the first place).

> +	node = get_node(hwc->config, uncore);
> +
> +	/* are we already assigned? */
> +	if (hwc->idx != -1 && node->events[hwc->idx] == event)
> +		goto out;

Why would the event already be assigned a particular counter?

Which other piece of code might do that?

As far as I can see, nothing else can.

> +
> +	for (i = 0; i < node->num_counters; i++) {
> +		if (node->events[i] == event) {
> +			hwc->idx = i;
> +			goto out;
> +		}
> +	}

This should never happen, in the absence of a programming error. An
event should not be added multiple times, and adds and dels should be
balanced.

> +
> +	/* if not take the first available counter */
> +	hwc->idx = -1;
> +	for (i = 0; i < node->num_counters; i++) {
> +		if (cmpxchg(&node->events[i], NULL, event) == NULL) {
> +			hwc->idx = i;
> +			break;
> +		}
> +	}
> +out:
> +	if (hwc->idx == -1)
> +		return -EBUSY;
> +
> +	hwc->config_base = hwc->idx;
> +	hwc->event_base = L2C_TAD_COUNTER_OFFSET +
> +			  hwc->idx * sizeof(unsigned long long);

What's going on here?

I see that we write use hwc->event_base as an offset into registers in
the HW, so a sizeof unsigned long long is unusual.

I'm guessing that you're figuring out the address of a 64 bit register.
A comment, and sizeof(u64) would be better.

> +EVENT_ATTR(l2t_hit,	L2C_TAD_EVENT_L2T_HIT);
> +EVENT_ATTR(l2t_miss,	L2C_TAD_EVENT_L2T_MISS);
> +EVENT_ATTR(l2t_noalloc,	L2C_TAD_EVENT_L2T_NOALLOC);
> +EVENT_ATTR(l2_vic,	L2C_TAD_EVENT_L2_VIC);
> +EVENT_ATTR(sc_fail,	L2C_TAD_EVENT_SC_FAIL);
> +EVENT_ATTR(sc_pass,	L2C_TAD_EVENT_SC_PASS);
> +EVENT_ATTR(lfb_occ,	L2C_TAD_EVENT_LFB_OCC);
> +EVENT_ATTR(wait_lfb,	L2C_TAD_EVENT_WAIT_LFB);
> +EVENT_ATTR(wait_vab,	L2C_TAD_EVENT_WAIT_VAB);
> +EVENT_ATTR(rtg_hit,	L2C_TAD_EVENT_RTG_HIT);
> +EVENT_ATTR(rtg_miss,	L2C_TAD_EVENT_RTG_MISS);
> +EVENT_ATTR(l2_rtg_vic,	L2C_TAD_EVENT_L2_RTG_VIC);
> +EVENT_ATTR(l2_open_oci,	L2C_TAD_EVENT_L2_OPEN_OCI);

> +static struct attribute *thunder_l2c_tad_events_attr[] = {
> +	EVENT_PTR(l2t_hit),
> +	EVENT_PTR(l2t_miss),
> +	EVENT_PTR(l2t_noalloc),
> +	EVENT_PTR(l2_vic),
> +	EVENT_PTR(sc_fail),
> +	EVENT_PTR(sc_pass),
> +	EVENT_PTR(lfb_occ),
> +	EVENT_PTR(wait_lfb),
> +	EVENT_PTR(wait_vab),
> +	EVENT_PTR(rtg_hit),
> +	EVENT_PTR(rtg_miss),
> +	EVENT_PTR(l2_rtg_vic),
> +	EVENT_PTR(l2_open_oci),

This duplication is tedious.

Please do something like we did for CCI in commit 5e442eba342e567e
("arm-cci: simplify sysfs attr handling") so you only need to define
each attribute once to create it and place it in the relevant attribute
pointer list.

Likewise for the other PMUs.

> +static struct attribute_group thunder_l2c_tad_events_group = {
> +	.name = "events",
> +	.attrs = NULL,
> +};
> +
> +static const struct attribute_group *thunder_l2c_tad_attr_groups[] = {
> +	&thunder_uncore_attr_group,
> +	&thunder_l2c_tad_format_group,
> +	&thunder_l2c_tad_events_group,
> +	NULL,
> +};
> +
> +struct pmu thunder_l2c_tad_pmu = {
> +	.attr_groups	= thunder_l2c_tad_attr_groups,
> +	.name		= "thunder_l2c_tad",
> +	.event_init	= thunder_uncore_event_init,
> +	.add		= thunder_uncore_add,
> +	.del		= thunder_uncore_del,
> +	.start		= thunder_uncore_start,
> +	.stop		= thunder_uncore_stop,
> +	.read		= thunder_uncore_read,
> +};
> +
> +static int event_valid(u64 config)

A bool would be clearer.

> +{
> +	if ((config > 0 && config <= L2C_TAD_EVENT_WAIT_VAB) ||
> +	    config == L2C_TAD_EVENT_RTG_HIT ||
> +	    config == L2C_TAD_EVENT_RTG_MISS ||
> +	    config == L2C_TAD_EVENT_L2_RTG_VIC ||
> +	    config == L2C_TAD_EVENT_L2_OPEN_OCI ||
> +	    ((config & 0x80) && ((config & 0xf) <= 3)))

What are these last cases?

> +		return 1;
> +
> +	if (thunder_uncore_version == 1)
> +		if (config == L2C_TAD_EVENT_OPEN_CCPI ||
> +		    (config >= L2C_TAD_EVENT_LOOKUP &&
> +		     config <= L2C_TAD_EVENT_LOOKUP_ALL) ||
> +		    (config >= L2C_TAD_EVENT_TAG_ALC_HIT &&
> +		     config <= L2C_TAD_EVENT_OCI_RTG_ALC_VIC &&
> +		     config != 0x4d &&
> +		     config != 0x66 &&
> +		     config != 0x67))

Likewise, what are these last cases?

Why not rule these out explicitly first?

> +			return 1;
> +
> +	return 0;
> +}
> +
> +int __init thunder_uncore_l2c_tad_setup(void)
> +{
> +	int ret = -ENOMEM;
> +
> +	thunder_uncore_l2c_tad = kzalloc(sizeof(struct thunder_uncore),
> +					 GFP_KERNEL);

As previously, sizeof(*ptr) is preferred to sizeof(type), though it
doesn't save you anything here.

> +	if (!thunder_uncore_l2c_tad)
> +		goto fail_nomem;
> +
> +	if (thunder_uncore_version == 0)
> +		thunder_l2c_tad_events_group.attrs = thunder_l2c_tad_events_attr;
> +	else /* default */
> +		thunder_l2c_tad_events_group.attrs = thunder_l2c_tad_pass2_events_attr;
> +
> +	ret = thunder_uncore_setup(thunder_uncore_l2c_tad,
> +			   PCI_DEVICE_ID_THUNDER_L2C_TAD,
> +			   L2C_TAD_CONTROL_OFFSET,
> +			   L2C_TAD_COUNTER_OFFSET + L2C_TAD_NR_COUNTERS
> +				* sizeof(unsigned long long),

It would be nicer to calculate the size earlier (with sizeof(u64) as
previously mentioned).

> +			   &thunder_l2c_tad_pmu,
> +			   L2C_TAD_NR_COUNTERS);
> +	if (ret)
> +		goto fail;
> +
> +	thunder_uncore_l2c_tad->type = L2C_TAD_TYPE;

I believe this can go, with thunder_uncore containing a pmu.

Thanks,
Mark.
diff mbox

Patch

diff --git a/drivers/perf/uncore/Makefile b/drivers/perf/uncore/Makefile
index b9c72c2..6a16caf 100644
--- a/drivers/perf/uncore/Makefile
+++ b/drivers/perf/uncore/Makefile
@@ -1 +1,2 @@ 
-obj-$(CONFIG_ARCH_THUNDER) += uncore_cavium.o
+obj-$(CONFIG_ARCH_THUNDER) += uncore_cavium.o		\
+			      uncore_cavium_l2c_tad.o
diff --git a/drivers/perf/uncore/uncore_cavium.c b/drivers/perf/uncore/uncore_cavium.c
index 4fd5e45..b92b2ae 100644
--- a/drivers/perf/uncore/uncore_cavium.c
+++ b/drivers/perf/uncore/uncore_cavium.c
@@ -15,7 +15,10 @@  int thunder_uncore_version;
 
 struct thunder_uncore *event_to_thunder_uncore(struct perf_event *event)
 {
-	return NULL;
+	if (event->pmu->type == thunder_l2c_tad_pmu.type)
+		return thunder_uncore_l2c_tad;
+	else
+		return NULL;
 }
 
 void thunder_uncore_read(struct perf_event *event)
@@ -296,6 +299,7 @@  static int __init thunder_uncore_init(void)
 		thunder_uncore_version = 1;
 	pr_info("PMU version: %d\n", thunder_uncore_version);
 
+	thunder_uncore_l2c_tad_setup();
 	return 0;
 }
 late_initcall(thunder_uncore_init);
diff --git a/drivers/perf/uncore/uncore_cavium.h b/drivers/perf/uncore/uncore_cavium.h
index c799709..7a9c367 100644
--- a/drivers/perf/uncore/uncore_cavium.h
+++ b/drivers/perf/uncore/uncore_cavium.h
@@ -7,7 +7,7 @@ 
 #define pr_fmt(fmt)     "thunderx_uncore: " fmt
 
 enum uncore_type {
-	NOP_TYPE,
+	L2C_TAD_TYPE,
 };
 
 extern int thunder_uncore_version;
@@ -65,6 +65,9 @@  static inline struct thunder_uncore_node *get_node(u64 config,
 extern struct attribute_group thunder_uncore_attr_group;
 extern struct device_attribute format_attr_node;
 
+extern struct thunder_uncore *thunder_uncore_l2c_tad;
+extern struct pmu thunder_l2c_tad_pmu;
+
 /* Prototypes */
 struct thunder_uncore *event_to_thunder_uncore(struct perf_event *event);
 void thunder_uncore_del(struct perf_event *event, int flags);
@@ -76,3 +79,5 @@  int thunder_uncore_setup(struct thunder_uncore *uncore, int id,
 ssize_t thunder_events_sysfs_show(struct device *dev,
 				  struct device_attribute *attr,
 				  char *page);
+
+int thunder_uncore_l2c_tad_setup(void);
diff --git a/drivers/perf/uncore/uncore_cavium_l2c_tad.c b/drivers/perf/uncore/uncore_cavium_l2c_tad.c
new file mode 100644
index 0000000..c8dc305
--- /dev/null
+++ b/drivers/perf/uncore/uncore_cavium_l2c_tad.c
@@ -0,0 +1,600 @@ 
+/*
+ * Cavium Thunder uncore PMU support, L2C TAD counters.
+ *
+ * Copyright 2016 Cavium Inc.
+ * Author: Jan Glauber <jan.glauber@cavium.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/perf_event.h>
+
+#include "uncore_cavium.h"
+
+#ifndef PCI_DEVICE_ID_THUNDER_L2C_TAD
+#define PCI_DEVICE_ID_THUNDER_L2C_TAD	0xa02e
+#endif
+
+#define L2C_TAD_NR_COUNTERS             4
+#define L2C_TAD_CONTROL_OFFSET		0x10000
+#define L2C_TAD_COUNTER_OFFSET		0x100
+
+/* L2C TAD event list */
+#define L2C_TAD_EVENTS_DISABLED		0x00
+
+#define L2C_TAD_EVENT_L2T_HIT		0x01
+#define L2C_TAD_EVENT_L2T_MISS		0x02
+#define L2C_TAD_EVENT_L2T_NOALLOC	0x03
+#define L2C_TAD_EVENT_L2_VIC		0x04
+#define L2C_TAD_EVENT_SC_FAIL		0x05
+#define L2C_TAD_EVENT_SC_PASS		0x06
+#define L2C_TAD_EVENT_LFB_OCC		0x07
+#define L2C_TAD_EVENT_WAIT_LFB		0x08
+#define L2C_TAD_EVENT_WAIT_VAB		0x09
+
+#define L2C_TAD_EVENT_RTG_HIT		0x41
+#define L2C_TAD_EVENT_RTG_MISS		0x42
+#define L2C_TAD_EVENT_L2_RTG_VIC	0x44
+#define L2C_TAD_EVENT_L2_OPEN_OCI	0x48
+
+#define L2C_TAD_EVENT_QD0_IDX		0x80
+#define L2C_TAD_EVENT_QD0_RDAT		0x81
+#define L2C_TAD_EVENT_QD0_BNKS		0x82
+#define L2C_TAD_EVENT_QD0_WDAT		0x83
+
+#define L2C_TAD_EVENT_QD1_IDX		0x90
+#define L2C_TAD_EVENT_QD1_RDAT		0x91
+#define L2C_TAD_EVENT_QD1_BNKS		0x92
+#define L2C_TAD_EVENT_QD1_WDAT		0x93
+
+#define L2C_TAD_EVENT_QD2_IDX		0xa0
+#define L2C_TAD_EVENT_QD2_RDAT		0xa1
+#define L2C_TAD_EVENT_QD2_BNKS		0xa2
+#define L2C_TAD_EVENT_QD2_WDAT		0xa3
+
+#define L2C_TAD_EVENT_QD3_IDX		0xb0
+#define L2C_TAD_EVENT_QD3_RDAT		0xb1
+#define L2C_TAD_EVENT_QD3_BNKS		0xb2
+#define L2C_TAD_EVENT_QD3_WDAT		0xb3
+
+#define L2C_TAD_EVENT_QD4_IDX		0xc0
+#define L2C_TAD_EVENT_QD4_RDAT		0xc1
+#define L2C_TAD_EVENT_QD4_BNKS		0xc2
+#define L2C_TAD_EVENT_QD4_WDAT		0xc3
+
+#define L2C_TAD_EVENT_QD5_IDX		0xd0
+#define L2C_TAD_EVENT_QD5_RDAT		0xd1
+#define L2C_TAD_EVENT_QD5_BNKS		0xd2
+#define L2C_TAD_EVENT_QD5_WDAT		0xd3
+
+#define L2C_TAD_EVENT_QD6_IDX		0xe0
+#define L2C_TAD_EVENT_QD6_RDAT		0xe1
+#define L2C_TAD_EVENT_QD6_BNKS		0xe2
+#define L2C_TAD_EVENT_QD6_WDAT		0xe3
+
+#define L2C_TAD_EVENT_QD7_IDX		0xf0
+#define L2C_TAD_EVENT_QD7_RDAT		0xf1
+#define L2C_TAD_EVENT_QD7_BNKS		0xf2
+#define L2C_TAD_EVENT_QD7_WDAT		0xf3
+
+/* pass2 added/changed event list */
+#define L2C_TAD_EVENT_OPEN_CCPI			0x0a
+#define L2C_TAD_EVENT_LOOKUP			0x40
+#define L2C_TAD_EVENT_LOOKUP_XMC_LCL		0x41
+#define L2C_TAD_EVENT_LOOKUP_XMC_RMT		0x42
+#define L2C_TAD_EVENT_LOOKUP_MIB		0x43
+#define L2C_TAD_EVENT_LOOKUP_ALL		0x44
+#define L2C_TAD_EVENT_TAG_ALC_HIT		0x48
+#define L2C_TAD_EVENT_TAG_ALC_MISS		0x49
+#define L2C_TAD_EVENT_TAG_ALC_NALC		0x4a
+#define L2C_TAD_EVENT_TAG_NALC_HIT		0x4b
+#define L2C_TAD_EVENT_TAG_NALC_MISS		0x4c
+#define L2C_TAD_EVENT_LMC_WR			0x4e
+#define L2C_TAD_EVENT_LMC_SBLKDTY		0x4f
+#define L2C_TAD_EVENT_TAG_ALC_RTG_HIT		0x50
+#define L2C_TAD_EVENT_TAG_ALC_RTG_HITE		0x51
+#define L2C_TAD_EVENT_TAG_ALC_RTG_HITS		0x52
+#define L2C_TAD_EVENT_TAG_ALC_RTG_MISS		0x53
+#define L2C_TAD_EVENT_TAG_NALC_RTG_HIT		0x54
+#define L2C_TAD_EVENT_TAG_NALC_RTG_MISS		0x55
+#define L2C_TAD_EVENT_TAG_NALC_RTG_HITE		0x56
+#define L2C_TAD_EVENT_TAG_NALC_RTG_HITS		0x57
+#define L2C_TAD_EVENT_TAG_ALC_LCL_EVICT		0x58
+#define L2C_TAD_EVENT_TAG_ALC_LCL_CLNVIC	0x59
+#define L2C_TAD_EVENT_TAG_ALC_LCL_DTYVIC	0x5a
+#define L2C_TAD_EVENT_TAG_ALC_RMT_EVICT		0x5b
+#define L2C_TAD_EVENT_TAG_ALC_RMT_VIC		0x5c
+#define L2C_TAD_EVENT_RTG_ALC			0x5d
+#define L2C_TAD_EVENT_RTG_ALC_HIT		0x5e
+#define L2C_TAD_EVENT_RTG_ALC_HITWB		0x5f
+#define L2C_TAD_EVENT_STC_TOTAL			0x60
+#define L2C_TAD_EVENT_STC_TOTAL_FAIL		0x61
+#define L2C_TAD_EVENT_STC_RMT			0x62
+#define L2C_TAD_EVENT_STC_RMT_FAIL		0x63
+#define L2C_TAD_EVENT_STC_LCL			0x64
+#define L2C_TAD_EVENT_STC_LCL_FAIL		0x65
+#define L2C_TAD_EVENT_OCI_RTG_WAIT		0x68
+#define L2C_TAD_EVENT_OCI_FWD_CYC_HIT		0x69
+#define L2C_TAD_EVENT_OCI_FWD_RACE		0x6a
+#define L2C_TAD_EVENT_OCI_HAKS			0x6b
+#define L2C_TAD_EVENT_OCI_FLDX_TAG_E_NODAT	0x6c
+#define L2C_TAD_EVENT_OCI_FLDX_TAG_E_DAT	0x6d
+#define L2C_TAD_EVENT_OCI_RLDD			0x6e
+#define L2C_TAD_EVENT_OCI_RLDD_PEMD		0x6f
+#define L2C_TAD_EVENT_OCI_RRQ_DAT_CNT		0x70
+#define L2C_TAD_EVENT_OCI_RRQ_DAT_DMASK		0x71
+#define L2C_TAD_EVENT_OCI_RSP_DAT_CNT		0x72
+#define L2C_TAD_EVENT_OCI_RSP_DAT_DMASK		0x73
+#define L2C_TAD_EVENT_OCI_RSP_DAT_VICD_CNT	0x74
+#define L2C_TAD_EVENT_OCI_RSP_DAT_VICD_DMASK	0x75
+#define L2C_TAD_EVENT_OCI_RTG_ALC_EVICT		0x76
+#define L2C_TAD_EVENT_OCI_RTG_ALC_VIC		0x77
+
+struct thunder_uncore *thunder_uncore_l2c_tad;
+
+static void thunder_uncore_start(struct perf_event *event, int flags)
+{
+	struct thunder_uncore *uncore = event_to_thunder_uncore(event);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore_unit *unit;
+	u64 prev;
+	int id;
+
+	node = get_node(hwc->config, uncore);
+	id = get_id(hwc->config);
+
+	/* restore counter value divided by units into all counters */
+	if (flags & PERF_EF_RELOAD) {
+		prev = local64_read(&hwc->prev_count);
+		prev = prev / node->nr_units;
+
+		list_for_each_entry(unit, &node->unit_list, entry)
+			writeq(prev, hwc->event_base + unit->map);
+	}
+
+	hwc->state = 0;
+
+	/* write byte in control registers for all units on the node */
+	list_for_each_entry(unit, &node->unit_list, entry)
+		writeb(id, hwc->config_base + unit->map);
+
+	perf_event_update_userpage(event);
+}
+
+static void thunder_uncore_stop(struct perf_event *event, int flags)
+{
+	struct thunder_uncore *uncore = event_to_thunder_uncore(event);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	struct thunder_uncore_unit *unit;
+
+	/* reset selection value for all units on the node */
+	node = get_node(hwc->config, uncore);
+
+	list_for_each_entry(unit, &node->unit_list, entry)
+		writeb(L2C_TAD_EVENTS_DISABLED, hwc->config_base + unit->map);
+	hwc->state |= PERF_HES_STOPPED;
+
+	if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+		thunder_uncore_read(event);
+		hwc->state |= PERF_HES_UPTODATE;
+	}
+}
+
+static int thunder_uncore_add(struct perf_event *event, int flags)
+{
+	struct thunder_uncore *uncore = event_to_thunder_uncore(event);
+	struct hw_perf_event *hwc = &event->hw;
+	struct thunder_uncore_node *node;
+	int i;
+
+	WARN_ON_ONCE(!uncore);
+	node = get_node(hwc->config, uncore);
+
+	/* are we already assigned? */
+	if (hwc->idx != -1 && node->events[hwc->idx] == event)
+		goto out;
+
+	for (i = 0; i < node->num_counters; i++) {
+		if (node->events[i] == event) {
+			hwc->idx = i;
+			goto out;
+		}
+	}
+
+	/* if not take the first available counter */
+	hwc->idx = -1;
+	for (i = 0; i < node->num_counters; i++) {
+		if (cmpxchg(&node->events[i], NULL, event) == NULL) {
+			hwc->idx = i;
+			break;
+		}
+	}
+out:
+	if (hwc->idx == -1)
+		return -EBUSY;
+
+	hwc->config_base = hwc->idx;
+	hwc->event_base = L2C_TAD_COUNTER_OFFSET +
+			  hwc->idx * sizeof(unsigned long long);
+	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+	if (flags & PERF_EF_START)
+		thunder_uncore_start(event, PERF_EF_RELOAD);
+	return 0;
+}
+
+PMU_FORMAT_ATTR(event, "config:0-7");
+
+static struct attribute *thunder_l2c_tad_format_attr[] = {
+	&format_attr_event.attr,
+	&format_attr_node.attr,
+	NULL,
+};
+
+static struct attribute_group thunder_l2c_tad_format_group = {
+	.name = "format",
+	.attrs = thunder_l2c_tad_format_attr,
+};
+
+EVENT_ATTR(l2t_hit,	L2C_TAD_EVENT_L2T_HIT);
+EVENT_ATTR(l2t_miss,	L2C_TAD_EVENT_L2T_MISS);
+EVENT_ATTR(l2t_noalloc,	L2C_TAD_EVENT_L2T_NOALLOC);
+EVENT_ATTR(l2_vic,	L2C_TAD_EVENT_L2_VIC);
+EVENT_ATTR(sc_fail,	L2C_TAD_EVENT_SC_FAIL);
+EVENT_ATTR(sc_pass,	L2C_TAD_EVENT_SC_PASS);
+EVENT_ATTR(lfb_occ,	L2C_TAD_EVENT_LFB_OCC);
+EVENT_ATTR(wait_lfb,	L2C_TAD_EVENT_WAIT_LFB);
+EVENT_ATTR(wait_vab,	L2C_TAD_EVENT_WAIT_VAB);
+EVENT_ATTR(rtg_hit,	L2C_TAD_EVENT_RTG_HIT);
+EVENT_ATTR(rtg_miss,	L2C_TAD_EVENT_RTG_MISS);
+EVENT_ATTR(l2_rtg_vic,	L2C_TAD_EVENT_L2_RTG_VIC);
+EVENT_ATTR(l2_open_oci,	L2C_TAD_EVENT_L2_OPEN_OCI);
+
+EVENT_ATTR(qd0_idx,	L2C_TAD_EVENT_QD0_IDX);
+EVENT_ATTR(qd0_rdat,	L2C_TAD_EVENT_QD0_RDAT);
+EVENT_ATTR(qd0_bnks,	L2C_TAD_EVENT_QD0_BNKS);
+EVENT_ATTR(qd0_wdat,	L2C_TAD_EVENT_QD0_WDAT);
+
+EVENT_ATTR(qd1_idx,	L2C_TAD_EVENT_QD1_IDX);
+EVENT_ATTR(qd1_rdat,	L2C_TAD_EVENT_QD1_RDAT);
+EVENT_ATTR(qd1_bnks,	L2C_TAD_EVENT_QD1_BNKS);
+EVENT_ATTR(qd1_wdat,	L2C_TAD_EVENT_QD1_WDAT);
+
+EVENT_ATTR(qd2_idx,	L2C_TAD_EVENT_QD2_IDX);
+EVENT_ATTR(qd2_rdat,	L2C_TAD_EVENT_QD2_RDAT);
+EVENT_ATTR(qd2_bnks,	L2C_TAD_EVENT_QD2_BNKS);
+EVENT_ATTR(qd2_wdat,	L2C_TAD_EVENT_QD2_WDAT);
+
+EVENT_ATTR(qd3_idx,	L2C_TAD_EVENT_QD3_IDX);
+EVENT_ATTR(qd3_rdat,	L2C_TAD_EVENT_QD3_RDAT);
+EVENT_ATTR(qd3_bnks,	L2C_TAD_EVENT_QD3_BNKS);
+EVENT_ATTR(qd3_wdat,	L2C_TAD_EVENT_QD3_WDAT);
+
+EVENT_ATTR(qd4_idx,	L2C_TAD_EVENT_QD4_IDX);
+EVENT_ATTR(qd4_rdat,	L2C_TAD_EVENT_QD4_RDAT);
+EVENT_ATTR(qd4_bnks,	L2C_TAD_EVENT_QD4_BNKS);
+EVENT_ATTR(qd4_wdat,	L2C_TAD_EVENT_QD4_WDAT);
+
+EVENT_ATTR(qd5_idx,	L2C_TAD_EVENT_QD5_IDX);
+EVENT_ATTR(qd5_rdat,	L2C_TAD_EVENT_QD5_RDAT);
+EVENT_ATTR(qd5_bnks,	L2C_TAD_EVENT_QD5_BNKS);
+EVENT_ATTR(qd5_wdat,	L2C_TAD_EVENT_QD5_WDAT);
+
+EVENT_ATTR(qd6_idx,	L2C_TAD_EVENT_QD6_IDX);
+EVENT_ATTR(qd6_rdat,	L2C_TAD_EVENT_QD6_RDAT);
+EVENT_ATTR(qd6_bnks,	L2C_TAD_EVENT_QD6_BNKS);
+EVENT_ATTR(qd6_wdat,	L2C_TAD_EVENT_QD6_WDAT);
+
+EVENT_ATTR(qd7_idx,	L2C_TAD_EVENT_QD7_IDX);
+EVENT_ATTR(qd7_rdat,	L2C_TAD_EVENT_QD7_RDAT);
+EVENT_ATTR(qd7_bnks,	L2C_TAD_EVENT_QD7_BNKS);
+EVENT_ATTR(qd7_wdat,	L2C_TAD_EVENT_QD7_WDAT);
+
+static struct attribute *thunder_l2c_tad_events_attr[] = {
+	EVENT_PTR(l2t_hit),
+	EVENT_PTR(l2t_miss),
+	EVENT_PTR(l2t_noalloc),
+	EVENT_PTR(l2_vic),
+	EVENT_PTR(sc_fail),
+	EVENT_PTR(sc_pass),
+	EVENT_PTR(lfb_occ),
+	EVENT_PTR(wait_lfb),
+	EVENT_PTR(wait_vab),
+	EVENT_PTR(rtg_hit),
+	EVENT_PTR(rtg_miss),
+	EVENT_PTR(l2_rtg_vic),
+	EVENT_PTR(l2_open_oci),
+
+	EVENT_PTR(qd0_idx),
+	EVENT_PTR(qd0_rdat),
+	EVENT_PTR(qd0_bnks),
+	EVENT_PTR(qd0_wdat),
+
+	EVENT_PTR(qd1_idx),
+	EVENT_PTR(qd1_rdat),
+	EVENT_PTR(qd1_bnks),
+	EVENT_PTR(qd1_wdat),
+
+	EVENT_PTR(qd2_idx),
+	EVENT_PTR(qd2_rdat),
+	EVENT_PTR(qd2_bnks),
+	EVENT_PTR(qd2_wdat),
+
+	EVENT_PTR(qd3_idx),
+	EVENT_PTR(qd3_rdat),
+	EVENT_PTR(qd3_bnks),
+	EVENT_PTR(qd3_wdat),
+
+	EVENT_PTR(qd4_idx),
+	EVENT_PTR(qd4_rdat),
+	EVENT_PTR(qd4_bnks),
+	EVENT_PTR(qd4_wdat),
+
+	EVENT_PTR(qd5_idx),
+	EVENT_PTR(qd5_rdat),
+	EVENT_PTR(qd5_bnks),
+	EVENT_PTR(qd5_wdat),
+
+	EVENT_PTR(qd6_idx),
+	EVENT_PTR(qd6_rdat),
+	EVENT_PTR(qd6_bnks),
+	EVENT_PTR(qd6_wdat),
+
+	EVENT_PTR(qd7_idx),
+	EVENT_PTR(qd7_rdat),
+	EVENT_PTR(qd7_bnks),
+	EVENT_PTR(qd7_wdat),
+	NULL,
+};
+
+/* pass2 added/chanegd events */
+EVENT_ATTR(open_ccpi,		L2C_TAD_EVENT_OPEN_CCPI);
+EVENT_ATTR(lookup,		L2C_TAD_EVENT_LOOKUP);
+EVENT_ATTR(lookup_xmc_lcl,	L2C_TAD_EVENT_LOOKUP_XMC_LCL);
+EVENT_ATTR(lookup_xmc_rmt,	L2C_TAD_EVENT_LOOKUP_XMC_RMT);
+EVENT_ATTR(lookup_mib,		L2C_TAD_EVENT_LOOKUP_MIB);
+EVENT_ATTR(lookup_all,		L2C_TAD_EVENT_LOOKUP_ALL);
+
+EVENT_ATTR(tag_alc_hit,		L2C_TAD_EVENT_TAG_ALC_HIT);
+EVENT_ATTR(tag_alc_miss,	L2C_TAD_EVENT_TAG_ALC_MISS);
+EVENT_ATTR(tag_alc_nalc,	L2C_TAD_EVENT_TAG_ALC_NALC);
+EVENT_ATTR(tag_nalc_hit,	L2C_TAD_EVENT_TAG_NALC_HIT);
+EVENT_ATTR(tag_nalc_miss,	L2C_TAD_EVENT_TAG_NALC_MISS);
+
+EVENT_ATTR(lmc_wr,		L2C_TAD_EVENT_LMC_WR);
+EVENT_ATTR(lmc_sblkdty,		L2C_TAD_EVENT_LMC_SBLKDTY);
+
+EVENT_ATTR(tag_alc_rtg_hit,	L2C_TAD_EVENT_TAG_ALC_RTG_HIT);
+EVENT_ATTR(tag_alc_rtg_hite,	L2C_TAD_EVENT_TAG_ALC_RTG_HITE);
+EVENT_ATTR(tag_alc_rtg_hits,	L2C_TAD_EVENT_TAG_ALC_RTG_HITS);
+EVENT_ATTR(tag_alc_rtg_miss,	L2C_TAD_EVENT_TAG_ALC_RTG_MISS);
+EVENT_ATTR(tag_alc_nalc_rtg_hit, L2C_TAD_EVENT_TAG_NALC_RTG_HIT);
+EVENT_ATTR(tag_nalc_rtg_miss,	L2C_TAD_EVENT_TAG_NALC_RTG_MISS);
+EVENT_ATTR(tag_nalc_rtg_hite,	L2C_TAD_EVENT_TAG_NALC_RTG_HITE);
+EVENT_ATTR(tag_nalc_rtg_hits,	L2C_TAD_EVENT_TAG_NALC_RTG_HITS);
+EVENT_ATTR(tag_alc_lcl_evict,	L2C_TAD_EVENT_TAG_ALC_LCL_EVICT);
+EVENT_ATTR(tag_alc_lcl_clnvic,	L2C_TAD_EVENT_TAG_ALC_LCL_CLNVIC);
+EVENT_ATTR(tag_alc_lcl_dtyvic,	L2C_TAD_EVENT_TAG_ALC_LCL_DTYVIC);
+EVENT_ATTR(tag_alc_rmt_evict,	L2C_TAD_EVENT_TAG_ALC_RMT_EVICT);
+EVENT_ATTR(tag_alc_rmt_vic,	L2C_TAD_EVENT_TAG_ALC_RMT_VIC);
+
+EVENT_ATTR(rtg_alc,		L2C_TAD_EVENT_RTG_ALC);
+EVENT_ATTR(rtg_alc_hit,		L2C_TAD_EVENT_RTG_ALC_HIT);
+EVENT_ATTR(rtg_alc_hitwb,	L2C_TAD_EVENT_RTG_ALC_HITWB);
+
+EVENT_ATTR(stc_total,		L2C_TAD_EVENT_STC_TOTAL);
+EVENT_ATTR(stc_total_fail,	L2C_TAD_EVENT_STC_TOTAL_FAIL);
+EVENT_ATTR(stc_rmt,		L2C_TAD_EVENT_STC_RMT);
+EVENT_ATTR(stc_rmt_fail,	L2C_TAD_EVENT_STC_RMT_FAIL);
+EVENT_ATTR(stc_lcl,		L2C_TAD_EVENT_STC_LCL);
+EVENT_ATTR(stc_lcl_fail,	L2C_TAD_EVENT_STC_LCL_FAIL);
+
+EVENT_ATTR(oci_rtg_wait,	L2C_TAD_EVENT_OCI_RTG_WAIT);
+EVENT_ATTR(oci_fwd_cyc_hit,	L2C_TAD_EVENT_OCI_FWD_CYC_HIT);
+EVENT_ATTR(oci_fwd_race,	L2C_TAD_EVENT_OCI_FWD_RACE);
+EVENT_ATTR(oci_haks,		L2C_TAD_EVENT_OCI_HAKS);
+EVENT_ATTR(oci_fldx_tag_e_nodat, L2C_TAD_EVENT_OCI_FLDX_TAG_E_NODAT);
+EVENT_ATTR(oci_fldx_tag_e_dat,	L2C_TAD_EVENT_OCI_FLDX_TAG_E_DAT);
+EVENT_ATTR(oci_rldd,		L2C_TAD_EVENT_OCI_RLDD);
+EVENT_ATTR(oci_rldd_pemd,	L2C_TAD_EVENT_OCI_RLDD_PEMD);
+EVENT_ATTR(oci_rrq_dat_cnt,	L2C_TAD_EVENT_OCI_RRQ_DAT_CNT);
+EVENT_ATTR(oci_rrq_dat_dmask,	L2C_TAD_EVENT_OCI_RRQ_DAT_DMASK);
+EVENT_ATTR(oci_rsp_dat_cnt,	L2C_TAD_EVENT_OCI_RSP_DAT_CNT);
+EVENT_ATTR(oci_rsp_dat_dmaks,	L2C_TAD_EVENT_OCI_RSP_DAT_DMASK);
+EVENT_ATTR(oci_rsp_dat_vicd_cnt, L2C_TAD_EVENT_OCI_RSP_DAT_VICD_CNT);
+EVENT_ATTR(oci_rsp_dat_vicd_dmask, L2C_TAD_EVENT_OCI_RSP_DAT_VICD_DMASK);
+EVENT_ATTR(oci_rtg_alc_evict,	L2C_TAD_EVENT_OCI_RTG_ALC_EVICT);
+EVENT_ATTR(oci_rtg_alc_vic,	L2C_TAD_EVENT_OCI_RTG_ALC_VIC);
+
+static struct attribute *thunder_l2c_tad_pass2_events_attr[] = {
+	EVENT_PTR(l2t_hit),
+	EVENT_PTR(l2t_miss),
+	EVENT_PTR(l2t_noalloc),
+	EVENT_PTR(l2_vic),
+	EVENT_PTR(sc_fail),
+	EVENT_PTR(sc_pass),
+	EVENT_PTR(lfb_occ),
+	EVENT_PTR(wait_lfb),
+	EVENT_PTR(wait_vab),
+	EVENT_PTR(open_ccpi),
+
+	EVENT_PTR(lookup),
+	EVENT_PTR(lookup_xmc_lcl),
+	EVENT_PTR(lookup_xmc_rmt),
+	EVENT_PTR(lookup_mib),
+	EVENT_PTR(lookup_all),
+
+	EVENT_PTR(tag_alc_hit),
+	EVENT_PTR(tag_alc_miss),
+	EVENT_PTR(tag_alc_nalc),
+	EVENT_PTR(tag_nalc_hit),
+	EVENT_PTR(tag_nalc_miss),
+
+	EVENT_PTR(lmc_wr),
+	EVENT_PTR(lmc_sblkdty),
+
+	EVENT_PTR(tag_alc_rtg_hit),
+	EVENT_PTR(tag_alc_rtg_hite),
+	EVENT_PTR(tag_alc_rtg_hits),
+	EVENT_PTR(tag_alc_rtg_miss),
+	EVENT_PTR(tag_alc_nalc_rtg_hit),
+	EVENT_PTR(tag_nalc_rtg_miss),
+	EVENT_PTR(tag_nalc_rtg_hite),
+	EVENT_PTR(tag_nalc_rtg_hits),
+	EVENT_PTR(tag_alc_lcl_evict),
+	EVENT_PTR(tag_alc_lcl_clnvic),
+	EVENT_PTR(tag_alc_lcl_dtyvic),
+	EVENT_PTR(tag_alc_rmt_evict),
+	EVENT_PTR(tag_alc_rmt_vic),
+
+	EVENT_PTR(rtg_alc),
+	EVENT_PTR(rtg_alc_hit),
+	EVENT_PTR(rtg_alc_hitwb),
+
+	EVENT_PTR(stc_total),
+	EVENT_PTR(stc_total_fail),
+	EVENT_PTR(stc_rmt),
+	EVENT_PTR(stc_rmt_fail),
+	EVENT_PTR(stc_lcl),
+	EVENT_PTR(stc_lcl_fail),
+
+	EVENT_PTR(oci_rtg_wait),
+	EVENT_PTR(oci_fwd_cyc_hit),
+	EVENT_PTR(oci_fwd_race),
+	EVENT_PTR(oci_haks),
+	EVENT_PTR(oci_fldx_tag_e_nodat),
+	EVENT_PTR(oci_fldx_tag_e_dat),
+	EVENT_PTR(oci_rldd),
+	EVENT_PTR(oci_rldd_pemd),
+	EVENT_PTR(oci_rrq_dat_cnt),
+	EVENT_PTR(oci_rrq_dat_dmask),
+	EVENT_PTR(oci_rsp_dat_cnt),
+	EVENT_PTR(oci_rsp_dat_dmaks),
+	EVENT_PTR(oci_rsp_dat_vicd_cnt),
+	EVENT_PTR(oci_rsp_dat_vicd_dmask),
+	EVENT_PTR(oci_rtg_alc_evict),
+	EVENT_PTR(oci_rtg_alc_vic),
+
+	EVENT_PTR(qd0_idx),
+	EVENT_PTR(qd0_rdat),
+	EVENT_PTR(qd0_bnks),
+	EVENT_PTR(qd0_wdat),
+
+	EVENT_PTR(qd1_idx),
+	EVENT_PTR(qd1_rdat),
+	EVENT_PTR(qd1_bnks),
+	EVENT_PTR(qd1_wdat),
+
+	EVENT_PTR(qd2_idx),
+	EVENT_PTR(qd2_rdat),
+	EVENT_PTR(qd2_bnks),
+	EVENT_PTR(qd2_wdat),
+
+	EVENT_PTR(qd3_idx),
+	EVENT_PTR(qd3_rdat),
+	EVENT_PTR(qd3_bnks),
+	EVENT_PTR(qd3_wdat),
+
+	EVENT_PTR(qd4_idx),
+	EVENT_PTR(qd4_rdat),
+	EVENT_PTR(qd4_bnks),
+	EVENT_PTR(qd4_wdat),
+
+	EVENT_PTR(qd5_idx),
+	EVENT_PTR(qd5_rdat),
+	EVENT_PTR(qd5_bnks),
+	EVENT_PTR(qd5_wdat),
+
+	EVENT_PTR(qd6_idx),
+	EVENT_PTR(qd6_rdat),
+	EVENT_PTR(qd6_bnks),
+	EVENT_PTR(qd6_wdat),
+
+	EVENT_PTR(qd7_idx),
+	EVENT_PTR(qd7_rdat),
+	EVENT_PTR(qd7_bnks),
+	EVENT_PTR(qd7_wdat),
+	NULL,
+};
+
+static struct attribute_group thunder_l2c_tad_events_group = {
+	.name = "events",
+	.attrs = NULL,
+};
+
+static const struct attribute_group *thunder_l2c_tad_attr_groups[] = {
+	&thunder_uncore_attr_group,
+	&thunder_l2c_tad_format_group,
+	&thunder_l2c_tad_events_group,
+	NULL,
+};
+
+struct pmu thunder_l2c_tad_pmu = {
+	.attr_groups	= thunder_l2c_tad_attr_groups,
+	.name		= "thunder_l2c_tad",
+	.event_init	= thunder_uncore_event_init,
+	.add		= thunder_uncore_add,
+	.del		= thunder_uncore_del,
+	.start		= thunder_uncore_start,
+	.stop		= thunder_uncore_stop,
+	.read		= thunder_uncore_read,
+};
+
+static int event_valid(u64 config)
+{
+	if ((config > 0 && config <= L2C_TAD_EVENT_WAIT_VAB) ||
+	    config == L2C_TAD_EVENT_RTG_HIT ||
+	    config == L2C_TAD_EVENT_RTG_MISS ||
+	    config == L2C_TAD_EVENT_L2_RTG_VIC ||
+	    config == L2C_TAD_EVENT_L2_OPEN_OCI ||
+	    ((config & 0x80) && ((config & 0xf) <= 3)))
+		return 1;
+
+	if (thunder_uncore_version == 1)
+		if (config == L2C_TAD_EVENT_OPEN_CCPI ||
+		    (config >= L2C_TAD_EVENT_LOOKUP &&
+		     config <= L2C_TAD_EVENT_LOOKUP_ALL) ||
+		    (config >= L2C_TAD_EVENT_TAG_ALC_HIT &&
+		     config <= L2C_TAD_EVENT_OCI_RTG_ALC_VIC &&
+		     config != 0x4d &&
+		     config != 0x66 &&
+		     config != 0x67))
+			return 1;
+
+	return 0;
+}
+
+int __init thunder_uncore_l2c_tad_setup(void)
+{
+	int ret = -ENOMEM;
+
+	thunder_uncore_l2c_tad = kzalloc(sizeof(struct thunder_uncore),
+					 GFP_KERNEL);
+	if (!thunder_uncore_l2c_tad)
+		goto fail_nomem;
+
+	if (thunder_uncore_version == 0)
+		thunder_l2c_tad_events_group.attrs = thunder_l2c_tad_events_attr;
+	else /* default */
+		thunder_l2c_tad_events_group.attrs = thunder_l2c_tad_pass2_events_attr;
+
+	ret = thunder_uncore_setup(thunder_uncore_l2c_tad,
+			   PCI_DEVICE_ID_THUNDER_L2C_TAD,
+			   L2C_TAD_CONTROL_OFFSET,
+			   L2C_TAD_COUNTER_OFFSET + L2C_TAD_NR_COUNTERS
+				* sizeof(unsigned long long),
+			   &thunder_l2c_tad_pmu,
+			   L2C_TAD_NR_COUNTERS);
+	if (ret)
+		goto fail;
+
+	thunder_uncore_l2c_tad->type = L2C_TAD_TYPE;
+	thunder_uncore_l2c_tad->event_valid = event_valid;
+	return 0;
+
+fail:
+	kfree(thunder_uncore_l2c_tad);
+fail_nomem:
+	return ret;
+}