12eb87d75SZhang Rui // SPDX-License-Identifier: GPL-2.0-only
22eb87d75SZhang Rui /*
32eb87d75SZhang Rui  * cooling device driver that activates the processor throttling by
42eb87d75SZhang Rui  * programming the TCC Offset register.
52eb87d75SZhang Rui  * Copyright (c) 2021, Intel Corporation.
62eb87d75SZhang Rui  */
72eb87d75SZhang Rui #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
82eb87d75SZhang Rui 
92eb87d75SZhang Rui #include <linux/device.h>
104e3ecc28SZhang Rui #include <linux/intel_tcc.h>
112eb87d75SZhang Rui #include <linux/module.h>
122eb87d75SZhang Rui #include <linux/thermal.h>
132eb87d75SZhang Rui #include <asm/cpu_device_id.h>
142eb87d75SZhang Rui 
152eb87d75SZhang Rui #define TCC_PROGRAMMABLE	BIT(30)
16be6abd3eSZhang Rui #define TCC_LOCKED		BIT(31)
172eb87d75SZhang Rui 
182eb87d75SZhang Rui static struct thermal_cooling_device *tcc_cdev;
192eb87d75SZhang Rui 
tcc_get_max_state(struct thermal_cooling_device * cdev,unsigned long * state)202eb87d75SZhang Rui static int tcc_get_max_state(struct thermal_cooling_device *cdev, unsigned long
212eb87d75SZhang Rui 			     *state)
222eb87d75SZhang Rui {
234e3ecc28SZhang Rui 	*state = 0x3f;
242eb87d75SZhang Rui 	return 0;
252eb87d75SZhang Rui }
262eb87d75SZhang Rui 
tcc_get_cur_state(struct thermal_cooling_device * cdev,unsigned long * state)272eb87d75SZhang Rui static int tcc_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
282eb87d75SZhang Rui 			     *state)
292eb87d75SZhang Rui {
304e3ecc28SZhang Rui 	int offset = intel_tcc_get_offset(-1);
312eb87d75SZhang Rui 
324e3ecc28SZhang Rui 	if (offset < 0)
334e3ecc28SZhang Rui 		return offset;
342eb87d75SZhang Rui 
354e3ecc28SZhang Rui 	*state = offset;
362eb87d75SZhang Rui 	return 0;
372eb87d75SZhang Rui }
382eb87d75SZhang Rui 
tcc_set_cur_state(struct thermal_cooling_device * cdev,unsigned long state)392eb87d75SZhang Rui static int tcc_set_cur_state(struct thermal_cooling_device *cdev, unsigned long
402eb87d75SZhang Rui 			     state)
412eb87d75SZhang Rui {
424e3ecc28SZhang Rui 	return intel_tcc_set_offset(-1, (int)state);
432eb87d75SZhang Rui }
442eb87d75SZhang Rui 
452eb87d75SZhang Rui static const struct thermal_cooling_device_ops tcc_cooling_ops = {
462eb87d75SZhang Rui 	.get_max_state = tcc_get_max_state,
472eb87d75SZhang Rui 	.get_cur_state = tcc_get_cur_state,
482eb87d75SZhang Rui 	.set_cur_state = tcc_set_cur_state,
492eb87d75SZhang Rui };
502eb87d75SZhang Rui 
512eb87d75SZhang Rui static const struct x86_cpu_id tcc_ids[] __initconst = {
522eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, NULL),
532eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, NULL),
542eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, NULL),
552eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, NULL),
562eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, NULL),
572eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, NULL),
582eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL),
592eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL),
602eb87d75SZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL),
61a414a08aSSumeet Pawnikar 	X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL),
62a414a08aSSumeet Pawnikar 	X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL),
63*882cdb06SPeter Zijlstra 	X86_MATCH_INTEL_FAM6_MODEL(ATOM_GRACEMONT, NULL),
6462f46fc7SSumeet Pawnikar 	X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL),
65312c1a44SSumeet Pawnikar 	X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL),
66e77f069fSZhang Rui 	X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL),
672eb87d75SZhang Rui 	{}
682eb87d75SZhang Rui };
692eb87d75SZhang Rui 
702eb87d75SZhang Rui MODULE_DEVICE_TABLE(x86cpu, tcc_ids);
712eb87d75SZhang Rui 
tcc_cooling_init(void)722eb87d75SZhang Rui static int __init tcc_cooling_init(void)
732eb87d75SZhang Rui {
742eb87d75SZhang Rui 	int ret;
752eb87d75SZhang Rui 	u64 val;
762eb87d75SZhang Rui 	const struct x86_cpu_id *id;
772eb87d75SZhang Rui 
782eb87d75SZhang Rui 	int err;
792eb87d75SZhang Rui 
802eb87d75SZhang Rui 	id = x86_match_cpu(tcc_ids);
812eb87d75SZhang Rui 	if (!id)
822eb87d75SZhang Rui 		return -ENODEV;
832eb87d75SZhang Rui 
842eb87d75SZhang Rui 	err = rdmsrl_safe(MSR_PLATFORM_INFO, &val);
852eb87d75SZhang Rui 	if (err)
862eb87d75SZhang Rui 		return err;
872eb87d75SZhang Rui 
882eb87d75SZhang Rui 	if (!(val & TCC_PROGRAMMABLE))
892eb87d75SZhang Rui 		return -ENODEV;
902eb87d75SZhang Rui 
91be6abd3eSZhang Rui 	err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
92be6abd3eSZhang Rui 	if (err)
93be6abd3eSZhang Rui 		return err;
94be6abd3eSZhang Rui 
95be6abd3eSZhang Rui 	if (val & TCC_LOCKED) {
96be6abd3eSZhang Rui 		pr_info("TCC Offset locked\n");
97be6abd3eSZhang Rui 		return -ENODEV;
98be6abd3eSZhang Rui 	}
99be6abd3eSZhang Rui 
1002eb87d75SZhang Rui 	pr_info("Programmable TCC Offset detected\n");
1012eb87d75SZhang Rui 
1022eb87d75SZhang Rui 	tcc_cdev =
1032eb87d75SZhang Rui 	    thermal_cooling_device_register("TCC Offset", NULL,
1042eb87d75SZhang Rui 					    &tcc_cooling_ops);
1052eb87d75SZhang Rui 	if (IS_ERR(tcc_cdev)) {
1062eb87d75SZhang Rui 		ret = PTR_ERR(tcc_cdev);
1072eb87d75SZhang Rui 		return ret;
1082eb87d75SZhang Rui 	}
1092eb87d75SZhang Rui 	return 0;
1102eb87d75SZhang Rui }
1112eb87d75SZhang Rui 
module_init(tcc_cooling_init)1122eb87d75SZhang Rui module_init(tcc_cooling_init)
1132eb87d75SZhang Rui 
1142eb87d75SZhang Rui static void __exit tcc_cooling_exit(void)
1152eb87d75SZhang Rui {
1162eb87d75SZhang Rui 	thermal_cooling_device_unregister(tcc_cdev);
1172eb87d75SZhang Rui }
1182eb87d75SZhang Rui 
1192eb87d75SZhang Rui module_exit(tcc_cooling_exit)
1202eb87d75SZhang Rui 
1214e3ecc28SZhang Rui MODULE_IMPORT_NS(INTEL_TCC);
1222eb87d75SZhang Rui MODULE_DESCRIPTION("TCC offset cooling device Driver");
1232eb87d75SZhang Rui MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
1242eb87d75SZhang Rui MODULE_LICENSE("GPL v2");
125