1 /* 2 * via-cputemp.c - Driver for VIA CPU core temperature monitoring 3 * Copyright (C) 2009 VIA Technologies, Inc. 4 * 5 * based on existing coretemp.c, which is 6 * 7 * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; version 2 of the License. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 * 02110-1301 USA. 22 */ 23 24 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 25 26 #include <linux/module.h> 27 #include <linux/init.h> 28 #include <linux/slab.h> 29 #include <linux/hwmon.h> 30 #include <linux/sysfs.h> 31 #include <linux/hwmon-sysfs.h> 32 #include <linux/err.h> 33 #include <linux/mutex.h> 34 #include <linux/list.h> 35 #include <linux/platform_device.h> 36 #include <linux/cpu.h> 37 #include <asm/msr.h> 38 #include <asm/processor.h> 39 40 #define DRVNAME "via_cputemp" 41 42 enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME }; 43 44 /* 45 * Functions declaration 46 */ 47 48 struct via_cputemp_data { 49 struct device *hwmon_dev; 50 const char *name; 51 u32 id; 52 u32 msr; 53 }; 54 55 /* 56 * Sysfs stuff 57 */ 58 59 static ssize_t show_name(struct device *dev, struct device_attribute 60 *devattr, char *buf) 61 { 62 int ret; 63 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 64 struct via_cputemp_data *data = dev_get_drvdata(dev); 65 66 if (attr->index == SHOW_NAME) 67 ret = sprintf(buf, "%s\n", data->name); 68 else /* show label */ 69 ret = sprintf(buf, "Core %d\n", data->id); 70 return ret; 71 } 72 73 static ssize_t show_temp(struct device *dev, 74 struct device_attribute *devattr, char *buf) 75 { 76 struct via_cputemp_data *data = dev_get_drvdata(dev); 77 u32 eax, edx; 78 int err; 79 80 err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); 81 if (err) 82 return -EAGAIN; 83 84 return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000); 85 } 86 87 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 88 SHOW_TEMP); 89 static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); 90 static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME); 91 92 static struct attribute *via_cputemp_attributes[] = { 93 &sensor_dev_attr_name.dev_attr.attr, 94 &sensor_dev_attr_temp1_label.dev_attr.attr, 95 &sensor_dev_attr_temp1_input.dev_attr.attr, 96 NULL 97 }; 98 99 static const struct attribute_group via_cputemp_group = { 100 .attrs = via_cputemp_attributes, 101 }; 102 103 static int __devinit via_cputemp_probe(struct platform_device *pdev) 104 { 105 struct via_cputemp_data *data; 106 struct cpuinfo_x86 *c = &cpu_data(pdev->id); 107 int err; 108 u32 eax, edx; 109 110 data = kzalloc(sizeof(struct via_cputemp_data), GFP_KERNEL); 111 if (!data) { 112 err = -ENOMEM; 113 dev_err(&pdev->dev, "Out of memory\n"); 114 goto exit; 115 } 116 117 data->id = pdev->id; 118 data->name = "via_cputemp"; 119 120 switch (c->x86_model) { 121 case 0xA: 122 /* C7 A */ 123 case 0xD: 124 /* C7 D */ 125 data->msr = 0x1169; 126 break; 127 case 0xF: 128 /* Nano */ 129 data->msr = 0x1423; 130 break; 131 default: 132 err = -ENODEV; 133 goto exit_free; 134 } 135 136 /* test if we can access the TEMPERATURE MSR */ 137 err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); 138 if (err) { 139 dev_err(&pdev->dev, 140 "Unable to access TEMPERATURE MSR, giving up\n"); 141 goto exit_free; 142 } 143 144 platform_set_drvdata(pdev, data); 145 146 err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group); 147 if (err) 148 goto exit_free; 149 150 data->hwmon_dev = hwmon_device_register(&pdev->dev); 151 if (IS_ERR(data->hwmon_dev)) { 152 err = PTR_ERR(data->hwmon_dev); 153 dev_err(&pdev->dev, "Class registration failed (%d)\n", 154 err); 155 goto exit_remove; 156 } 157 158 return 0; 159 160 exit_remove: 161 sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); 162 exit_free: 163 platform_set_drvdata(pdev, NULL); 164 kfree(data); 165 exit: 166 return err; 167 } 168 169 static int __devexit via_cputemp_remove(struct platform_device *pdev) 170 { 171 struct via_cputemp_data *data = platform_get_drvdata(pdev); 172 173 hwmon_device_unregister(data->hwmon_dev); 174 sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); 175 platform_set_drvdata(pdev, NULL); 176 kfree(data); 177 return 0; 178 } 179 180 static struct platform_driver via_cputemp_driver = { 181 .driver = { 182 .owner = THIS_MODULE, 183 .name = DRVNAME, 184 }, 185 .probe = via_cputemp_probe, 186 .remove = __devexit_p(via_cputemp_remove), 187 }; 188 189 struct pdev_entry { 190 struct list_head list; 191 struct platform_device *pdev; 192 unsigned int cpu; 193 }; 194 195 static LIST_HEAD(pdev_list); 196 static DEFINE_MUTEX(pdev_list_mutex); 197 198 static int __cpuinit via_cputemp_device_add(unsigned int cpu) 199 { 200 int err; 201 struct platform_device *pdev; 202 struct pdev_entry *pdev_entry; 203 204 pdev = platform_device_alloc(DRVNAME, cpu); 205 if (!pdev) { 206 err = -ENOMEM; 207 pr_err("Device allocation failed\n"); 208 goto exit; 209 } 210 211 pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); 212 if (!pdev_entry) { 213 err = -ENOMEM; 214 goto exit_device_put; 215 } 216 217 err = platform_device_add(pdev); 218 if (err) { 219 pr_err("Device addition failed (%d)\n", err); 220 goto exit_device_free; 221 } 222 223 pdev_entry->pdev = pdev; 224 pdev_entry->cpu = cpu; 225 mutex_lock(&pdev_list_mutex); 226 list_add_tail(&pdev_entry->list, &pdev_list); 227 mutex_unlock(&pdev_list_mutex); 228 229 return 0; 230 231 exit_device_free: 232 kfree(pdev_entry); 233 exit_device_put: 234 platform_device_put(pdev); 235 exit: 236 return err; 237 } 238 239 static void __cpuinit via_cputemp_device_remove(unsigned int cpu) 240 { 241 struct pdev_entry *p; 242 243 mutex_lock(&pdev_list_mutex); 244 list_for_each_entry(p, &pdev_list, list) { 245 if (p->cpu == cpu) { 246 platform_device_unregister(p->pdev); 247 list_del(&p->list); 248 mutex_unlock(&pdev_list_mutex); 249 kfree(p); 250 return; 251 } 252 } 253 mutex_unlock(&pdev_list_mutex); 254 } 255 256 static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb, 257 unsigned long action, void *hcpu) 258 { 259 unsigned int cpu = (unsigned long) hcpu; 260 261 switch (action) { 262 case CPU_ONLINE: 263 case CPU_DOWN_FAILED: 264 via_cputemp_device_add(cpu); 265 break; 266 case CPU_DOWN_PREPARE: 267 via_cputemp_device_remove(cpu); 268 break; 269 } 270 return NOTIFY_OK; 271 } 272 273 static struct notifier_block via_cputemp_cpu_notifier __refdata = { 274 .notifier_call = via_cputemp_cpu_callback, 275 }; 276 277 static int __init via_cputemp_init(void) 278 { 279 int i, err; 280 281 if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) { 282 printk(KERN_DEBUG DRVNAME ": Not a VIA CPU\n"); 283 err = -ENODEV; 284 goto exit; 285 } 286 287 err = platform_driver_register(&via_cputemp_driver); 288 if (err) 289 goto exit; 290 291 for_each_online_cpu(i) { 292 struct cpuinfo_x86 *c = &cpu_data(i); 293 294 if (c->x86 != 6) 295 continue; 296 297 if (c->x86_model < 0x0a) 298 continue; 299 300 if (c->x86_model > 0x0f) { 301 pr_warn("Unknown CPU model 0x%x\n", c->x86_model); 302 continue; 303 } 304 305 via_cputemp_device_add(i); 306 } 307 308 #ifndef CONFIG_HOTPLUG_CPU 309 if (list_empty(&pdev_list)) { 310 err = -ENODEV; 311 goto exit_driver_unreg; 312 } 313 #endif 314 315 register_hotcpu_notifier(&via_cputemp_cpu_notifier); 316 return 0; 317 318 #ifndef CONFIG_HOTPLUG_CPU 319 exit_driver_unreg: 320 platform_driver_unregister(&via_cputemp_driver); 321 #endif 322 exit: 323 return err; 324 } 325 326 static void __exit via_cputemp_exit(void) 327 { 328 struct pdev_entry *p, *n; 329 330 unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); 331 mutex_lock(&pdev_list_mutex); 332 list_for_each_entry_safe(p, n, &pdev_list, list) { 333 platform_device_unregister(p->pdev); 334 list_del(&p->list); 335 kfree(p); 336 } 337 mutex_unlock(&pdev_list_mutex); 338 platform_driver_unregister(&via_cputemp_driver); 339 } 340 341 MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>"); 342 MODULE_DESCRIPTION("VIA CPU temperature monitor"); 343 MODULE_LICENSE("GPL"); 344 345 module_init(via_cputemp_init) 346 module_exit(via_cputemp_exit) 347