cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

via-cputemp.c (7275B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * via-cputemp.c - Driver for VIA CPU core temperature monitoring
      4 * Copyright (C) 2009 VIA Technologies, Inc.
      5 *
      6 * based on existing coretemp.c, which is
      7 *
      8 * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>
      9 */
     10
     11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     12
     13#include <linux/module.h>
     14#include <linux/init.h>
     15#include <linux/slab.h>
     16#include <linux/hwmon.h>
     17#include <linux/hwmon-vid.h>
     18#include <linux/sysfs.h>
     19#include <linux/hwmon-sysfs.h>
     20#include <linux/err.h>
     21#include <linux/mutex.h>
     22#include <linux/list.h>
     23#include <linux/platform_device.h>
     24#include <linux/cpu.h>
     25#include <asm/msr.h>
     26#include <asm/processor.h>
     27#include <asm/cpu_device_id.h>
     28
     29#define DRVNAME	"via_cputemp"
     30
     31enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };
     32
     33/*
     34 * Functions declaration
     35 */
     36
     37struct via_cputemp_data {
     38	struct device *hwmon_dev;
     39	const char *name;
     40	u8 vrm;
     41	u32 id;
     42	u32 msr_temp;
     43	u32 msr_vid;
     44};
     45
     46/*
     47 * Sysfs stuff
     48 */
     49
     50static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
     51			 char *buf)
     52{
     53	int ret;
     54	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
     55	struct via_cputemp_data *data = dev_get_drvdata(dev);
     56
     57	if (attr->index == SHOW_NAME)
     58		ret = sprintf(buf, "%s\n", data->name);
     59	else	/* show label */
     60		ret = sprintf(buf, "Core %d\n", data->id);
     61	return ret;
     62}
     63
     64static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
     65			 char *buf)
     66{
     67	struct via_cputemp_data *data = dev_get_drvdata(dev);
     68	u32 eax, edx;
     69	int err;
     70
     71	err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
     72	if (err)
     73		return -EAGAIN;
     74
     75	return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000);
     76}
     77
     78static ssize_t cpu0_vid_show(struct device *dev,
     79			     struct device_attribute *devattr, char *buf)
     80{
     81	struct via_cputemp_data *data = dev_get_drvdata(dev);
     82	u32 eax, edx;
     83	int err;
     84
     85	err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx);
     86	if (err)
     87		return -EAGAIN;
     88
     89	return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm));
     90}
     91
     92static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, SHOW_TEMP);
     93static SENSOR_DEVICE_ATTR_RO(temp1_label, name, SHOW_LABEL);
     94static SENSOR_DEVICE_ATTR_RO(name, name, SHOW_NAME);
     95
     96static struct attribute *via_cputemp_attributes[] = {
     97	&sensor_dev_attr_name.dev_attr.attr,
     98	&sensor_dev_attr_temp1_label.dev_attr.attr,
     99	&sensor_dev_attr_temp1_input.dev_attr.attr,
    100	NULL
    101};
    102
    103static const struct attribute_group via_cputemp_group = {
    104	.attrs = via_cputemp_attributes,
    105};
    106
    107/* Optional attributes */
    108static DEVICE_ATTR_RO(cpu0_vid);
    109
    110static int via_cputemp_probe(struct platform_device *pdev)
    111{
    112	struct via_cputemp_data *data;
    113	struct cpuinfo_x86 *c = &cpu_data(pdev->id);
    114	int err;
    115	u32 eax, edx;
    116
    117	data = devm_kzalloc(&pdev->dev, sizeof(struct via_cputemp_data),
    118			    GFP_KERNEL);
    119	if (!data)
    120		return -ENOMEM;
    121
    122	data->id = pdev->id;
    123	data->name = "via_cputemp";
    124
    125	if (c->x86 == 7) {
    126		data->msr_temp = 0x1423;
    127	} else {
    128		switch (c->x86_model) {
    129		case 0xA:
    130			/* C7 A */
    131		case 0xD:
    132			/* C7 D */
    133			data->msr_temp = 0x1169;
    134			data->msr_vid = 0x198;
    135			break;
    136		case 0xF:
    137			/* Nano */
    138			data->msr_temp = 0x1423;
    139			break;
    140		default:
    141			return -ENODEV;
    142		}
    143	}
    144
    145	/* test if we can access the TEMPERATURE MSR */
    146	err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
    147	if (err) {
    148		dev_err(&pdev->dev,
    149			"Unable to access TEMPERATURE MSR, giving up\n");
    150		return err;
    151	}
    152
    153	platform_set_drvdata(pdev, data);
    154
    155	err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group);
    156	if (err)
    157		return err;
    158
    159	if (data->msr_vid)
    160		data->vrm = vid_which_vrm();
    161
    162	if (data->vrm) {
    163		err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid);
    164		if (err)
    165			goto exit_remove;
    166	}
    167
    168	data->hwmon_dev = hwmon_device_register(&pdev->dev);
    169	if (IS_ERR(data->hwmon_dev)) {
    170		err = PTR_ERR(data->hwmon_dev);
    171		dev_err(&pdev->dev, "Class registration failed (%d)\n",
    172			err);
    173		goto exit_remove;
    174	}
    175
    176	return 0;
    177
    178exit_remove:
    179	if (data->vrm)
    180		device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
    181	sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
    182	return err;
    183}
    184
    185static int via_cputemp_remove(struct platform_device *pdev)
    186{
    187	struct via_cputemp_data *data = platform_get_drvdata(pdev);
    188
    189	hwmon_device_unregister(data->hwmon_dev);
    190	if (data->vrm)
    191		device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
    192	sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
    193	return 0;
    194}
    195
    196static struct platform_driver via_cputemp_driver = {
    197	.driver = {
    198		.name = DRVNAME,
    199	},
    200	.probe = via_cputemp_probe,
    201	.remove = via_cputemp_remove,
    202};
    203
    204struct pdev_entry {
    205	struct list_head list;
    206	struct platform_device *pdev;
    207	unsigned int cpu;
    208};
    209
    210static LIST_HEAD(pdev_list);
    211static DEFINE_MUTEX(pdev_list_mutex);
    212
    213static int via_cputemp_online(unsigned int cpu)
    214{
    215	int err;
    216	struct platform_device *pdev;
    217	struct pdev_entry *pdev_entry;
    218
    219	pdev = platform_device_alloc(DRVNAME, cpu);
    220	if (!pdev) {
    221		err = -ENOMEM;
    222		pr_err("Device allocation failed\n");
    223		goto exit;
    224	}
    225
    226	pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
    227	if (!pdev_entry) {
    228		err = -ENOMEM;
    229		goto exit_device_put;
    230	}
    231
    232	err = platform_device_add(pdev);
    233	if (err) {
    234		pr_err("Device addition failed (%d)\n", err);
    235		goto exit_device_free;
    236	}
    237
    238	pdev_entry->pdev = pdev;
    239	pdev_entry->cpu = cpu;
    240	mutex_lock(&pdev_list_mutex);
    241	list_add_tail(&pdev_entry->list, &pdev_list);
    242	mutex_unlock(&pdev_list_mutex);
    243
    244	return 0;
    245
    246exit_device_free:
    247	kfree(pdev_entry);
    248exit_device_put:
    249	platform_device_put(pdev);
    250exit:
    251	return err;
    252}
    253
    254static int via_cputemp_down_prep(unsigned int cpu)
    255{
    256	struct pdev_entry *p;
    257
    258	mutex_lock(&pdev_list_mutex);
    259	list_for_each_entry(p, &pdev_list, list) {
    260		if (p->cpu == cpu) {
    261			platform_device_unregister(p->pdev);
    262			list_del(&p->list);
    263			mutex_unlock(&pdev_list_mutex);
    264			kfree(p);
    265			return 0;
    266		}
    267	}
    268	mutex_unlock(&pdev_list_mutex);
    269	return 0;
    270}
    271
    272static const struct x86_cpu_id __initconst cputemp_ids[] = {
    273	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_A,	NULL),
    274	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_D,	NULL),
    275	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_NANO,	NULL),
    276	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 7, X86_MODEL_ANY,		NULL),
    277	{}
    278};
    279MODULE_DEVICE_TABLE(x86cpu, cputemp_ids);
    280
    281static enum cpuhp_state via_temp_online;
    282
    283static int __init via_cputemp_init(void)
    284{
    285	int err;
    286
    287	if (!x86_match_cpu(cputemp_ids))
    288		return -ENODEV;
    289
    290	err = platform_driver_register(&via_cputemp_driver);
    291	if (err)
    292		goto exit;
    293
    294	err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online",
    295				via_cputemp_online, via_cputemp_down_prep);
    296	if (err < 0)
    297		goto exit_driver_unreg;
    298	via_temp_online = err;
    299
    300#ifndef CONFIG_HOTPLUG_CPU
    301	if (list_empty(&pdev_list)) {
    302		err = -ENODEV;
    303		goto exit_hp_unreg;
    304	}
    305#endif
    306	return 0;
    307
    308#ifndef CONFIG_HOTPLUG_CPU
    309exit_hp_unreg:
    310	cpuhp_remove_state_nocalls(via_temp_online);
    311#endif
    312exit_driver_unreg:
    313	platform_driver_unregister(&via_cputemp_driver);
    314exit:
    315	return err;
    316}
    317
    318static void __exit via_cputemp_exit(void)
    319{
    320	cpuhp_remove_state(via_temp_online);
    321	platform_driver_unregister(&via_cputemp_driver);
    322}
    323
    324MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
    325MODULE_DESCRIPTION("VIA CPU temperature monitor");
    326MODULE_LICENSE("GPL");
    327
    328module_init(via_cputemp_init)
    329module_exit(via_cputemp_exit)