Commit | Line | Data |
---|---|---|
de415dee | 1 | // SPDX-License-Identifier: GPL-2.0 |
4ec567b8 SP |
2 | /* |
3 | * Intel Turbo Boost Max Technology 3.0 legacy (non HWP) enumeration driver | |
4 | * Copyright (c) 2017, Intel Corporation. | |
5 | * All rights reserved. | |
6 | * | |
af050abb | 7 | * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> |
4ec567b8 SP |
8 | */ |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
10 | ||
30321108 AS |
11 | #include <linux/cpufeature.h> |
12 | #include <linux/cpuhotplug.h> | |
af050abb | 13 | #include <linux/init.h> |
30321108 | 14 | #include <linux/kernel.h> |
af050abb PG |
15 | #include <linux/topology.h> |
16 | #include <linux/workqueue.h> | |
30321108 | 17 | |
4ec567b8 SP |
18 | #include <asm/cpu_device_id.h> |
19 | #include <asm/intel-family.h> | |
20 | ||
21 | #define MSR_OC_MAILBOX 0x150 | |
22 | #define MSR_OC_MAILBOX_CMD_OFFSET 32 | |
23 | #define MSR_OC_MAILBOX_RSP_OFFSET 32 | |
24 | #define MSR_OC_MAILBOX_BUSY_BIT 63 | |
25 | #define OC_MAILBOX_FC_CONTROL_CMD 0x1C | |
26 | ||
27 | /* | |
28 | * Typical latency to get mail box response is ~3us, It takes +3 us to | |
29 | * process reading mailbox after issuing mailbox write on a Broadwell 3.4 GHz | |
30 | * system. So for most of the time, the first mailbox read should have the | |
31 | * response, but to avoid some boundary cases retry twice. | |
32 | */ | |
33 | #define OC_MAILBOX_RETRY_COUNT 2 | |
34 | ||
35 | static int get_oc_core_priority(unsigned int cpu) | |
36 | { | |
37 | u64 value, cmd = OC_MAILBOX_FC_CONTROL_CMD; | |
38 | int ret, i; | |
39 | ||
40 | /* Issue favored core read command */ | |
41 | value = cmd << MSR_OC_MAILBOX_CMD_OFFSET; | |
42 | /* Set the busy bit to indicate OS is trying to issue command */ | |
43 | value |= BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT); | |
44 | ret = wrmsrl_safe(MSR_OC_MAILBOX, value); | |
45 | if (ret) { | |
46 | pr_debug("cpu %d OC mailbox write failed\n", cpu); | |
47 | return ret; | |
48 | } | |
49 | ||
50 | for (i = 0; i < OC_MAILBOX_RETRY_COUNT; ++i) { | |
51 | ret = rdmsrl_safe(MSR_OC_MAILBOX, &value); | |
52 | if (ret) { | |
53 | pr_debug("cpu %d OC mailbox read failed\n", cpu); | |
54 | break; | |
55 | } | |
56 | ||
57 | if (value & BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT)) { | |
58 | pr_debug("cpu %d OC mailbox still processing\n", cpu); | |
59 | ret = -EBUSY; | |
60 | continue; | |
61 | } | |
62 | ||
63 | if ((value >> MSR_OC_MAILBOX_RSP_OFFSET) & 0xff) { | |
64 | pr_debug("cpu %d OC mailbox cmd failed\n", cpu); | |
65 | ret = -ENXIO; | |
66 | break; | |
67 | } | |
68 | ||
69 | ret = value & 0xff; | |
70 | pr_debug("cpu %d max_ratio %d\n", cpu, ret); | |
71 | break; | |
72 | } | |
73 | ||
74 | return ret; | |
75 | } | |
76 | ||
77 | /* | |
78 | * The work item is needed to avoid CPU hotplug locking issues. The function | |
79 | * itmt_legacy_set_priority() is called from CPU online callback, so can't | |
80 | * call sched_set_itmt_support() from there as this function will aquire | |
81 | * hotplug locks in its path. | |
82 | */ | |
83 | static void itmt_legacy_work_fn(struct work_struct *work) | |
84 | { | |
85 | sched_set_itmt_support(); | |
86 | } | |
87 | ||
88 | static DECLARE_WORK(sched_itmt_work, itmt_legacy_work_fn); | |
89 | ||
90 | static int itmt_legacy_cpu_online(unsigned int cpu) | |
91 | { | |
92 | static u32 max_highest_perf = 0, min_highest_perf = U32_MAX; | |
93 | int priority; | |
94 | ||
95 | priority = get_oc_core_priority(cpu); | |
96 | if (priority < 0) | |
97 | return 0; | |
98 | ||
99 | sched_set_itmt_core_prio(priority, cpu); | |
100 | ||
101 | /* Enable ITMT feature when a core with different priority is found */ | |
102 | if (max_highest_perf <= min_highest_perf) { | |
103 | if (priority > max_highest_perf) | |
104 | max_highest_perf = priority; | |
105 | ||
106 | if (priority < min_highest_perf) | |
107 | min_highest_perf = priority; | |
108 | ||
109 | if (max_highest_perf > min_highest_perf) | |
110 | schedule_work(&sched_itmt_work); | |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | #define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } | |
117 | ||
118 | static const struct x86_cpu_id itmt_legacy_cpu_ids[] = { | |
119 | ICPU(INTEL_FAM6_BROADWELL_X), | |
5520437b | 120 | ICPU(INTEL_FAM6_SKYLAKE_X), |
4ec567b8 SP |
121 | {} |
122 | }; | |
4ec567b8 SP |
123 | |
124 | static int __init itmt_legacy_init(void) | |
125 | { | |
126 | const struct x86_cpu_id *id; | |
127 | int ret; | |
128 | ||
129 | id = x86_match_cpu(itmt_legacy_cpu_ids); | |
130 | if (!id) | |
131 | return -ENODEV; | |
132 | ||
4ec567b8 SP |
133 | ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, |
134 | "platform/x86/turbo_max_3:online", | |
135 | itmt_legacy_cpu_online, NULL); | |
136 | if (ret < 0) | |
137 | return ret; | |
138 | ||
139 | return 0; | |
140 | } | |
141 | late_initcall(itmt_legacy_init) |