Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * via-cputemp.c - Driver for VIA CPU core temperature monitoring
0004  * Copyright (C) 2009 VIA Technologies, Inc.
0005  *
0006  * based on existing coretemp.c, which is
0007  *
0008  * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>
0009  */
0010 
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012 
0013 #include <linux/module.h>
0014 #include <linux/init.h>
0015 #include <linux/slab.h>
0016 #include <linux/hwmon.h>
0017 #include <linux/hwmon-vid.h>
0018 #include <linux/sysfs.h>
0019 #include <linux/hwmon-sysfs.h>
0020 #include <linux/err.h>
0021 #include <linux/mutex.h>
0022 #include <linux/list.h>
0023 #include <linux/platform_device.h>
0024 #include <linux/cpu.h>
0025 #include <asm/msr.h>
0026 #include <asm/processor.h>
0027 #include <asm/cpu_device_id.h>
0028 
0029 #define DRVNAME "via_cputemp"
0030 
0031 enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };
0032 
0033 /*
0034  * Functions declaration
0035  */
0036 
0037 struct via_cputemp_data {
0038     struct device *hwmon_dev;
0039     const char *name;
0040     u8 vrm;
0041     u32 id;
0042     u32 msr_temp;
0043     u32 msr_vid;
0044 };
0045 
0046 /*
0047  * Sysfs stuff
0048  */
0049 
0050 static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
0051              char *buf)
0052 {
0053     int ret;
0054     struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0055     struct via_cputemp_data *data = dev_get_drvdata(dev);
0056 
0057     if (attr->index == SHOW_NAME)
0058         ret = sprintf(buf, "%s\n", data->name);
0059     else    /* show label */
0060         ret = sprintf(buf, "Core %d\n", data->id);
0061     return ret;
0062 }
0063 
0064 static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
0065              char *buf)
0066 {
0067     struct via_cputemp_data *data = dev_get_drvdata(dev);
0068     u32 eax, edx;
0069     int err;
0070 
0071     err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
0072     if (err)
0073         return -EAGAIN;
0074 
0075     return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000);
0076 }
0077 
0078 static ssize_t cpu0_vid_show(struct device *dev,
0079                  struct device_attribute *devattr, char *buf)
0080 {
0081     struct via_cputemp_data *data = dev_get_drvdata(dev);
0082     u32 eax, edx;
0083     int err;
0084 
0085     err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx);
0086     if (err)
0087         return -EAGAIN;
0088 
0089     return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm));
0090 }
0091 
0092 static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, SHOW_TEMP);
0093 static SENSOR_DEVICE_ATTR_RO(temp1_label, name, SHOW_LABEL);
0094 static SENSOR_DEVICE_ATTR_RO(name, name, SHOW_NAME);
0095 
0096 static struct attribute *via_cputemp_attributes[] = {
0097     &sensor_dev_attr_name.dev_attr.attr,
0098     &sensor_dev_attr_temp1_label.dev_attr.attr,
0099     &sensor_dev_attr_temp1_input.dev_attr.attr,
0100     NULL
0101 };
0102 
0103 static const struct attribute_group via_cputemp_group = {
0104     .attrs = via_cputemp_attributes,
0105 };
0106 
0107 /* Optional attributes */
0108 static DEVICE_ATTR_RO(cpu0_vid);
0109 
0110 static int via_cputemp_probe(struct platform_device *pdev)
0111 {
0112     struct via_cputemp_data *data;
0113     struct cpuinfo_x86 *c = &cpu_data(pdev->id);
0114     int err;
0115     u32 eax, edx;
0116 
0117     data = devm_kzalloc(&pdev->dev, sizeof(struct via_cputemp_data),
0118                 GFP_KERNEL);
0119     if (!data)
0120         return -ENOMEM;
0121 
0122     data->id = pdev->id;
0123     data->name = "via_cputemp";
0124 
0125     if (c->x86 == 7) {
0126         data->msr_temp = 0x1423;
0127     } else {
0128         switch (c->x86_model) {
0129         case 0xA:
0130             /* C7 A */
0131         case 0xD:
0132             /* C7 D */
0133             data->msr_temp = 0x1169;
0134             data->msr_vid = 0x198;
0135             break;
0136         case 0xF:
0137             /* Nano */
0138             data->msr_temp = 0x1423;
0139             break;
0140         default:
0141             return -ENODEV;
0142         }
0143     }
0144 
0145     /* test if we can access the TEMPERATURE MSR */
0146     err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
0147     if (err) {
0148         dev_err(&pdev->dev,
0149             "Unable to access TEMPERATURE MSR, giving up\n");
0150         return err;
0151     }
0152 
0153     platform_set_drvdata(pdev, data);
0154 
0155     err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group);
0156     if (err)
0157         return err;
0158 
0159     if (data->msr_vid)
0160         data->vrm = vid_which_vrm();
0161 
0162     if (data->vrm) {
0163         err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid);
0164         if (err)
0165             goto exit_remove;
0166     }
0167 
0168     data->hwmon_dev = hwmon_device_register(&pdev->dev);
0169     if (IS_ERR(data->hwmon_dev)) {
0170         err = PTR_ERR(data->hwmon_dev);
0171         dev_err(&pdev->dev, "Class registration failed (%d)\n",
0172             err);
0173         goto exit_remove;
0174     }
0175 
0176     return 0;
0177 
0178 exit_remove:
0179     if (data->vrm)
0180         device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
0181     sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
0182     return err;
0183 }
0184 
0185 static int via_cputemp_remove(struct platform_device *pdev)
0186 {
0187     struct via_cputemp_data *data = platform_get_drvdata(pdev);
0188 
0189     hwmon_device_unregister(data->hwmon_dev);
0190     if (data->vrm)
0191         device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
0192     sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
0193     return 0;
0194 }
0195 
0196 static struct platform_driver via_cputemp_driver = {
0197     .driver = {
0198         .name = DRVNAME,
0199     },
0200     .probe = via_cputemp_probe,
0201     .remove = via_cputemp_remove,
0202 };
0203 
0204 struct pdev_entry {
0205     struct list_head list;
0206     struct platform_device *pdev;
0207     unsigned int cpu;
0208 };
0209 
0210 static LIST_HEAD(pdev_list);
0211 static DEFINE_MUTEX(pdev_list_mutex);
0212 
0213 static int via_cputemp_online(unsigned int cpu)
0214 {
0215     int err;
0216     struct platform_device *pdev;
0217     struct pdev_entry *pdev_entry;
0218 
0219     pdev = platform_device_alloc(DRVNAME, cpu);
0220     if (!pdev) {
0221         err = -ENOMEM;
0222         pr_err("Device allocation failed\n");
0223         goto exit;
0224     }
0225 
0226     pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
0227     if (!pdev_entry) {
0228         err = -ENOMEM;
0229         goto exit_device_put;
0230     }
0231 
0232     err = platform_device_add(pdev);
0233     if (err) {
0234         pr_err("Device addition failed (%d)\n", err);
0235         goto exit_device_free;
0236     }
0237 
0238     pdev_entry->pdev = pdev;
0239     pdev_entry->cpu = cpu;
0240     mutex_lock(&pdev_list_mutex);
0241     list_add_tail(&pdev_entry->list, &pdev_list);
0242     mutex_unlock(&pdev_list_mutex);
0243 
0244     return 0;
0245 
0246 exit_device_free:
0247     kfree(pdev_entry);
0248 exit_device_put:
0249     platform_device_put(pdev);
0250 exit:
0251     return err;
0252 }
0253 
0254 static int via_cputemp_down_prep(unsigned int cpu)
0255 {
0256     struct pdev_entry *p;
0257 
0258     mutex_lock(&pdev_list_mutex);
0259     list_for_each_entry(p, &pdev_list, list) {
0260         if (p->cpu == cpu) {
0261             platform_device_unregister(p->pdev);
0262             list_del(&p->list);
0263             mutex_unlock(&pdev_list_mutex);
0264             kfree(p);
0265             return 0;
0266         }
0267     }
0268     mutex_unlock(&pdev_list_mutex);
0269     return 0;
0270 }
0271 
0272 static const struct x86_cpu_id __initconst cputemp_ids[] = {
0273     X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_A,   NULL),
0274     X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_D,   NULL),
0275     X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_NANO,   NULL),
0276     X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 7, X86_MODEL_ANY,       NULL),
0277     {}
0278 };
0279 MODULE_DEVICE_TABLE(x86cpu, cputemp_ids);
0280 
0281 static enum cpuhp_state via_temp_online;
0282 
0283 static int __init via_cputemp_init(void)
0284 {
0285     int err;
0286 
0287     if (!x86_match_cpu(cputemp_ids))
0288         return -ENODEV;
0289 
0290     err = platform_driver_register(&via_cputemp_driver);
0291     if (err)
0292         goto exit;
0293 
0294     err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online",
0295                 via_cputemp_online, via_cputemp_down_prep);
0296     if (err < 0)
0297         goto exit_driver_unreg;
0298     via_temp_online = err;
0299 
0300 #ifndef CONFIG_HOTPLUG_CPU
0301     if (list_empty(&pdev_list)) {
0302         err = -ENODEV;
0303         goto exit_hp_unreg;
0304     }
0305 #endif
0306     return 0;
0307 
0308 #ifndef CONFIG_HOTPLUG_CPU
0309 exit_hp_unreg:
0310     cpuhp_remove_state_nocalls(via_temp_online);
0311 #endif
0312 exit_driver_unreg:
0313     platform_driver_unregister(&via_cputemp_driver);
0314 exit:
0315     return err;
0316 }
0317 
0318 static void __exit via_cputemp_exit(void)
0319 {
0320     cpuhp_remove_state(via_temp_online);
0321     platform_driver_unregister(&via_cputemp_driver);
0322 }
0323 
0324 MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
0325 MODULE_DESCRIPTION("VIA CPU temperature monitor");
0326 MODULE_LICENSE("GPL");
0327 
0328 module_init(via_cputemp_init)
0329 module_exit(via_cputemp_exit)