diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a24930fa7399..99f625d18ea9 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -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 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index aa3bab43af2d..17a5bf6d6f1c 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -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 diff --git a/drivers/soc/qcom/pfe-wa.c b/drivers/soc/qcom/pfe-wa.c new file mode 100644 index 000000000000..ef1f8f1d39ee --- /dev/null +++ b/drivers/soc/qcom/pfe-wa.c @@ -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 +#include +#include +#include +#include +#include +#include + +/* + * 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);