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 void 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 } 194 195 static struct platform_driver via_cputemp_driver = { 196 .driver = { 197 .name = DRVNAME, 198 }, 199 .probe = via_cputemp_probe, 200 .remove_new = via_cputemp_remove, 201 }; 202 203 struct pdev_entry { 204 struct list_head list; 205 struct platform_device *pdev; 206 unsigned int cpu; 207 }; 208 209 static LIST_HEAD(pdev_list); 210 static DEFINE_MUTEX(pdev_list_mutex); 211 212 static int via_cputemp_online(unsigned int cpu) 213 { 214 int err; 215 struct platform_device *pdev; 216 struct pdev_entry *pdev_entry; 217 218 pdev = platform_device_alloc(DRVNAME, cpu); 219 if (!pdev) { 220 err = -ENOMEM; 221 pr_err("Device allocation failed\n"); 222 goto exit; 223 } 224 225 pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); 226 if (!pdev_entry) { 227 err = -ENOMEM; 228 goto exit_device_put; 229 } 230 231 err = platform_device_add(pdev); 232 if (err) { 233 pr_err("Device addition failed (%d)\n", err); 234 goto exit_device_free; 235 } 236 237 pdev_entry->pdev = pdev; 238 pdev_entry->cpu = cpu; 239 mutex_lock(&pdev_list_mutex); 240 list_add_tail(&pdev_entry->list, &pdev_list); 241 mutex_unlock(&pdev_list_mutex); 242 243 return 0; 244 245 exit_device_free: 246 kfree(pdev_entry); 247 exit_device_put: 248 platform_device_put(pdev); 249 exit: 250 return err; 251 } 252 253 static int via_cputemp_down_prep(unsigned int cpu) 254 { 255 struct pdev_entry *p; 256 257 mutex_lock(&pdev_list_mutex); 258 list_for_each_entry(p, &pdev_list, list) { 259 if (p->cpu == cpu) { 260 platform_device_unregister(p->pdev); 261 list_del(&p->list); 262 mutex_unlock(&pdev_list_mutex); 263 kfree(p); 264 return 0; 265 } 266 } 267 mutex_unlock(&pdev_list_mutex); 268 return 0; 269 } 270 271 static const struct x86_cpu_id __initconst cputemp_ids[] = { 272 X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_A, NULL), 273 X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_D, NULL), 274 X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_NANO, NULL), 275 X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 7, X86_MODEL_ANY, NULL), 276 {} 277 }; 278 MODULE_DEVICE_TABLE(x86cpu, cputemp_ids); 279 280 static enum cpuhp_state via_temp_online; 281 282 static int __init via_cputemp_init(void) 283 { 284 int err; 285 286 if (!x86_match_cpu(cputemp_ids)) 287 return -ENODEV; 288 289 err = platform_driver_register(&via_cputemp_driver); 290 if (err) 291 goto exit; 292 293 err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online", 294 via_cputemp_online, via_cputemp_down_prep); 295 if (err < 0) 296 goto exit_driver_unreg; 297 via_temp_online = err; 298 299 #ifndef CONFIG_HOTPLUG_CPU 300 if (list_empty(&pdev_list)) { 301 err = -ENODEV; 302 goto exit_hp_unreg; 303 } 304 #endif 305 return 0; 306 307 #ifndef CONFIG_HOTPLUG_CPU 308 exit_hp_unreg: 309 cpuhp_remove_state_nocalls(via_temp_online); 310 #endif 311 exit_driver_unreg: 312 platform_driver_unregister(&via_cputemp_driver); 313 exit: 314 return err; 315 } 316 317 static void __exit via_cputemp_exit(void) 318 { 319 cpuhp_remove_state(via_temp_online); 320 platform_driver_unregister(&via_cputemp_driver); 321 } 322 323 MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>"); 324 MODULE_DESCRIPTION("VIA CPU temperature monitor"); 325 MODULE_LICENSE("GPL"); 326 327 module_init(via_cputemp_init) 328 module_exit(via_cputemp_exit) 329