Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | // SPDX-License-Identifier: GPL-2.0-only
/*
* cooling device driver that activates the processor throttling by
* programming the TCC Offset register.
* Copyright (c) 2021, Intel Corporation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
#include <linux/module.h>
#include <linux/thermal.h>
#include <asm/cpu_device_id.h>
#define TCC_SHIFT 24
#define TCC_MASK (0x3fULL<<24)
#define TCC_PROGRAMMABLE BIT(30)
static struct thermal_cooling_device *tcc_cdev;
static int tcc_get_max_state(struct thermal_cooling_device *cdev, unsigned long
*state)
{
*state = TCC_MASK >> TCC_SHIFT;
return 0;
}
static int tcc_offset_update(int tcc)
{
u64 val;
int err;
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
if (err)
return err;
val &= ~TCC_MASK;
val |= tcc << TCC_SHIFT;
err = wrmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, val);
if (err)
return err;
return 0;
}
static int tcc_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
*state)
{
u64 val;
int err;
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
if (err)
return err;
*state = (val & TCC_MASK) >> TCC_SHIFT;
return 0;
}
static int tcc_set_cur_state(struct thermal_cooling_device *cdev, unsigned long
state)
{
return tcc_offset_update(state);
}
static const struct thermal_cooling_device_ops tcc_cooling_ops = {
.get_max_state = tcc_get_max_state,
.get_cur_state = tcc_get_cur_state,
.set_cur_state = tcc_set_cur_state,
};
static const struct x86_cpu_id tcc_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL),
{}
};
MODULE_DEVICE_TABLE(x86cpu, tcc_ids);
static int __init tcc_cooling_init(void)
{
int ret;
u64 val;
const struct x86_cpu_id *id;
int err;
id = x86_match_cpu(tcc_ids);
if (!id)
return -ENODEV;
err = rdmsrl_safe(MSR_PLATFORM_INFO, &val);
if (err)
return err;
if (!(val & TCC_PROGRAMMABLE))
return -ENODEV;
pr_info("Programmable TCC Offset detected\n");
tcc_cdev =
thermal_cooling_device_register("TCC Offset", NULL,
&tcc_cooling_ops);
if (IS_ERR(tcc_cdev)) {
ret = PTR_ERR(tcc_cdev);
return ret;
}
return 0;
}
module_init(tcc_cooling_init)
static void __exit tcc_cooling_exit(void)
{
thermal_cooling_device_unregister(tcc_cdev);
}
module_exit(tcc_cooling_exit)
MODULE_DESCRIPTION("TCC offset cooling device Driver");
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_LICENSE("GPL v2");
|