170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte/* 270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * via-cputemp.c - Driver for VIA CPU core temperature monitoring 370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * Copyright (C) 2009 VIA Technologies, Inc. 470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * 570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * based on existing coretemp.c, which is 670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * 770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz> 870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * 970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * This program is free software; you can redistribute it and/or modify 1070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * it under the terms of the GNU General Public License as published by 1170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * the Free Software Foundation; version 2 of the License. 1270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * 1370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * This program is distributed in the hope that it will be useful, 1470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * but WITHOUT ANY WARRANTY; without even the implied warranty of 1570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * GNU General Public License for more details. 1770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * 1870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * You should have received a copy of the GNU General Public License 1970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * along with this program; if not, write to the Free Software 2070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * 02110-1301 USA. 2270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte */ 2370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 24edb8d53c684da4062ac8c461727ddc1839336aa5Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 25edb8d53c684da4062ac8c461727ddc1839336aa5Joe Perches 2670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/module.h> 2770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/init.h> 2870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/slab.h> 2970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/hwmon.h> 30764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare#include <linux/hwmon-vid.h> 3170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/sysfs.h> 3270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/hwmon-sysfs.h> 3370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/err.h> 3470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/mutex.h> 3570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/list.h> 3670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/platform_device.h> 3770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <linux/cpu.h> 3870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <asm/msr.h> 3970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#include <asm/processor.h> 4070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 4170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte#define DRVNAME "via_cputemp" 4270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 43f279941863f0d39fa8285b84449b52b8286a254bH. Peter Anvinenum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME }; 4470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 4570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte/* 4670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * Functions declaration 4770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte */ 4870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 4970c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestruct via_cputemp_data { 5070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct device *hwmon_dev; 5170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte const char *name; 52764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare u8 vrm; 5370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte u32 id; 54764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare u32 msr_temp; 55764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare u32 msr_vid; 5670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte}; 5770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 5870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte/* 5970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte * Sysfs stuff 6070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte */ 6170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 6270c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic ssize_t show_name(struct device *dev, struct device_attribute 6370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte *devattr, char *buf) 6470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 6570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte int ret; 6670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 6770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct via_cputemp_data *data = dev_get_drvdata(dev); 6870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 6970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (attr->index == SHOW_NAME) 7070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte ret = sprintf(buf, "%s\n", data->name); 7170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte else /* show label */ 7270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte ret = sprintf(buf, "Core %d\n", data->id); 7370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return ret; 7470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 7570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 7670c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic ssize_t show_temp(struct device *dev, 7770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct device_attribute *devattr, char *buf) 7870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 7970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct via_cputemp_data *data = dev_get_drvdata(dev); 8070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte u32 eax, edx; 8170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte int err; 8270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 83764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); 8470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (err) 8570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return -EAGAIN; 8670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 8770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000); 8870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 8970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 90764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvarestatic ssize_t show_cpu_vid(struct device *dev, 91764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare struct device_attribute *devattr, char *buf) 92764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare{ 93764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare struct via_cputemp_data *data = dev_get_drvdata(dev); 94764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare u32 eax, edx; 95764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare int err; 96764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare 97764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx); 98764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare if (err) 99764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare return -EAGAIN; 100764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare 101764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm)); 102764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare} 103764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare 10470c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 10570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte SHOW_TEMP); 10670c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); 10770c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME); 10870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 10970c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic struct attribute *via_cputemp_attributes[] = { 11070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte &sensor_dev_attr_name.dev_attr.attr, 11170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte &sensor_dev_attr_temp1_label.dev_attr.attr, 11270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte &sensor_dev_attr_temp1_input.dev_attr.attr, 11370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte NULL 11470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte}; 11570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 11670c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic const struct attribute_group via_cputemp_group = { 11770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte .attrs = via_cputemp_attributes, 11870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte}; 11970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 120764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare/* Optional attributes */ 121764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvarestatic DEVICE_ATTR(cpu0_vid, S_IRUGO, show_cpu_vid, NULL); 122764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare 12370c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic int __devinit via_cputemp_probe(struct platform_device *pdev) 12470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 12570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct via_cputemp_data *data; 12670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct cpuinfo_x86 *c = &cpu_data(pdev->id); 12770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte int err; 12870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte u32 eax, edx; 12970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 13070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte data = kzalloc(sizeof(struct via_cputemp_data), GFP_KERNEL); 13170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (!data) { 13270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = -ENOMEM; 13370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte dev_err(&pdev->dev, "Out of memory\n"); 13470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit; 13570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 13670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 13770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte data->id = pdev->id; 13870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte data->name = "via_cputemp"; 13970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 14070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte switch (c->x86_model) { 14170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte case 0xA: 14270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte /* C7 A */ 14370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte case 0xD: 14470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte /* C7 D */ 145764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare data->msr_temp = 0x1169; 146764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare data->msr_vid = 0x198; 14770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte break; 14870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte case 0xF: 14970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte /* Nano */ 150764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare data->msr_temp = 0x1423; 15170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte break; 15270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte default: 15370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = -ENODEV; 15470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit_free; 15570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 15670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 15770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte /* test if we can access the TEMPERATURE MSR */ 158764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); 15970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (err) { 16070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte dev_err(&pdev->dev, 16170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte "Unable to access TEMPERATURE MSR, giving up\n"); 16270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit_free; 16370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 16470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 16570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte platform_set_drvdata(pdev, data); 16670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 16770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group); 16870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (err) 16970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit_free; 17070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 171764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare if (data->msr_vid) 172764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare data->vrm = vid_which_vrm(); 173764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare 174764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare if (data->vrm) { 175764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid); 176764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare if (err) 177764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare goto exit_remove; 178764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare } 179764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare 18070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte data->hwmon_dev = hwmon_device_register(&pdev->dev); 18170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (IS_ERR(data->hwmon_dev)) { 18270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = PTR_ERR(data->hwmon_dev); 18370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte dev_err(&pdev->dev, "Class registration failed (%d)\n", 18470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err); 18570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit_remove; 18670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 18770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 18870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return 0; 18970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 19070c38772aef27b01dc236fb4016261c3828df6aaHarald Welteexit_remove: 191764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare if (data->vrm) 192764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); 19370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); 19470c38772aef27b01dc236fb4016261c3828df6aaHarald Welteexit_free: 19570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte platform_set_drvdata(pdev, NULL); 19670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte kfree(data); 19770c38772aef27b01dc236fb4016261c3828df6aaHarald Welteexit: 19870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return err; 19970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 20070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 20170c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic int __devexit via_cputemp_remove(struct platform_device *pdev) 20270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 20370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct via_cputemp_data *data = platform_get_drvdata(pdev); 20470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 20570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte hwmon_device_unregister(data->hwmon_dev); 206764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare if (data->vrm) 207764e043bb48b6b94f9dec228aedbd8ab08f4708bJean Delvare device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); 20870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); 20970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte platform_set_drvdata(pdev, NULL); 21070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte kfree(data); 21170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return 0; 21270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 21370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 21470c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic struct platform_driver via_cputemp_driver = { 21570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte .driver = { 21670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte .owner = THIS_MODULE, 21770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte .name = DRVNAME, 21870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte }, 21970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte .probe = via_cputemp_probe, 22070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte .remove = __devexit_p(via_cputemp_remove), 22170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte}; 22270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 22370c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestruct pdev_entry { 22470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct list_head list; 22570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct platform_device *pdev; 22670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte unsigned int cpu; 22770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte}; 22870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 22970c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic LIST_HEAD(pdev_list); 23070c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic DEFINE_MUTEX(pdev_list_mutex); 23170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 23270c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic int __cpuinit via_cputemp_device_add(unsigned int cpu) 23370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 23470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte int err; 23570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct platform_device *pdev; 23670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct pdev_entry *pdev_entry; 23770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 23870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte pdev = platform_device_alloc(DRVNAME, cpu); 23970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (!pdev) { 24070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = -ENOMEM; 241edb8d53c684da4062ac8c461727ddc1839336aa5Joe Perches pr_err("Device allocation failed\n"); 24270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit; 24370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 24470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 24570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); 24670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (!pdev_entry) { 24770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = -ENOMEM; 24870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit_device_put; 24970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 25070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 25170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = platform_device_add(pdev); 25270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (err) { 253edb8d53c684da4062ac8c461727ddc1839336aa5Joe Perches pr_err("Device addition failed (%d)\n", err); 25470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit_device_free; 25570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 25670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 25770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte pdev_entry->pdev = pdev; 25870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte pdev_entry->cpu = cpu; 25970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte mutex_lock(&pdev_list_mutex); 26070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte list_add_tail(&pdev_entry->list, &pdev_list); 26170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte mutex_unlock(&pdev_list_mutex); 26270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 26370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return 0; 26470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 26570c38772aef27b01dc236fb4016261c3828df6aaHarald Welteexit_device_free: 26670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte kfree(pdev_entry); 26770c38772aef27b01dc236fb4016261c3828df6aaHarald Welteexit_device_put: 26870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte platform_device_put(pdev); 26970c38772aef27b01dc236fb4016261c3828df6aaHarald Welteexit: 27070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return err; 27170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 27270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 273a5f42a6bc51454137b918f67310168c27d1dd1deJan Beulichstatic void __cpuinit via_cputemp_device_remove(unsigned int cpu) 27470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 275ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich struct pdev_entry *p; 276ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich 27770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte mutex_lock(&pdev_list_mutex); 278ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich list_for_each_entry(p, &pdev_list, list) { 27970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (p->cpu == cpu) { 28070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte platform_device_unregister(p->pdev); 28170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte list_del(&p->list); 282ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich mutex_unlock(&pdev_list_mutex); 28370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte kfree(p); 284ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich return; 28570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 28670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 28770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte mutex_unlock(&pdev_list_mutex); 28870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 28970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 29070c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb, 29170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte unsigned long action, void *hcpu) 29270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 29370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte unsigned int cpu = (unsigned long) hcpu; 29470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 29570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte switch (action) { 29670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte case CPU_ONLINE: 29770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte case CPU_DOWN_FAILED: 29870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte via_cputemp_device_add(cpu); 29970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte break; 30070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte case CPU_DOWN_PREPARE: 30170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte via_cputemp_device_remove(cpu); 30270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte break; 30370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 30470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return NOTIFY_OK; 30570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 30670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 30770c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic struct notifier_block via_cputemp_cpu_notifier __refdata = { 30870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte .notifier_call = via_cputemp_cpu_callback, 30970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte}; 31070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 31170c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic int __init via_cputemp_init(void) 31270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 31370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte int i, err; 31470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 31570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) { 31670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte printk(KERN_DEBUG DRVNAME ": Not a VIA CPU\n"); 31770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = -ENODEV; 31870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit; 31970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 32070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 32170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = platform_driver_register(&via_cputemp_driver); 32270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (err) 32370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit; 32470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 32570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte for_each_online_cpu(i) { 32670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct cpuinfo_x86 *c = &cpu_data(i); 32770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 32870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (c->x86 != 6) 32970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte continue; 33070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 33170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (c->x86_model < 0x0a) 33270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte continue; 33370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 33470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (c->x86_model > 0x0f) { 335edb8d53c684da4062ac8c461727ddc1839336aa5Joe Perches pr_warn("Unknown CPU model 0x%x\n", c->x86_model); 33670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte continue; 33770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 33870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 339ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich via_cputemp_device_add(i); 34070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 341ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich 342ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich#ifndef CONFIG_HOTPLUG_CPU 34370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte if (list_empty(&pdev_list)) { 34470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte err = -ENODEV; 34570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte goto exit_driver_unreg; 34670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 347ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich#endif 34870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 34970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte register_hotcpu_notifier(&via_cputemp_cpu_notifier); 35070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return 0; 35170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 352ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich#ifndef CONFIG_HOTPLUG_CPU 35370c38772aef27b01dc236fb4016261c3828df6aaHarald Welteexit_driver_unreg: 35470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte platform_driver_unregister(&via_cputemp_driver); 355ae9e0ce73e9fc55a620e0b3bd4a154330a7e64ccJan Beulich#endif 35670c38772aef27b01dc236fb4016261c3828df6aaHarald Welteexit: 35770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte return err; 35870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 35970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 36070c38772aef27b01dc236fb4016261c3828df6aaHarald Weltestatic void __exit via_cputemp_exit(void) 36170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte{ 36270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte struct pdev_entry *p, *n; 36317c10d61c750619324ee2a46c5a9e03a435fe212Chen Gong 36470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); 36570c38772aef27b01dc236fb4016261c3828df6aaHarald Welte mutex_lock(&pdev_list_mutex); 36670c38772aef27b01dc236fb4016261c3828df6aaHarald Welte list_for_each_entry_safe(p, n, &pdev_list, list) { 36770c38772aef27b01dc236fb4016261c3828df6aaHarald Welte platform_device_unregister(p->pdev); 36870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte list_del(&p->list); 36970c38772aef27b01dc236fb4016261c3828df6aaHarald Welte kfree(p); 37070c38772aef27b01dc236fb4016261c3828df6aaHarald Welte } 37170c38772aef27b01dc236fb4016261c3828df6aaHarald Welte mutex_unlock(&pdev_list_mutex); 37270c38772aef27b01dc236fb4016261c3828df6aaHarald Welte platform_driver_unregister(&via_cputemp_driver); 37370c38772aef27b01dc236fb4016261c3828df6aaHarald Welte} 37470c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 37570c38772aef27b01dc236fb4016261c3828df6aaHarald WelteMODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>"); 37670c38772aef27b01dc236fb4016261c3828df6aaHarald WelteMODULE_DESCRIPTION("VIA CPU temperature monitor"); 37770c38772aef27b01dc236fb4016261c3828df6aaHarald WelteMODULE_LICENSE("GPL"); 37870c38772aef27b01dc236fb4016261c3828df6aaHarald Welte 37970c38772aef27b01dc236fb4016261c3828df6aaHarald Weltemodule_init(via_cputemp_init) 38070c38772aef27b01dc236fb4016261c3828df6aaHarald Weltemodule_exit(via_cputemp_exit) 381