M7350/kernel/drivers/soc/qcom/pfe-wa.c
2024-09-09 08:57:42 +00:00

145 lines
3.4 KiB
C

/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/perf_event.h>
#include <linux/slab.h>
/*
* BIT 19:16 -> prefix
* BIT 15:12 -> Reg number
* BIT 11:4 -> Code number
* BIT 3:0 -> Group number
*/
#define L1_DROP_EVENT 0x12513
#define EHWPF_BITS 0x3
#define EHWPF_SHIFT 0x0
#define EHWPF_MASK ~(EHWPF_BITS << EHWPF_SHIFT)
/*
* EHWPF
* 0x0: turn off PFE
* 0x1: turn on l1 cache PFE
* 0x2: turn on l1 and l2 cache PFE
* 0x3: turn on l1,l2, l3 cache PFE
*/
#define EHWPF_OFF (0x0 << EHWPF_SHIFT)
#define EHWPF_L1L2 (0x2 << EHWPF_SHIFT)
struct l1_drop_data {
struct notifier_block nb_cpu;
struct perf_event *l1_drop_events[NR_CPUS];
};
static unsigned long max_allowed_cntr = 257;
module_param(max_allowed_cntr, ulong, 0);
static void l1_drop_handler(struct perf_event *event,
struct perf_sample_data *data,
struct pt_regs *regs)
{
u32 val;
asm volatile ("mrs %0, s3_1_c11_c4_7" : "=r" (val));
val = (val & EHWPF_MASK) | EHWPF_OFF;
asm volatile ("msr s3_1_c11_c4_7, %0" : : "r" (val));
val = (val & EHWPF_MASK) | EHWPF_L1L2;
isb();
asm volatile ("msr s3_1_c11_c4_7, %0" : : "r" (val));
isb();
}
static void l1_drop_event_create(int cpu, void *info)
{
struct l1_drop_data *drv = info;
struct perf_event *event = drv->l1_drop_events[cpu];
struct perf_event_attr attr = {
.pinned = 1,
.disabled = 0,
.sample_period = max_allowed_cntr,
.type = PERF_TYPE_RAW,
.config = L1_DROP_EVENT,
.size = sizeof(struct perf_event_attr),
};
if (event)
return;
event = perf_event_create_kernel_counter(&attr, cpu, NULL,
l1_drop_handler,
drv);
if (IS_ERR(event)) {
pr_err("PERF Event creation failed on cpu %d ptr_err %ld\n",
cpu, PTR_ERR(event));
return;
}
drv->l1_drop_events[cpu] = event;
}
static int l1_drop_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
struct l1_drop_data *data = container_of(self, struct l1_drop_data,
nb_cpu);
unsigned long cpu = (unsigned long)hcpu;
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
l1_drop_event_create(cpu, data);
break;
};
return NOTIFY_OK;
}
static struct of_device_id pfe_wa_of_match[] = {
{.compatible = "qcom,kryo-pmuv3", },
{}
};
static int __init msm_pfe_wa_init(void)
{
int cpu;
struct device_node *np;
struct l1_drop_data *l1_drop;
np = of_find_matching_node(NULL, pfe_wa_of_match);
if (!np) {
pr_debug("Disable PFE WA - PMU is not enabled\n");
return -ENODEV;
}
if (!of_find_property(np, "qcom,enable-pfe-wa", NULL)) {
pr_debug("Disable PFE WA\n");
return -ENODEV;
}
l1_drop = kzalloc(sizeof(struct l1_drop_data), GFP_KERNEL);
if (!l1_drop)
return -ENOMEM;
l1_drop->nb_cpu.notifier_call = l1_drop_cpu_notify;
register_cpu_notifier(&l1_drop->nb_cpu);
get_online_cpus();
for_each_online_cpu(cpu)
l1_drop_event_create(cpu, l1_drop);
put_online_cpus();
return 0;
}
late_initcall(msm_pfe_wa_init);