1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * via-cputemp.c - Driver for VIA CPU core temperature monitoring 4 * Copyright (C) 2009 VIA Technologies, Inc. 5 * 6 * based on existing coretemp.c, which is 7 * 8 * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz> 9 */ 10 11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 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 31 enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME }; 32 33 /* 34 * Functions declaration 35 */ 36 37 struct 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 stuff 48 */ 49 50 static 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 64 static 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 78 static 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 92 static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, SHOW_TEMP); 93 static SENSOR_DEVICE_ATTR_RO(temp1_label, name, SHOW_LABEL); 94 static SENSOR_DEVICE_ATTR_RO(name, name, SHOW_NAME); 95 96 static 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 NULL 101 }; 102 103 static const struct attribute_group via_cputemp_group = { 104 .attrs = via_cputemp_attributes, 105 }; 106 107 /* Optional attributes */ 108 static DEVICE_ATTR_RO(cpu0_vid); 109 110 static 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 178 exit_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 185 static int 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 return 0; 194 } 195 196 static struct platform_driver via_cputemp_driver = { 197 .driver = { 198 .name = DRVNAME, 199 }, 200 .probe = via_cputemp_probe, 201 .remove = via_cputemp_remove, 202 }; 203 204 struct pdev_entry { 205 struct list_head list; 206 struct platform_device *pdev; 207 unsigned int cpu; 208 }; 209 210 static LIST_HEAD(pdev_list); 211 static DEFINE_MUTEX(pdev_list_mutex); 212 213 static int via_cputemp_online(unsigned int cpu) 214 { 215 int err; 216 struct platform_device *pdev; 217 struct pdev_entry *pdev_entry; 218 219 pdev = platform_device_alloc(DRVNAME, cpu); 220 if (!pdev) { 221 err = -ENOMEM; 222 pr_err("Device allocation failed\n"); 223 goto exit; 224 } 225 226 pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); 227 if (!pdev_entry) { 228 err = -ENOMEM; 229 goto exit_device_put; 230 } 231 232 err = platform_device_add(pdev); 233 if (err) { 234 pr_err("Device addition failed (%d)\n", err); 235 goto exit_device_free; 236 } 237 238 pdev_entry->pdev = pdev; 239 pdev_entry->cpu = cpu; 240 mutex_lock(&pdev_list_mutex); 241 list_add_tail(&pdev_entry->list, &pdev_list); 242 mutex_unlock(&pdev_list_mutex); 243 244 return 0; 245 246 exit_device_free: 247 kfree(pdev_entry); 248 exit_device_put: 249 platform_device_put(pdev); 250 exit: 251 return err; 252 } 253 254 static int via_cputemp_down_prep(unsigned int cpu) 255 { 256 struct pdev_entry *p; 257 258 mutex_lock(&pdev_list_mutex); 259 list_for_each_entry(p, &pdev_list, list) { 260 if (p->cpu == cpu) { 261 platform_device_unregister(p->pdev); 262 list_del(&p->list); 263 mutex_unlock(&pdev_list_mutex); 264 kfree(p); 265 return 0; 266 } 267 } 268 mutex_unlock(&pdev_list_mutex); 269 return 0; 270 } 271 272 static const struct x86_cpu_id __initconst cputemp_ids[] = { 273 { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */ 274 { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */ 275 { X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */ 276 { X86_VENDOR_CENTAUR, 7, X86_MODEL_ANY, }, 277 {} 278 }; 279 MODULE_DEVICE_TABLE(x86cpu, cputemp_ids); 280 281 static enum cpuhp_state via_temp_online; 282 283 static int __init via_cputemp_init(void) 284 { 285 int err; 286 287 if (!x86_match_cpu(cputemp_ids)) 288 return -ENODEV; 289 290 err = platform_driver_register(&via_cputemp_driver); 291 if (err) 292 goto exit; 293 294 err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online", 295 via_cputemp_online, via_cputemp_down_prep); 296 if (err < 0) 297 goto exit_driver_unreg; 298 via_temp_online = err; 299 300 #ifndef CONFIG_HOTPLUG_CPU 301 if (list_empty(&pdev_list)) { 302 err = -ENODEV; 303 goto exit_hp_unreg; 304 } 305 #endif 306 return 0; 307 308 #ifndef CONFIG_HOTPLUG_CPU 309 exit_hp_unreg: 310 cpuhp_remove_state_nocalls(via_temp_online); 311 #endif 312 exit_driver_unreg: 313 platform_driver_unregister(&via_cputemp_driver); 314 exit: 315 return err; 316 } 317 318 static void __exit via_cputemp_exit(void) 319 { 320 cpuhp_remove_state(via_temp_online); 321 platform_driver_unregister(&via_cputemp_driver); 322 } 323 324 MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>"); 325 MODULE_DESCRIPTION("VIA CPU temperature monitor"); 326 MODULE_LICENSE("GPL"); 327 328 module_init(via_cputemp_init) 329 module_exit(via_cputemp_exit) 330