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