// SPDX-License-Identifier: GPL-2.0
/*
* CAVIUM THUNDERX2 SoC PMU UNCORE
* Copyright (C) 2018 Cavium Inc.
* Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
*/
#include <linux/acpi.h>
#include <linux/cpuhotplug.h>
#include <linux/perf_event.h>
#include <linux/platform_device.h>
/* Each ThunderX2(TX2) Socket has a L3C and DMC UNCORE PMU device.
* Each UNCORE PMU device consists of 4 independent programmable counters.
* Counters are 32 bit and do not support overflow interrupt,
* they need to be sampled before overflow(i.e, at every 2 seconds).
*/
#define TX2_PMU_DMC_L3C_MAX_COUNTERS 4
#define TX2_PMU_CCPI2_MAX_COUNTERS 8
#define TX2_PMU_MAX_COUNTERS TX2_PMU_CCPI2_MAX_COUNTERS
#define TX2_PMU_DMC_CHANNELS 8
#define TX2_PMU_L3_TILES 16
#define TX2_PMU_HRTIMER_INTERVAL (2 * NSEC_PER_SEC)
#define GET_EVENTID(ev, mask) ((ev->hw.config) & mask)
#define GET_COUNTERID(ev, mask) ((ev->hw.idx) & mask)
/* 1 byte per counter(4 counters).
* Event id is encoded in bits [5:1] of a byte,
*/
#define DMC_EVENT_CFG(idx, val) ((val) << (((idx) * 8) + 1))
/* bits[3:0] to select counters, are indexed from 8 to 15. */
#define CCPI2_COUNTER_OFFSET 8
#define L3C_COUNTER_CTL 0xA8
#define L3C_COUNTER_DATA 0xAC
#define DMC_COUNTER_CTL 0x234
#define DMC_COUNTER_DATA 0x240
#define CCPI2_PERF_CTL 0x108
#define CCPI2_COUNTER_CTL 0x10C
#define CCPI2_COUNTER_SEL 0x12c
#define CCPI2_COUNTER_DATA_L 0x130
#define CCPI2_COUNTER_DATA_H 0x134
/* L3C event IDs */
#define L3_EVENT_READ_REQ 0xD
#define L3_EVENT_WRITEBACK_REQ 0xE
#define L3_EVENT_INV_N_WRITE_REQ 0xF
#define L3_EVENT_INV_REQ 0x10
#define L3_EVENT_EVICT_REQ 0x13
#define L3_EVENT_INV_N_WRITE_HIT 0x14
#define L3_EVENT_INV_HIT 0x15
#define L3_EVENT_READ_HIT 0x17
#define L3_EVENT_MAX 0x18
/* DMC event IDs */
#define DMC_EVENT_COUNT_CYCLES 0x1
#define DMC_EVENT_WRITE_TXNS 0xB
#define DMC_EVENT_DATA_TRANSFERS 0xD
#define DMC_EVENT_READ_TXNS 0xF
#define DMC_EVENT_MAX 0x10
#define CCPI2_EVENT_REQ_PKT_SENT 0x3D
#define CCPI2_EVENT_SNOOP_PKT_SENT 0x65
#define CCPI2_EVENT_DATA_PKT_SENT 0x105
#define CCPI2_EVENT_GIC_PKT_SENT 0x12D
#define CCPI2_EVENT_MAX 0x200
#define CCPI2_PERF_CTL_ENABLE BIT(0)
#define CCPI2_PERF_CTL_START BIT(1)
#define CCPI2_PERF_CTL_RESET BIT(4)
#define CCPI2_EVENT_LEVEL_RISING_EDGE BIT(10)
#define CCPI2_EVENT_TYPE_EDGE_SENSITIVE BIT(11)
enum tx2_uncore_type {
PMU_TYPE_L3C,
PMU_TYPE_DMC,
PMU_TYPE_CCPI2,
PMU_TYPE_INVALID,
};
/*
* Each socket has 3 uncore devices associated with a PMU. The DMC and
* L3C have 4 32-bit counters and the CCPI2 has 8 64-bit counters.
*/
struct tx2_uncore_pmu {
struct hlist_node hpnode;
struct list_head entry;
struct pmu pmu;
char *name;
int node;
int cpu;
u32 max_counters;
u32 counters_mask;
u32 prorate_factor;
u32 max_events;
u32 events_mask;
u64 hrtimer_interval;
void __iomem *base;
DECLARE_BITMAP(active_counters, TX2_PMU_MAX_COUNTERS);
struct perf_event *events[TX2_PMU_MAX_COUNTERS];
struct device *dev;
struct hrtimer hrtimer;
const struct attribute_group **attr_groups;
enum tx2_uncore_type type;
enum hrtimer_restart (*hrtimer_callback)(struct hrtimer *cb);
void (*init_cntr_base)(struct perf_event *event,
struct tx2_uncore_pmu *tx2_pmu);
void (*stop_event)(struct perf_event *event);
void (*start_event)(struct perf_event *event, int flags);
};
static LIST_HEAD(tx2_pmus);
static inline struct tx2_uncore_pmu *pmu_to_tx2_pmu(struct pmu *pmu)
{
return container_of(pmu, struct tx2_uncore_pmu, pmu);
}
#define TX2_PMU_FORMAT_ATTR(_var, _name, _format) \
static ssize_t \
__tx2_pmu_##_var##_show(struct device *dev, \
struct device_attribute *attr, \
char *page) \
{ \
BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \
return sprintf(page, _format "\n"); \
} \
\
static struct device_attribute format_attr_##_var = \
__ATTR(_name, 0444, __tx2_pmu_##_var##_show, NULL)
TX2_PMU_FORMAT_ATTR(event, event, "config:0-4");
TX2_PMU_FORMAT_ATTR(event_ccpi2, event, "config:0-9");
static struct attribute *l3c_pmu_format_attrs[] = {
&format_attr_event.attr,
NULL,
};
static struct attribute *dmc_pmu_format_attrs[] = {
&format_attr_event.attr,
NULL,
};
static struct attribute *ccpi2_pmu_format_attrs[] = {
&format_attr_event_ccpi2.attr,
NULL,
};
static const struct attribute_group l3c_pmu_format_attr_group = {
.name = "format",
.attrs = l3c_pmu_format_attrs,
};
static const struct attribute_group dmc_pmu_format_attr_group = {
.name = "format",
.attrs = dmc_pmu_format_attrs,
};
static const struct attribute_group ccpi2_pmu_format_attr_group = {
.name = "format",
.attrs = ccpi2_pmu_format_attrs,
};
/*
* sysfs event attributes
*/
static ssize_t tx2_pmu_event_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
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);
}
#define TX2_EVENT_ATTR(name, config) \
PMU_EVENT_ATTR(name, tx2_pmu_event_attr_##name, \
config, tx2_pmu_event_show)
TX2_EVENT_ATTR(read_request, L3_EVENT_READ_REQ);
TX2_EVENT_ATTR(writeback_request, L3_EVENT_WRITEBACK_REQ);
TX2_EVENT_ATTR(inv_nwrite_request, L3_EVENT_INV_N_WRITE_REQ);
TX2_EVENT_ATTR