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 #include <linux/module.h> 25 #include <linux/delay.h> 26 #include <linux/init.h> 27 #include <linux/slab.h> 28 #include <linux/jiffies.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 } SHOW; 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 printk(KERN_ERR DRVNAME ": 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 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", 220 err); 221 goto exit_device_free; 222 } 223 224 pdev_entry->pdev = pdev; 225 pdev_entry->cpu = cpu; 226 mutex_lock(&pdev_list_mutex); 227 list_add_tail(&pdev_entry->list, &pdev_list); 228 mutex_unlock(&pdev_list_mutex); 229 230 return 0; 231 232 exit_device_free: 233 kfree(pdev_entry); 234 exit_device_put: 235 platform_device_put(pdev); 236 exit: 237 return err; 238 } 239 240 #ifdef CONFIG_HOTPLUG_CPU 241 static void via_cputemp_device_remove(unsigned int cpu) 242 { 243 struct pdev_entry *p, *n; 244 mutex_lock(&pdev_list_mutex); 245 list_for_each_entry_safe(p, n, &pdev_list, list) { 246 if (p->cpu == cpu) { 247 platform_device_unregister(p->pdev); 248 list_del(&p->list); 249 kfree(p); 250 } 251 } 252 mutex_unlock(&pdev_list_mutex); 253 } 254 255 static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb, 256 unsigned long action, void *hcpu) 257 { 258 unsigned int cpu = (unsigned long) hcpu; 259 260 switch (action) { 261 case CPU_ONLINE: 262 case CPU_DOWN_FAILED: 263 via_cputemp_device_add(cpu); 264 break; 265 case CPU_DOWN_PREPARE: 266 via_cputemp_device_remove(cpu); 267 break; 268 } 269 return NOTIFY_OK; 270 } 271 272 static struct notifier_block via_cputemp_cpu_notifier __refdata = { 273 .notifier_call = via_cputemp_cpu_callback, 274 }; 275 #endif /* !CONFIG_HOTPLUG_CPU */ 276 277 static int __init via_cputemp_init(void) 278 { 279 int i, err; 280 struct pdev_entry *p, *n; 281 282 if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) { 283 printk(KERN_DEBUG DRVNAME ": Not a VIA CPU\n"); 284 err = -ENODEV; 285 goto exit; 286 } 287 288 err = platform_driver_register(&via_cputemp_driver); 289 if (err) 290 goto exit; 291 292 for_each_online_cpu(i) { 293 struct cpuinfo_x86 *c = &cpu_data(i); 294 295 if (c->x86 != 6) 296 continue; 297 298 if (c->x86_model < 0x0a) 299 continue; 300 301 if (c->x86_model > 0x0f) { 302 printk(KERN_WARNING DRVNAME ": Unknown CPU " 303 "model 0x%x\n", c->x86_model); 304 continue; 305 } 306 307 err = via_cputemp_device_add(i); 308 if (err) 309 goto exit_devices_unreg; 310 } 311 if (list_empty(&pdev_list)) { 312 err = -ENODEV; 313 goto exit_driver_unreg; 314 } 315 316 #ifdef CONFIG_HOTPLUG_CPU 317 register_hotcpu_notifier(&via_cputemp_cpu_notifier); 318 #endif 319 return 0; 320 321 exit_devices_unreg: 322 mutex_lock(&pdev_list_mutex); 323 list_for_each_entry_safe(p, n, &pdev_list, list) { 324 platform_device_unregister(p->pdev); 325 list_del(&p->list); 326 kfree(p); 327 } 328 mutex_unlock(&pdev_list_mutex); 329 exit_driver_unreg: 330 platform_driver_unregister(&via_cputemp_driver); 331 exit: 332 return err; 333 } 334 335 static void __exit via_cputemp_exit(void) 336 { 337 struct pdev_entry *p, *n; 338 #ifdef CONFIG_HOTPLUG_CPU 339 unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); 340 #endif 341 mutex_lock(&pdev_list_mutex); 342 list_for_each_entry_safe(p, n, &pdev_list, list) { 343 platform_device_unregister(p->pdev); 344 list_del(&p->list); 345 kfree(p); 346 } 347 mutex_unlock(&pdev_list_mutex); 348 platform_driver_unregister(&via_cputemp_driver); 349 } 350 351 MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>"); 352 MODULE_DESCRIPTION("VIA CPU temperature monitor"); 353 MODULE_LICENSE("GPL"); 354 355 module_init(via_cputemp_init) 356 module_exit(via_cputemp_exit) 357