soc: qcom: Add support PFE WA
Sometimes the PFTLB entries get stuck in the invalid state and new prefetches get dropped. As a workaround, when a large number of L1 prefetches have been dropped, above a threshold, the PMU can generate an interrupt. And then reset PFE. The default threshold is 257. Change-Id: Id6142037fa411575c3cbaa30dd08140ab6e5cb3f Signed-off-by: Se Wang (Patrick) Oh <sewango@codeaurora.org>
This commit is contained in:
parent
ba6b11b381
commit
010f6cf3d2
3 changed files with 155 additions and 0 deletions
|
@ -9,6 +9,16 @@ config MSM_INRUSH_CURRENT_MITIGATION
|
|||
huge load on a rail. This driver introduces an intermediate
|
||||
load to mitigate the in-rush current.
|
||||
|
||||
config MSM_PFE_WA
|
||||
depends on HW_PERF_EVENTS
|
||||
bool "Enable a H/W PFE WA"
|
||||
help
|
||||
Sometimes the PFTLB entries get stuck in the invalid state and new
|
||||
prefetches get dropped. For a workaround, count L1 prefeches dropped
|
||||
due to PFTLB miss and reset H/W PFE when a overflow happens.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MSM_SMEM
|
||||
depends on ARCH_QCOM
|
||||
depends on REMOTE_SPINLOCK_MSM
|
||||
|
|
|
@ -33,6 +33,7 @@ obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
|
|||
obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o
|
||||
obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
|
||||
obj-$(CONFIG_MSM_CORE_CTL_HELPER) += core_ctl_helper.o
|
||||
obj-$(CONFIG_MSM_PFE_WA) += pfe-wa.o
|
||||
|
||||
ifdef CONFIG_MSM_SUBSYSTEM_RESTART
|
||||
obj-y += subsystem_notif.o
|
||||
|
|
144
drivers/soc/qcom/pfe-wa.c
Normal file
144
drivers/soc/qcom/pfe-wa.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
/* 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);
|
Loading…
Add table
Reference in a new issue