brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · 5abe95b Raw
329 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * via-cputemp.c - Driver for VIA CPU core temperature monitoring4 * Copyright (C) 2009 VIA Technologies, Inc.5 *6 * based on existing coretemp.c, which is7 *8 * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>9 */10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt12 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 declaration35 */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 stuff48 */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	NULL101};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 void 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}194 195static struct platform_driver via_cputemp_driver = {196	.driver = {197		.name = DRVNAME,198	},199	.probe = via_cputemp_probe,200	.remove_new = via_cputemp_remove,201};202 203struct pdev_entry {204	struct list_head list;205	struct platform_device *pdev;206	unsigned int cpu;207};208 209static LIST_HEAD(pdev_list);210static DEFINE_MUTEX(pdev_list_mutex);211 212static int via_cputemp_online(unsigned int cpu)213{214	int err;215	struct platform_device *pdev;216	struct pdev_entry *pdev_entry;217 218	pdev = platform_device_alloc(DRVNAME, cpu);219	if (!pdev) {220		err = -ENOMEM;221		pr_err("Device allocation failed\n");222		goto exit;223	}224 225	pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);226	if (!pdev_entry) {227		err = -ENOMEM;228		goto exit_device_put;229	}230 231	err = platform_device_add(pdev);232	if (err) {233		pr_err("Device addition failed (%d)\n", err);234		goto exit_device_free;235	}236 237	pdev_entry->pdev = pdev;238	pdev_entry->cpu = cpu;239	mutex_lock(&pdev_list_mutex);240	list_add_tail(&pdev_entry->list, &pdev_list);241	mutex_unlock(&pdev_list_mutex);242 243	return 0;244 245exit_device_free:246	kfree(pdev_entry);247exit_device_put:248	platform_device_put(pdev);249exit:250	return err;251}252 253static int via_cputemp_down_prep(unsigned int cpu)254{255	struct pdev_entry *p;256 257	mutex_lock(&pdev_list_mutex);258	list_for_each_entry(p, &pdev_list, list) {259		if (p->cpu == cpu) {260			platform_device_unregister(p->pdev);261			list_del(&p->list);262			mutex_unlock(&pdev_list_mutex);263			kfree(p);264			return 0;265		}266	}267	mutex_unlock(&pdev_list_mutex);268	return 0;269}270 271static const struct x86_cpu_id __initconst cputemp_ids[] = {272	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_A,	NULL),273	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_D,	NULL),274	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_NANO,	NULL),275	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 7, X86_MODEL_ANY,		NULL),276	{}277};278MODULE_DEVICE_TABLE(x86cpu, cputemp_ids);279 280static enum cpuhp_state via_temp_online;281 282static int __init via_cputemp_init(void)283{284	int err;285 286	if (!x86_match_cpu(cputemp_ids))287		return -ENODEV;288 289	err = platform_driver_register(&via_cputemp_driver);290	if (err)291		goto exit;292 293	err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online",294				via_cputemp_online, via_cputemp_down_prep);295	if (err < 0)296		goto exit_driver_unreg;297	via_temp_online = err;298 299#ifndef CONFIG_HOTPLUG_CPU300	if (list_empty(&pdev_list)) {301		err = -ENODEV;302		goto exit_hp_unreg;303	}304#endif305	return 0;306 307#ifndef CONFIG_HOTPLUG_CPU308exit_hp_unreg:309	cpuhp_remove_state_nocalls(via_temp_online);310#endif311exit_driver_unreg:312	platform_driver_unregister(&via_cputemp_driver);313exit:314	return err;315}316 317static void __exit via_cputemp_exit(void)318{319	cpuhp_remove_state(via_temp_online);320	platform_driver_unregister(&via_cputemp_driver);321}322 323MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");324MODULE_DESCRIPTION("VIA CPU temperature monitor");325MODULE_LICENSE("GPL");326 327module_init(via_cputemp_init)328module_exit(via_cputemp_exit)329