1*326ba66dSDrew Fustini // SPDX-License-Identifier: GPL-2.0-only
2*326ba66dSDrew Fustini #include <linux/cpu.h>
3*326ba66dSDrew Fustini #include <linux/cpu_pm.h>
4*326ba66dSDrew Fustini #include <linux/cpuhotplug.h>
5*326ba66dSDrew Fustini #include <linux/notifier.h>
6*326ba66dSDrew Fustini #include <linux/percpu-defs.h>
7*326ba66dSDrew Fustini #include <linux/types.h>
8*326ba66dSDrew Fustini
9*326ba66dSDrew Fustini #include <asm/cpufeature-macros.h>
10*326ba66dSDrew Fustini #include <asm/hwcap.h>
11*326ba66dSDrew Fustini #include <asm/qos.h>
12*326ba66dSDrew Fustini
13*326ba66dSDrew Fustini /*
14*326ba66dSDrew Fustini * Cached value of srmcfg csr for each cpu. Seeded to U32_MAX so the next
15*326ba66dSDrew Fustini * __switch_to_srmcfg() unconditionally writes the CSR. The encoding
16*326ba66dSDrew Fustini * MCID << 16 | RCID with both fields well under 16 bits can never
17*326ba66dSDrew Fustini * produce this sentinel. This covers early-boot context switches that
18*326ba66dSDrew Fustini * happen before riscv_srmcfg_init() runs as an arch_initcall.
19*326ba66dSDrew Fustini */
20*326ba66dSDrew Fustini DEFINE_PER_CPU(u32, cpu_srmcfg) = U32_MAX;
21*326ba66dSDrew Fustini
22*326ba66dSDrew Fustini /* default srmcfg value for each cpu, set via resctrl cpu assignment */
23*326ba66dSDrew Fustini DEFINE_PER_CPU(u32, cpu_srmcfg_default);
24*326ba66dSDrew Fustini
25*326ba66dSDrew Fustini /*
26*326ba66dSDrew Fustini * Invalidate the per-CPU srmcfg cache. Used as both the cpuhp startup
27*326ba66dSDrew Fustini * and teardown callback. U32_MAX is not a valid srmcfg value
28*326ba66dSDrew Fustini * (MCID << 16 | RCID, both fields under 16 bits), so the next
29*326ba66dSDrew Fustini * __switch_to_srmcfg() always writes the CSR.
30*326ba66dSDrew Fustini *
31*326ba66dSDrew Fustini * Ssqosid leaves the CSR implementation-defined across hart stop/start,
32*326ba66dSDrew Fustini * so the cached value cannot be trusted after online. The startup
33*326ba66dSDrew Fustini * callback runs at CPUHP_AP_ONLINE_DYN, before CPUHP_AP_ACTIVE makes
34*326ba66dSDrew Fustini * the CPU schedulable, so the cache is invalidated before any normal
35*326ba66dSDrew Fustini * task runs and the CSR is written on that task's first switch.
36*326ba66dSDrew Fustini * The teardown callback is not relied on. Idle and per-CPU kthreads keep
37*326ba66dSDrew Fustini * switching as the CPU goes down and overwrite the sentinel with the CPU
38*326ba66dSDrew Fustini * default, so it does not survive the offline period.
39*326ba66dSDrew Fustini */
riscv_srmcfg_reset_cache(unsigned int cpu)40*326ba66dSDrew Fustini static int riscv_srmcfg_reset_cache(unsigned int cpu)
41*326ba66dSDrew Fustini {
42*326ba66dSDrew Fustini per_cpu(cpu_srmcfg, cpu) = U32_MAX;
43*326ba66dSDrew Fustini return 0;
44*326ba66dSDrew Fustini }
45*326ba66dSDrew Fustini
46*326ba66dSDrew Fustini /*
47*326ba66dSDrew Fustini * CPU PM notifier: invalidate the cached srmcfg on resume from a deep
48*326ba66dSDrew Fustini * idle / suspend. Ssqosid leaves CSR_SRMCFG state across low-power
49*326ba66dSDrew Fustini * transitions implementation-defined, and the boot CPU never goes
50*326ba66dSDrew Fustini * through the cpuhp online callback during system suspend, so without
51*326ba66dSDrew Fustini * this hook __switch_to_srmcfg() would skip the CSR write when the
52*326ba66dSDrew Fustini * outgoing task happens to share its srmcfg with the pre-suspend cache.
53*326ba66dSDrew Fustini */
riscv_srmcfg_pm_notify(struct notifier_block * nb,unsigned long action,void * unused)54*326ba66dSDrew Fustini static int riscv_srmcfg_pm_notify(struct notifier_block *nb,
55*326ba66dSDrew Fustini unsigned long action, void *unused)
56*326ba66dSDrew Fustini {
57*326ba66dSDrew Fustini switch (action) {
58*326ba66dSDrew Fustini case CPU_PM_EXIT:
59*326ba66dSDrew Fustini case CPU_PM_ENTER_FAILED:
60*326ba66dSDrew Fustini /*
61*326ba66dSDrew Fustini * The CSR is implementation-defined across the low-power
62*326ba66dSDrew Fustini * transition. Invalidate the cache and eagerly rewrite the
63*326ba66dSDrew Fustini * CSR for the current task so it does not run mis-tagged
64*326ba66dSDrew Fustini * until the next context switch.
65*326ba66dSDrew Fustini */
66*326ba66dSDrew Fustini __this_cpu_write(cpu_srmcfg, U32_MAX);
67*326ba66dSDrew Fustini __switch_to_srmcfg(current);
68*326ba66dSDrew Fustini break;
69*326ba66dSDrew Fustini }
70*326ba66dSDrew Fustini return NOTIFY_OK;
71*326ba66dSDrew Fustini }
72*326ba66dSDrew Fustini
73*326ba66dSDrew Fustini static struct notifier_block riscv_srmcfg_pm_nb = {
74*326ba66dSDrew Fustini .notifier_call = riscv_srmcfg_pm_notify,
75*326ba66dSDrew Fustini };
76*326ba66dSDrew Fustini
riscv_srmcfg_init(void)77*326ba66dSDrew Fustini static int __init riscv_srmcfg_init(void)
78*326ba66dSDrew Fustini {
79*326ba66dSDrew Fustini int err;
80*326ba66dSDrew Fustini
81*326ba66dSDrew Fustini if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SSQOSID))
82*326ba66dSDrew Fustini return 0;
83*326ba66dSDrew Fustini
84*326ba66dSDrew Fustini /*
85*326ba66dSDrew Fustini * cpuhp_setup_state() invokes the startup callback locally on every
86*326ba66dSDrew Fustini * already-online CPU, so no separate seed loop is needed here.
87*326ba66dSDrew Fustini */
88*326ba66dSDrew Fustini err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "riscv/srmcfg:online",
89*326ba66dSDrew Fustini riscv_srmcfg_reset_cache, riscv_srmcfg_reset_cache);
90*326ba66dSDrew Fustini if (err < 0)
91*326ba66dSDrew Fustini pr_warn("srmcfg: cpuhp setup failed (%d), cache not invalidated on CPU online\n",
92*326ba66dSDrew Fustini err);
93*326ba66dSDrew Fustini
94*326ba66dSDrew Fustini /*
95*326ba66dSDrew Fustini * Register the PM notifier even if the cpuhp setup failed. It is
96*326ba66dSDrew Fustini * independent of the cpuhp state and guards suspend/resume.
97*326ba66dSDrew Fustini */
98*326ba66dSDrew Fustini cpu_pm_register_notifier(&riscv_srmcfg_pm_nb);
99*326ba66dSDrew Fustini return 0;
100*326ba66dSDrew Fustini }
101*326ba66dSDrew Fustini arch_initcall(riscv_srmcfg_init);
102