// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/perf_event.h>
#include <linux/platform_device.h>
#define CSKY_PMU_MAX_EVENTS 32
#define HPCR "<0, 0x0>" /* PMU Control reg */
#define HPCNTENR "<0, 0x4>" /* Count Enable reg */
static uint64_t (*hw_raw_read_mapping[CSKY_PMU_MAX_EVENTS])(void);
static void (*hw_raw_write_mapping[CSKY_PMU_MAX_EVENTS])(uint64_t val);
struct csky_pmu_t {
struct pmu pmu;
uint32_t hpcr;
} csky_pmu;
#define cprgr(reg) \
({ \
unsigned int tmp; \
asm volatile("cprgr %0, "reg"\n" \
: "=r"(tmp) \
: \
: "memory"); \
tmp; \
})
#define cpwgr(reg, val) \
({ \
asm volatile( \
"cpwgr %0, "reg"\n" \
: \
: "r"(val) \
: "memory"); \
})
#define cprcr(reg) \
({ \
unsigned int tmp; \
asm volatile("cprcr %0, "reg"\n" \
: "=r"(tmp) \
: \
: "memory"); \
tmp; \
})
#define cpwcr(reg, val) \
({ \
asm volatile( \
"cpwcr %0, "reg"\n" \
: \
: "r"(val) \
: "memory"); \
})
/* cycle counter */
static uint64_t csky_pmu_read_cc(void)
{
uint32_t lo, hi, tmp;
uint64_t result;
do {
tmp = cprgr("<0, 0x3>");
lo = cprgr("<0, 0x2>");
hi = cprgr("<0, 0x3>");
} while (hi != tmp);
result = (uint64_t) (hi) << 32;
result |= lo;
return result;
}
static void csky_pmu_write_cc(uint64_t val)
{
cpwgr("<0, 0x2>", (uint32_t) val);
cpwgr("<0, 0x3>", (uint32_t) (val >> 32));
}
/* instruction counter */
static uint64_t csky_pmu_read_ic(void)
{
uint32_t lo, hi, tmp;
uint64_t result;
do {
tmp = cprgr("<0, 0x5>");
lo = cprgr("<0, 0x4>");
hi = cprgr("<0, 0x5>");
} while (hi != tmp);
result = (uint64_t) (hi) << 32;
result |= lo;
return result;
}
static void csky_pmu_write_ic(uint64_t val)
{
cpwgr("<0, 0x4>", (uint32_t) val);
cpwgr("<0, 0x5>", (uint32_t) (val >> 32));
}
/* l1 icache access counter */
static uint64_t csky_pmu_read_icac(void)
{
uint32_t lo, hi, tmp;
uint64_t result;
do {
tmp = cprgr("<0, 0x7>");
lo = cprgr("<0, 0x6>");
hi = cprgr("<0, 0x7>");
} while (hi != tmp);
result = (uint64_t) (hi) << 32;
result |= lo;
return result;
}
static void csky_pmu_write_icac(uint64_t val)
{
cpwgr("<0, 0x6>", (uint32_t) val);
cpwgr("<0, 0x7>", (uint32_t) (val >> 32));
}
/* l1 icache miss counter */
static uint64_t csky_pmu_read_icmc(void)
{
uint32_t lo, hi, tmp;
uint64_t result;
do {
tmp = cprgr("<0, 0x9>");
lo = cprgr("<0, 0x8>");
hi = cprgr("<0, 0x9>");
} while (hi != tmp);
result = (uint64_t) (hi) << 32;
result |= lo;
return result;
}
static void csky_pmu_write_icmc(uint64_t val)
{
cpwgr("<0, 0x8>", (uint32_t) val);
cpwgr("<0, 0x9>", (uint32_t) (val >> 32));
}
/* l1 dcache access counter */
static uint64_t csky_pmu_read_dcac(void)
{
uint32_t lo, hi, tmp;
uint64_t result;
do {
tmp = cprgr("<0, 0xb>");
lo = cprgr("<0, 0xa>");
hi = cprgr("<0, 0xb>");
} while (hi != tmp);
result = (uint64_t) (hi) << 32;
result |= lo;
return result;
}
static void csky_pmu_write_dcac(uint64_t val)
{
cpwgr("<0, 0xa>", (uint32_t) val);
cpwgr("<0, 0xb>", (uint32_t) (val >> 32));
}
/* l1 dcache miss counter */
static uint64_t csky_pmu_read_dcmc(void)
{
uint32_t lo, hi, tmp;
uint64_t result;
do {
tmp = cprgr("<0, 0xd>");
lo = cprgr("<0, 0xc>");
hi = cprgr("<0, 0xd>");
} while (hi != tmp);
result = (uint64_t) (hi) << 32;
result |= lo;
return result;
}
static void csky_pmu_write_dcmc(uint64_t val)
{
cpwgr("<0, 0xc>", (uint32_t) val);
cpwgr("<0, 0xd>", (uint32_t) (val >> 32));
}
/* l2 cache access counter */
static uint64_t csky_pmu_read_l2ac(void)
{
uint32_t lo, hi, tmp;
uint64_t result;
do {
tmp = cprgr("<0, 0xf>");
lo = cprgr("<0, 0xe>");
hi = cprgr("