diff options
Diffstat (limited to 'drivers/hwmon/coretemp.c')
| -rw-r--r-- | drivers/hwmon/coretemp.c | 307 |
1 files changed, 189 insertions, 118 deletions
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index ccf0af5b988a..ca7a9b373bbd 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -46,9 +46,6 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) -#define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) -#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) - #ifdef CONFIG_SMP #define for_each_sibling(i, cpu) \ for_each_cpu(i, topology_sibling_cpumask(cpu)) @@ -58,6 +55,8 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); /* * Per-Core Temperature Data + * @tjmax: The static tjmax value when tjmax cannot be retrieved from + * IA32_TEMPERATURE_TARGET MSR. * @last_updated: The time when the current temperature value was updated * earlier (in jiffies). * @cpu_core_id: The CPU Core from which temperature values should be read @@ -67,11 +66,9 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); * @attr_size: Total number of pre-core attrs displayed in the sysfs. * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. * Otherwise, temp_data holds coretemp data. - * @valid: If this is 1, the current temperature is valid. */ struct temp_data { int temp; - int ttarget; int tjmax; unsigned long last_updated; unsigned int cpu; @@ -79,7 +76,6 @@ struct temp_data { u32 status_reg; int attr_size; bool is_pkg_data; - bool valid; struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; struct attribute *attrs[TOTAL_ATTRS + 1]; @@ -91,90 +87,13 @@ struct temp_data { struct platform_data { struct device *hwmon_dev; u16 pkg_id; + u16 cpu_map[NUM_REAL_CORES]; + struct ida ida; struct cpumask cpumask; struct temp_data *core_data[MAX_CORE_DATA]; struct device_attribute name_attr; }; -/* Keep track of how many zone pointers we allocated in init() */ -static int max_zones __read_mostly; -/* Array of zone pointers. Serialized by cpu hotplug lock */ -static struct platform_device **zone_devices; - -static ssize_t show_label(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct platform_data *pdata = dev_get_drvdata(dev); - struct temp_data *tdata = pdata->core_data[attr->index]; - - if (tdata->is_pkg_data) - return sprintf(buf, "Package id %u\n", pdata->pkg_id); - - return sprintf(buf, "Core %u\n", tdata->cpu_core_id); -} - -static ssize_t show_crit_alarm(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - u32 eax, edx; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct platform_data *pdata = dev_get_drvdata(dev); - struct temp_data *tdata = pdata->core_data[attr->index]; - - mutex_lock(&tdata->update_lock); - rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); - mutex_unlock(&tdata->update_lock); - - return sprintf(buf, "%d\n", (eax >> 5) & 1); -} - -static ssize_t show_tjmax(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct platform_data *pdata = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax); -} - -static ssize_t show_ttarget(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct platform_data *pdata = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget); -} - -static ssize_t show_temp(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - u32 eax, edx; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct platform_data *pdata = dev_get_drvdata(dev); - struct temp_data *tdata = pdata->core_data[attr->index]; - - mutex_lock(&tdata->update_lock); - - /* Check whether the time interval has elapsed */ - if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) { - rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); - /* - * Ignore the valid bit. In all observed cases the register - * value is either low or zero if the valid bit is 0. - * Return it instead of reporting an error which doesn't - * really help at all. - */ - tdata->temp = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000; - tdata->valid = true; - tdata->last_updated = jiffies; - } - - mutex_unlock(&tdata->update_lock); - return sprintf(buf, "%d\n", tdata->temp); -} - struct tjmax_pci { unsigned int device; int tjmax; @@ -243,10 +162,13 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) */ if (host_bridge && host_bridge->vendor == PCI_VENDOR_ID_INTEL) { for (i = 0; i < ARRAY_SIZE(tjmax_pci_table); i++) { - if (host_bridge->device == tjmax_pci_table[i].device) + if (host_bridge->device == tjmax_pci_table[i].device) { + pci_dev_put(host_bridge); return tjmax_pci_table[i].tjmax; + } } } + pci_dev_put(host_bridge); for (i = 0; i < ARRAY_SIZE(tjmax_table); i++) { if (strstr(c->x86_model_id, tjmax_table[i].id)) @@ -338,20 +260,25 @@ static bool cpu_has_tjmax(struct cpuinfo_x86 *c) model != 0x36; } -static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) +static int get_tjmax(struct temp_data *tdata, struct device *dev) { + struct cpuinfo_x86 *c = &cpu_data(tdata->cpu); int err; u32 eax, edx; u32 val; + /* use static tjmax once it is set */ + if (tdata->tjmax) + return tdata->tjmax; + /* * A new feature of current Intel(R) processors, the * IA32_TEMPERATURE_TARGET contains the TjMax value */ - err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); + err = rdmsr_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); if (err) { if (cpu_has_tjmax(c)) - dev_warn(dev, "Unable to read TjMax from CPU %u\n", id); + dev_warn(dev, "Unable to read TjMax from CPU %u\n", tdata->cpu); } else { val = (eax >> 16) & 0xff; /* @@ -367,14 +294,133 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) if (force_tjmax) { dev_notice(dev, "TjMax forced to %d degrees C by user\n", force_tjmax); - return force_tjmax * 1000; + tdata->tjmax = force_tjmax * 1000; + } else { + /* + * An assumption is made for early CPUs and unreadable MSR. + * NOTE: the calculated value may not be correct. + */ + tdata->tjmax = adjust_tjmax(c, tdata->cpu, dev); } + return tdata->tjmax; +} + +static int get_ttarget(struct temp_data *tdata, struct device *dev) +{ + u32 eax, edx; + int tjmax, ttarget_offset, ret; /* - * An assumption is made for early CPUs and unreadable MSR. - * NOTE: the calculated value may not be correct. + * ttarget is valid only if tjmax can be retrieved from + * MSR_IA32_TEMPERATURE_TARGET */ - return adjust_tjmax(c, id, dev); + if (tdata->tjmax) + return -ENODEV; + + ret = rdmsr_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); + if (ret) + return ret; + + tjmax = (eax >> 16) & 0xff; + + /* Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. */ + ttarget_offset = (eax >> 8) & 0xff; + + return (tjmax - ttarget_offset) * 1000; +} + +/* Keep track of how many zone pointers we allocated in init() */ +static int max_zones __read_mostly; +/* Array of zone pointers. Serialized by cpu hotplug lock */ +static struct platform_device **zone_devices; + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + if (tdata->is_pkg_data) + return sprintf(buf, "Package id %u\n", pdata->pkg_id); + + return sprintf(buf, "Core %u\n", tdata->cpu_core_id); +} + +static ssize_t show_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 eax, edx; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + mutex_lock(&tdata->update_lock); + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + mutex_unlock(&tdata->update_lock); + + return sprintf(buf, "%d\n", (eax >> 5) & 1); +} + +static ssize_t show_tjmax(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + int tjmax; + + mutex_lock(&tdata->update_lock); + tjmax = get_tjmax(tdata, dev); + mutex_unlock(&tdata->update_lock); + + return sprintf(buf, "%d\n", tjmax); +} + +static ssize_t show_ttarget(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + int ttarget; + + mutex_lock(&tdata->update_lock); + ttarget = get_ttarget(tdata, dev); + mutex_unlock(&tdata->update_lock); + + if (ttarget < 0) + return ttarget; + return sprintf(buf, "%d\n", ttarget); +} + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 eax, edx; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + int tjmax; + + mutex_lock(&tdata->update_lock); + + tjmax = get_tjmax(tdata, dev); + /* Check whether the time interval has elapsed */ + if (time_after(jiffies, tdata->last_updated + HZ)) { + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + /* + * Ignore the valid bit. In all observed cases the register + * value is either low or zero if the valid bit is 0. + * Return it instead of reporting an error which doesn't + * really help at all. + */ + tdata->temp = tjmax - ((eax >> 16) & 0x7f) * 1000; + tdata->last_updated = jiffies; + } + + mutex_unlock(&tdata->update_lock); + return sprintf(buf, "%d\n", tdata->temp); } static int create_core_attrs(struct temp_data *tdata, struct device *dev, @@ -441,7 +487,7 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag) MSR_IA32_THERM_STATUS; tdata->is_pkg_data = pkg_flag; tdata->cpu = cpu; - tdata->cpu_core_id = TO_CORE_ID(cpu); + tdata->cpu_core_id = topology_core_id(cpu); tdata->attr_size = MAX_CORE_ATTRS; mutex_init(&tdata->update_lock); return tdata; @@ -454,7 +500,7 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, struct platform_data *pdata = platform_get_drvdata(pdev); struct cpuinfo_x86 *c = &cpu_data(cpu); u32 eax, edx; - int err, attr_no; + int err, index, attr_no; /* * Find attr number for sysfs: @@ -462,37 +508,43 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, * The attr number is always core id + 2 * The Pkgtemp will always show up as temp1_*, if available */ - attr_no = pkg_flag ? PKG_SYSFS_ATTR_NO : TO_ATTR_NO(cpu); + if (pkg_flag) { + attr_no = PKG_SYSFS_ATTR_NO; + } else { + index = ida_alloc(&pdata->ida, GFP_KERNEL); + if (index < 0) + return index; + pdata->cpu_map[index] = topology_core_id(cpu); + attr_no = index + BASE_SYSFS_ATTR_NO; + } - if (attr_no > MAX_CORE_DATA - 1) - return -ERANGE; + if (attr_no > MAX_CORE_DATA - 1) { + err = -ERANGE; + goto ida_free; + } tdata = init_temp_data(cpu, pkg_flag); - if (!tdata) - return -ENOMEM; + if (!tdata) { + err = -ENOMEM; + goto ida_free; + } /* Test if we can access the status register */ err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx); if (err) goto exit_free; - /* We can access status register. Get Critical Temperature */ - tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); + /* Make sure tdata->tjmax is a valid indicator for dynamic/static tjmax */ + get_tjmax(tdata, &pdev->dev); /* - * Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. - * The target temperature is available on older CPUs but not in this - * register. Atoms don't have the register at all. + * The target temperature is available on older CPUs but not in the + * MSR_IA32_TEMPERATURE_TARGET register. Atoms don't have the register + * at all. */ - if (c->x86_model > 0xe && c->x86_model != 0x1c) { - err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, - &eax, &edx); - if (!err) { - tdata->ttarget - = tdata->tjmax - ((eax >> 8) & 0xff) * 1000; + if (c->x86_model > 0xe && c->x86_model != 0x1c) + if (get_ttarget(tdata, &pdev->dev) >= 0) tdata->attr_size++; - } - } pdata->core_data[attr_no] = tdata; @@ -505,6 +557,9 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, exit_free: pdata->core_data[attr_no] = NULL; kfree(tdata); +ida_free: + if (!pkg_flag) + ida_free(&pdata->ida, index); return err; } @@ -519,11 +574,18 @@ static void coretemp_remove_core(struct platform_data *pdata, int indx) { struct temp_data *tdata = pdata->core_data[indx]; + /* if we errored on add then this is already gone */ + if (!tdata) + return; + /* Remove the sysfs attributes */ sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group); kfree(pdata->core_data[indx]); pdata->core_data[indx] = NULL; + + if (indx >= BASE_SYSFS_ATTR_NO) + ida_free(&pdata->ida, indx - BASE_SYSFS_ATTR_NO); } static int coretemp_probe(struct platform_device *pdev) @@ -537,6 +599,7 @@ static int coretemp_probe(struct platform_device *pdev) return -ENOMEM; pdata->pkg_id = pdev->id; + ida_init(&pdata->ida); platform_set_drvdata(pdev, pdata); pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, @@ -553,6 +616,7 @@ static int coretemp_remove(struct platform_device *pdev) if (pdata->core_data[i]) coretemp_remove_core(pdata, i); + ida_destroy(&pdata->ida); return 0; } @@ -647,7 +711,7 @@ static int coretemp_cpu_offline(unsigned int cpu) struct platform_device *pdev = coretemp_get_pdev(cpu); struct platform_data *pd; struct temp_data *tdata; - int indx, target; + int i, indx = -1, target; /* * Don't execute this on suspend as the device remove locks @@ -660,12 +724,19 @@ static int coretemp_cpu_offline(unsigned int cpu) if (!pdev) return 0; - /* The core id is too big, just return */ - indx = TO_ATTR_NO(cpu); - if (indx > MAX_CORE_DATA - 1) + pd = platform_get_drvdata(pdev); + + for (i = 0; i < NUM_REAL_CORES; i++) { + if (pd->cpu_map[i] == topology_core_id(cpu)) { + indx = i + BASE_SYSFS_ATTR_NO; + break; + } + } + + /* Too many cores and this core is not populated, just return */ + if (indx < 0) return 0; - pd = platform_get_drvdata(pdev); tdata = pd->core_data[indx]; cpumask_clear_cpu(cpu, &pd->cpumask); |