diff options
Diffstat (limited to 'drivers/hwmon/coretemp.c')
| -rw-r--r-- | drivers/hwmon/coretemp.c | 938 |
1 files changed, 456 insertions, 482 deletions
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index ade35cf3f488..ad79db5a183e 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * coretemp.c - Linux kernel module for hardware monitoring * * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz> * * Inspired from many hwmon drivers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301 USA. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -36,9 +23,11 @@ #include <linux/cpu.h> #include <linux/smp.h> #include <linux/moduleparam.h> +#include <linux/pci.h> #include <asm/msr.h> #include <asm/processor.h> #include <asm/cpu_device_id.h> +#include <linux/sched/isolation.h> #define DRVNAME "coretemp" @@ -50,25 +39,30 @@ static int force_tjmax; module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); -#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ -#define NUM_REAL_CORES 32 /* Number of Real cores per cpu */ -#define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */ -#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ -#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) -#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) - -#define TO_PHYS_ID(cpu) (cpu_data(cpu).phys_proc_id) -#define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) -#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) +#define NUM_REAL_CORES 512 /* Number of Real cores per cpu */ +#define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */ + +enum coretemp_attr_index { + ATTR_LABEL, + ATTR_CRIT_ALARM, + ATTR_TEMP, + ATTR_TJMAX, + ATTR_TTARGET, + MAX_CORE_ATTRS = ATTR_TJMAX + 1, /* Maximum no of basic attrs */ + TOTAL_ATTRS = ATTR_TTARGET + 1 /* Maximum no of possible attrs */ +}; #ifdef CONFIG_SMP -#define for_each_sibling(i, cpu) for_each_cpu(i, cpu_sibling_mask(cpu)) +#define for_each_sibling(i, cpu) \ + for_each_cpu(i, topology_sibling_cpumask(cpu)) #else #define for_each_sibling(i, cpu) for (i = 0; false; ) #endif /* * 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 @@ -76,158 +70,89 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, * from where the temperature values should be read. * @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; + int index; u32 cpu_core_id; u32 status_reg; int attr_size; - bool is_pkg_data; - bool valid; - struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; + struct device_attribute sd_attrs[TOTAL_ATTRS]; char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; + struct attribute *attrs[TOTAL_ATTRS + 1]; + struct attribute_group attr_group; struct mutex update_lock; }; /* Platform Data per Physical CPU */ struct platform_data { - struct device *hwmon_dev; - u16 phys_proc_id; - struct temp_data *core_data[MAX_CORE_DATA]; + struct device *hwmon_dev; + u16 pkg_id; + int nr_cores; + struct ida ida; + struct cpumask cpumask; + struct temp_data *pkg_data; + struct temp_data **core_data; struct device_attribute name_attr; }; -struct pdev_entry { - struct list_head list; - struct platform_device *pdev; - u16 phys_proc_id; +struct tjmax_pci { + unsigned int device; + int tjmax; }; -static LIST_HEAD(pdev_list); -static DEFINE_MUTEX(pdev_list_mutex); - -static ssize_t show_name(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - return sprintf(buf, "%s\n", DRVNAME); -} - -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, "Physical id %u\n", pdata->phys_proc_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]; - - rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); - - 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); - tdata->valid = 0; - /* Check whether the data is valid */ - if (eax & 0x80000000) { - tdata->temp = tdata->tjmax - - ((eax >> 16) & 0x7f) * 1000; - tdata->valid = 1; - } - tdata->last_updated = jiffies; - } - - mutex_unlock(&tdata->update_lock); - return tdata->valid ? sprintf(buf, "%d\n", tdata->temp) : -EAGAIN; -} +static const struct tjmax_pci tjmax_pci_table[] = { + { 0x0708, 110000 }, /* CE41x0 (Sodaville ) */ + { 0x0c72, 102000 }, /* Atom S1240 (Centerton) */ + { 0x0c73, 95000 }, /* Atom S1220 (Centerton) */ + { 0x0c75, 95000 }, /* Atom S1260 (Centerton) */ +}; struct tjmax { char const *id; int tjmax; }; -static const struct tjmax __cpuinitconst tjmax_table[] = { +static const struct tjmax tjmax_table[] = { { "CPU 230", 100000 }, /* Model 0x1c, stepping 2 */ { "CPU 330", 125000 }, /* Model 0x1c, stepping 2 */ - { "CPU CE4110", 110000 }, /* Model 0x1c, stepping 10 Sodaville */ - { "CPU CE4150", 110000 }, /* Model 0x1c, stepping 10 */ - { "CPU CE4170", 110000 }, /* Model 0x1c, stepping 10 */ }; struct tjmax_model { - u8 model; - u8 mask; + u32 vfm; + u8 stepping_mask; int tjmax; }; #define ANY 0xff -static const struct tjmax_model __cpuinitconst tjmax_model_table[] = { - { 0x1c, 10, 100000 }, /* D4xx, K4xx, N4xx, D5xx, K5xx, N5xx */ - { 0x1c, ANY, 90000 }, /* Z5xx, N2xx, possibly others - * Note: Also matches 230 and 330, - * which are covered by tjmax_table - */ - { 0x26, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx) - * Note: TjMax for E6xxT is 110C, but CPU type - * is undetectable by software - */ - { 0x27, ANY, 90000 }, /* Atom Medfield (Z2460) */ - { 0x35, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z2760) */ - { 0x36, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) */ +static const struct tjmax_model tjmax_model_table[] = { + { INTEL_ATOM_BONNELL, 10, 100000 }, /* D4xx, K4xx, N4xx, D5xx, K5xx, N5xx */ + { INTEL_ATOM_BONNELL, ANY, 90000 }, /* Z5xx, N2xx, possibly others + * Note: Also matches 230 and 330, + * which are covered by tjmax_table + */ + { INTEL_ATOM_BONNELL_MID, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx) + * Note: TjMax for E6xxT is 110C, but CPU type + * is undetectable by software + */ + { INTEL_ATOM_SALTWELL_MID, ANY, 90000 }, /* Atom Medfield (Z2460) */ + { INTEL_ATOM_SALTWELL_TABLET, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */ + { INTEL_ATOM_SALTWELL, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) + * Also matches S12x0 (stepping 9), covered by + * PCI table + */ }; -static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, - struct device *dev) +static bool is_pkg_temp_data(struct temp_data *tdata) +{ + return tdata->index < 0; +} + +static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) { /* The 100C is default for both mobile and non mobile CPUs */ @@ -237,8 +162,29 @@ static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, int err; u32 eax, edx; int i; + u16 devfn = PCI_DEVFN(0, 0); + struct pci_dev *host_bridge = pci_get_domain_bus_and_slot(0, 0, devfn); - /* explicit tjmax table entries override heuristics */ + /* + * Explicit tjmax table entries override heuristics. + * First try PCI host bridge IDs, followed by model ID strings + * and model/stepping information. + */ + 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) { + pci_dev_put(host_bridge); + return tjmax_pci_table[i].tjmax; + } + } + } + pci_dev_put(host_bridge); + + /* + * This is literally looking for "CPU XXX" in the model string. + * Not checking it against the model as well. Just purely a + * string search. + */ for (i = 0; i < ARRAY_SIZE(tjmax_table); i++) { if (strstr(c->x86_model_id, tjmax_table[i].id)) return tjmax_table[i].tjmax; @@ -246,17 +192,18 @@ static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) { const struct tjmax_model *tm = &tjmax_model_table[i]; - if (c->x86_model == tm->model && - (tm->mask == ANY || c->x86_mask == tm->mask)) + if (c->x86_vfm == tm->vfm && + (tm->stepping_mask == ANY || + tm->stepping_mask == c->x86_stepping)) return tm->tjmax; } /* Early chips have no MSR for TjMax */ - if (c->x86_model == 0xf && c->x86_mask < 4) + if (c->x86_vfm == INTEL_CORE2_MEROM && c->x86_stepping < 4) usemsr_ee = 0; - if (c->x86_model > 0xe && usemsr_ee) { + if (c->x86_vfm > INTEL_CORE_YONAH && usemsr_ee) { u8 platform_id; /* @@ -270,7 +217,8 @@ static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, "Unable to access MSR 0x17, assuming desktop" " CPU\n"); usemsr_ee = 0; - } else if (c->x86_model < 0x17 && !(eax & 0x10000000)) { + } else if (c->x86_vfm < INTEL_CORE2_PENRYN && + !(eax & 0x10000000)) { /* * Trust bit 28 up to Penryn, I could not find any * documentation on that; if you happen to know @@ -285,7 +233,7 @@ static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, * Mobile Penryn CPU seems to be platform ID 7 or 5 * (guesswork) */ - if (c->x86_model == 0x17 && + if (c->x86_vfm == INTEL_CORE2_PENRYN && (platform_id == 5 || platform_id == 7)) { /* * If MSR EE bit is set, set it to 90 degrees C, @@ -317,91 +265,187 @@ static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, return tjmax; } -static int __cpuinit 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 (c->x86_model > 0xe && c->x86_model != 0x1c) - dev_warn(dev, "Unable to read TjMax from CPU %u\n", id); + dev_warn_once(dev, "Unable to read TjMax from CPU %u\n", tdata->cpu); } else { val = (eax >> 16) & 0xff; - /* - * If the TjMax is not plausible, an assumption - * will be used - */ - if (val) { - dev_dbg(dev, "TjMax is %d degrees C\n", val); + if (val) return val * 1000; - } } 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; } -static int create_name_attr(struct platform_data *pdata, - struct device *dev) +/* 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) { - sysfs_attr_init(&pdata->name_attr.attr); - pdata->name_attr.attr.name = "name"; - pdata->name_attr.attr.mode = S_IRUGO; - pdata->name_attr.show = show_name; - return device_create_file(dev, &pdata->name_attr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_LABEL]); + + if (is_pkg_temp_data(tdata)) + return sprintf(buf, "Package id %u\n", pdata->pkg_id); + + return sprintf(buf, "Core %u\n", tdata->cpu_core_id); } -static int __cpuinit create_core_attrs(struct temp_data *tdata, - struct device *dev, int attr_no) +static ssize_t show_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) { - int err, i; + u32 eax, edx; + struct temp_data *tdata = container_of(devattr, struct temp_data, + sd_attrs[ATTR_CRIT_ALARM]); + + 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 temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TJMAX]); + 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 temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TTARGET]); + 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 temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TEMP]); + 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) & 0xff) * 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) +{ + int i; static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, show_ttarget }; - static const char *const names[TOTAL_ATTRS] = { - "temp%d_label", "temp%d_crit_alarm", - "temp%d_input", "temp%d_crit", - "temp%d_max" }; + static const char *const suffixes[TOTAL_ATTRS] = { + "label", "crit_alarm", "input", "crit", "max" + }; for (i = 0; i < tdata->attr_size; i++) { - snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], - attr_no); - sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); - tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; - tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; - tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; - tdata->sd_attrs[i].index = attr_no; - err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr); - if (err) - goto exit_free; + /* + * We map the attr number to core id of the CPU + * The attr number is always core id + 2 + * The Pkgtemp will always show up as temp1_*, if available + */ + int attr_no = is_pkg_temp_data(tdata) ? 1 : tdata->cpu_core_id + 2; + + snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, + "temp%d_%s", attr_no, suffixes[i]); + sysfs_attr_init(&tdata->sd_attrs[i].attr); + tdata->sd_attrs[i].attr.name = tdata->attr_name[i]; + tdata->sd_attrs[i].attr.mode = 0444; + tdata->sd_attrs[i].show = rd_ptr[i]; + tdata->attrs[i] = &tdata->sd_attrs[i].attr; } - return 0; - -exit_free: - while (--i >= 0) - device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); - return err; + tdata->attr_group.attrs = tdata->attrs; + return sysfs_create_group(&dev->kobj, &tdata->attr_group); } -static int __cpuinit chk_ucode_version(unsigned int cpu) +static int chk_ucode_version(unsigned int cpu) { struct cpuinfo_x86 *c = &cpu_data(cpu); @@ -410,282 +454,218 @@ static int __cpuinit chk_ucode_version(unsigned int cpu) * Readings might stop update when processor visited too deep sleep, * fixed for stepping D0 (6EC). */ - if (c->x86_model == 0xe && c->x86_mask < 0xc && c->microcode < 0x39) { + if (c->x86_vfm == INTEL_CORE_YONAH && c->x86_stepping < 0xc && c->microcode < 0x39) { pr_err("Errata AE18 not fixed, update BIOS or microcode of the CPU!\n"); return -ENODEV; } return 0; } -static struct platform_device __cpuinit *coretemp_get_pdev(unsigned int cpu) +static struct platform_device *coretemp_get_pdev(unsigned int cpu) { - u16 phys_proc_id = TO_PHYS_ID(cpu); - struct pdev_entry *p; - - mutex_lock(&pdev_list_mutex); - - list_for_each_entry(p, &pdev_list, list) - if (p->phys_proc_id == phys_proc_id) { - mutex_unlock(&pdev_list_mutex); - return p->pdev; - } + int id = topology_logical_die_id(cpu); - mutex_unlock(&pdev_list_mutex); + if (id >= 0 && id < max_zones) + return zone_devices[id]; return NULL; } -static struct temp_data __cpuinit *init_temp_data(unsigned int cpu, - int pkg_flag) +static struct temp_data * +init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag) { struct temp_data *tdata; + if (!pdata->core_data) { + /* + * TODO: + * The information of actual possible cores in a package is broken for now. + * Will replace hardcoded NUM_REAL_CORES with actual per package core count + * when this information becomes available. + */ + pdata->nr_cores = NUM_REAL_CORES; + pdata->core_data = kcalloc(pdata->nr_cores, sizeof(struct temp_data *), + GFP_KERNEL); + if (!pdata->core_data) + return NULL; + } + tdata = kzalloc(sizeof(struct temp_data), GFP_KERNEL); if (!tdata) return NULL; + if (pkg_flag) { + pdata->pkg_data = tdata; + /* Use tdata->index as indicator of package temp data */ + tdata->index = -1; + } else { + tdata->index = ida_alloc_max(&pdata->ida, pdata->nr_cores - 1, GFP_KERNEL); + if (tdata->index < 0) { + kfree(tdata); + return NULL; + } + pdata->core_data[tdata->index] = tdata; + } + tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS : 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; } -static int __cpuinit create_core_data(struct platform_device *pdev, - unsigned int cpu, int pkg_flag) +static void destroy_temp_data(struct platform_data *pdata, struct temp_data *tdata) +{ + if (is_pkg_temp_data(tdata)) { + pdata->pkg_data = NULL; + kfree(pdata->core_data); + pdata->core_data = NULL; + pdata->nr_cores = 0; + } else { + pdata->core_data[tdata->index] = NULL; + ida_free(&pdata->ida, tdata->index); + } + kfree(tdata); +} + +static struct temp_data *get_temp_data(struct platform_data *pdata, int cpu) +{ + int i; + + /* cpu < 0 means get pkg temp_data */ + if (cpu < 0) + return pdata->pkg_data; + + for (i = 0; i < pdata->nr_cores; i++) { + if (pdata->core_data[i] && + pdata->core_data[i]->cpu_core_id == topology_core_id(cpu)) + return pdata->core_data[i]; + } + return NULL; +} + +static int create_core_data(struct platform_device *pdev, unsigned int cpu, + int pkg_flag) { struct temp_data *tdata; struct platform_data *pdata = platform_get_drvdata(pdev); struct cpuinfo_x86 *c = &cpu_data(cpu); u32 eax, edx; - int err, attr_no; - - /* - * Find attr number for sysfs: - * We map the attr number to core id of the CPU - * The attr number is always core id + 2 - * The Pkgtemp will always show up as temp1_*, if available - */ - attr_no = pkg_flag ? 1 : TO_ATTR_NO(cpu); - - if (attr_no > MAX_CORE_DATA - 1) - return -ERANGE; + int err; - /* - * Provide a single set of attributes for all HT siblings of a core - * to avoid duplicate sensors (the processor ID and core ID of all - * HT siblings of a core are the same). - * Skip if a HT sibling of this core is already registered. - * This is not an error. - */ - if (pdata->core_data[attr_no] != NULL) + if (!housekeeping_cpu(cpu, HK_TYPE_MISC)) return 0; - tdata = init_temp_data(cpu, pkg_flag); + tdata = init_temp_data(pdata, cpu, pkg_flag); if (!tdata) return -ENOMEM; /* Test if we can access the status register */ err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx); if (err) - goto exit_free; + goto err; - /* 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_vfm > INTEL_CORE_YONAH && c->x86_vfm != INTEL_ATOM_BONNELL) + if (get_ttarget(tdata, &pdev->dev) >= 0) tdata->attr_size++; - } - } - - pdata->core_data[attr_no] = tdata; /* Create sysfs interfaces */ - err = create_core_attrs(tdata, &pdev->dev, attr_no); + err = create_core_attrs(tdata, pdata->hwmon_dev); if (err) - goto exit_free; + goto err; return 0; -exit_free: - pdata->core_data[attr_no] = NULL; - kfree(tdata); + +err: + destroy_temp_data(pdata, tdata); return err; } -static void __cpuinit coretemp_add_core(unsigned int cpu, int pkg_flag) +static void +coretemp_add_core(struct platform_device *pdev, unsigned int cpu, int pkg_flag) { - struct platform_device *pdev = coretemp_get_pdev(cpu); - int err; - - if (!pdev) - return; - - err = create_core_data(pdev, cpu, pkg_flag); - if (err) + if (create_core_data(pdev, cpu, pkg_flag)) dev_err(&pdev->dev, "Adding Core %u failed\n", cpu); } -static void coretemp_remove_core(struct platform_data *pdata, - struct device *dev, int indx) +static void coretemp_remove_core(struct platform_data *pdata, struct temp_data *tdata) { - int i; - 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 */ - for (i = 0; i < tdata->attr_size; i++) - device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); + sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group); - kfree(pdata->core_data[indx]); - pdata->core_data[indx] = NULL; + destroy_temp_data(pdata, tdata); } -static int coretemp_probe(struct platform_device *pdev) +static int coretemp_device_add(int zoneid) { + struct platform_device *pdev; struct platform_data *pdata; int err; - /* Initialize the per-package data structures */ - pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL); + /* Initialize the per-zone data structures */ + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - err = create_name_attr(pdata, &pdev->dev); - if (err) - goto exit_free; - - pdata->phys_proc_id = pdev->id; - platform_set_drvdata(pdev, pdata); - - pdata->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(pdata->hwmon_dev)) { - err = PTR_ERR(pdata->hwmon_dev); - dev_err(&pdev->dev, "Class registration failed (%d)\n", err); - goto exit_name; - } - return 0; - -exit_name: - device_remove_file(&pdev->dev, &pdata->name_attr); -exit_free: - kfree(pdata); - return err; -} - -static int coretemp_remove(struct platform_device *pdev) -{ - struct platform_data *pdata = platform_get_drvdata(pdev); - int i; - - for (i = MAX_CORE_DATA - 1; i >= 0; --i) - if (pdata->core_data[i]) - coretemp_remove_core(pdata, &pdev->dev, i); + pdata->pkg_id = zoneid; + ida_init(&pdata->ida); - device_remove_file(&pdev->dev, &pdata->name_attr); - hwmon_device_unregister(pdata->hwmon_dev); - kfree(pdata); - return 0; -} - -static struct platform_driver coretemp_driver = { - .driver = { - .owner = THIS_MODULE, - .name = DRVNAME, - }, - .probe = coretemp_probe, - .remove = coretemp_remove, -}; - -static int __cpuinit coretemp_device_add(unsigned int cpu) -{ - int err; - struct platform_device *pdev; - struct pdev_entry *pdev_entry; - - mutex_lock(&pdev_list_mutex); - - pdev = platform_device_alloc(DRVNAME, TO_PHYS_ID(cpu)); + pdev = platform_device_alloc(DRVNAME, zoneid); if (!pdev) { err = -ENOMEM; - pr_err("Device allocation failed\n"); - goto exit; - } - - pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); - if (!pdev_entry) { - err = -ENOMEM; - goto exit_device_put; + goto err_free_pdata; } err = platform_device_add(pdev); - if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_free; - } - - pdev_entry->pdev = pdev; - pdev_entry->phys_proc_id = pdev->id; - - list_add_tail(&pdev_entry->list, &pdev_list); - mutex_unlock(&pdev_list_mutex); + if (err) + goto err_put_dev; + platform_set_drvdata(pdev, pdata); + zone_devices[zoneid] = pdev; return 0; -exit_device_free: - kfree(pdev_entry); -exit_device_put: +err_put_dev: platform_device_put(pdev); -exit: - mutex_unlock(&pdev_list_mutex); +err_free_pdata: + kfree(pdata); return err; } -static void __cpuinit coretemp_device_remove(unsigned int cpu) -{ - struct pdev_entry *p, *n; - u16 phys_proc_id = TO_PHYS_ID(cpu); - - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - if (p->phys_proc_id != phys_proc_id) - continue; - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } - mutex_unlock(&pdev_list_mutex); -} - -static bool __cpuinit is_any_core_online(struct platform_data *pdata) +static void coretemp_device_remove(int zoneid) { - int i; + struct platform_device *pdev = zone_devices[zoneid]; + struct platform_data *pdata = platform_get_drvdata(pdev); - /* Find online cores, except pkgtemp data */ - for (i = MAX_CORE_DATA - 1; i >= 0; --i) { - if (pdata->core_data[i] && - !pdata->core_data[i]->is_pkg_data) { - return true; - } - } - return false; + ida_destroy(&pdata->ida); + kfree(pdata); + platform_device_unregister(pdev); } -static void __cpuinit get_core_online(unsigned int cpu) +static int coretemp_cpu_online(unsigned int cpu) { - struct cpuinfo_x86 *c = &cpu_data(cpu); struct platform_device *pdev = coretemp_get_pdev(cpu); - int err; + struct cpuinfo_x86 *c = &cpu_data(cpu); + struct platform_data *pdata; + + /* + * Don't execute this on resume as the offline callback did + * not get executed on suspend. + */ + if (cpuhp_tasks_frozen) + return 0; /* * CPUID.06H.EAX[0] indicates whether the CPU has thermal @@ -693,12 +673,15 @@ static void __cpuinit get_core_online(unsigned int cpu) * without thermal sensors will be filtered out. */ if (!cpu_has(c, X86_FEATURE_DTHERM)) - return; + return -ENODEV; + + pdata = platform_get_drvdata(pdev); + if (!pdata->hwmon_dev) { + struct device *hwmon; - if (!pdev) { /* Check the microcode version of the CPU */ if (chk_ucode_version(cpu)) - return; + return -EINVAL; /* * Alright, we have DTS support. @@ -706,98 +689,97 @@ static void __cpuinit get_core_online(unsigned int cpu) * online. So, initialize per-pkg data structures and * then bring this core online. */ - err = coretemp_device_add(cpu); - if (err) - return; + hwmon = hwmon_device_register_with_groups(&pdev->dev, DRVNAME, + pdata, NULL); + if (IS_ERR(hwmon)) + return PTR_ERR(hwmon); + pdata->hwmon_dev = hwmon; + /* * Check whether pkgtemp support is available. * If so, add interfaces for pkgtemp. */ if (cpu_has(c, X86_FEATURE_PTS)) - coretemp_add_core(cpu, 1); + coretemp_add_core(pdev, cpu, 1); } + /* - * Physical CPU device already exists. - * So, just add interfaces for this core. + * Check whether a thread sibling is already online. If not add the + * interface for this CPU core. */ - coretemp_add_core(cpu, 0); + if (!cpumask_intersects(&pdata->cpumask, topology_sibling_cpumask(cpu))) + coretemp_add_core(pdev, cpu, 0); + + cpumask_set_cpu(cpu, &pdata->cpumask); + return 0; } -static void __cpuinit put_core_offline(unsigned int cpu) +static int coretemp_cpu_offline(unsigned int cpu) { - int i, indx; - struct platform_data *pdata; struct platform_device *pdev = coretemp_get_pdev(cpu); + struct platform_data *pd; + struct temp_data *tdata; + int target; - /* If the physical CPU device does not exist, just return */ - if (!pdev) - return; - - pdata = platform_get_drvdata(pdev); + /* No need to tear down any interfaces for suspend */ + if (cpuhp_tasks_frozen) + return 0; - indx = TO_ATTR_NO(cpu); + /* If the physical CPU device does not exist, just return */ + pd = platform_get_drvdata(pdev); + if (!pd->hwmon_dev) + return 0; - /* The core id is too big, just return */ - if (indx > MAX_CORE_DATA - 1) - return; + tdata = get_temp_data(pd, cpu); - if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu) - coretemp_remove_core(pdata, &pdev->dev, indx); + cpumask_clear_cpu(cpu, &pd->cpumask); /* - * If a HT sibling of a core is taken offline, but another HT sibling - * of the same core is still online, register the alternate sibling. - * This ensures that exactly one set of attributes is provided as long - * as at least one HT sibling of a core is online. + * If this is the last thread sibling, remove the CPU core + * interface, If there is still a sibling online, transfer the + * target cpu of that core interface to it. */ - for_each_sibling(i, cpu) { - if (i != cpu) { - get_core_online(i); - /* - * Display temperature sensor data for one HT sibling - * per core only, so abort the loop after one such - * sibling has been found. - */ - break; - } + target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu)); + if (target >= nr_cpu_ids) { + coretemp_remove_core(pd, tdata); + } else if (tdata && tdata->cpu == cpu) { + mutex_lock(&tdata->update_lock); + tdata->cpu = target; + mutex_unlock(&tdata->update_lock); } + /* - * If all cores in this pkg are offline, remove the device. - * coretemp_device_remove calls unregister_platform_device, - * which in turn calls coretemp_remove. This removes the - * pkgtemp entry and does other clean ups. + * If all cores in this pkg are offline, remove the interface. */ - if (!is_any_core_online(pdata)) - coretemp_device_remove(cpu); -} + tdata = get_temp_data(pd, -1); + if (cpumask_empty(&pd->cpumask)) { + if (tdata) + coretemp_remove_core(pd, tdata); + hwmon_device_unregister(pd->hwmon_dev); + pd->hwmon_dev = NULL; + return 0; + } -static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long) hcpu; - - switch (action) { - case CPU_ONLINE: - case CPU_DOWN_FAILED: - get_core_online(cpu); - break; - case CPU_DOWN_PREPARE: - put_core_offline(cpu); - break; + /* + * Check whether this core is the target for the package + * interface. We need to assign it to some other cpu. + */ + if (tdata && tdata->cpu == cpu) { + target = cpumask_first(&pd->cpumask); + mutex_lock(&tdata->update_lock); + tdata->cpu = target; + mutex_unlock(&tdata->update_lock); } - return NOTIFY_OK; + return 0; } - -static struct notifier_block coretemp_cpu_notifier __refdata = { - .notifier_call = coretemp_cpu_callback, -}; - static const struct x86_cpu_id __initconst coretemp_ids[] = { - { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, + X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); +static enum cpuhp_state coretemp_hp_online; + static int __init coretemp_init(void) { int i, err; @@ -805,59 +787,51 @@ static int __init coretemp_init(void) /* * CPUID.06H.EAX[0] indicates whether the CPU has thermal * sensors. We check this bit only, all the early CPUs - * without thermal sensors will be filtered out. + * without thermal sensors will be filtered out. This + * includes all the Family 5 and Family 15 (Pentium 4) + * models, since they never set the CPUID bit. */ if (!x86_match_cpu(coretemp_ids)) return -ENODEV; - err = platform_driver_register(&coretemp_driver); - if (err) - goto exit; - - get_online_cpus(); - for_each_online_cpu(i) - get_core_online(i); + max_zones = topology_max_packages() * topology_max_dies_per_package(); + zone_devices = kcalloc(max_zones, sizeof(struct platform_device *), + GFP_KERNEL); + if (!zone_devices) + return -ENOMEM; -#ifndef CONFIG_HOTPLUG_CPU - if (list_empty(&pdev_list)) { - put_online_cpus(); - err = -ENODEV; - goto exit_driver_unreg; + for (i = 0; i < max_zones; i++) { + err = coretemp_device_add(i); + if (err) + goto outzone; } -#endif - register_hotcpu_notifier(&coretemp_cpu_notifier); - put_online_cpus(); + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online", + coretemp_cpu_online, coretemp_cpu_offline); + if (err < 0) + goto outzone; + coretemp_hp_online = err; return 0; -#ifndef CONFIG_HOTPLUG_CPU -exit_driver_unreg: - platform_driver_unregister(&coretemp_driver); -#endif -exit: +outzone: + while (i--) + coretemp_device_remove(i); + kfree(zone_devices); return err; } +module_init(coretemp_init) static void __exit coretemp_exit(void) { - struct pdev_entry *p, *n; - - get_online_cpus(); - unregister_hotcpu_notifier(&coretemp_cpu_notifier); - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } - mutex_unlock(&pdev_list_mutex); - put_online_cpus(); - platform_driver_unregister(&coretemp_driver); + int i; + + cpuhp_remove_state(coretemp_hp_online); + for (i = 0; i < max_zones; i++) + coretemp_device_remove(i); + kfree(zone_devices); } +module_exit(coretemp_exit) MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); MODULE_DESCRIPTION("Intel Core temperature monitor"); MODULE_LICENSE("GPL"); - -module_init(coretemp_init) -module_exit(coretemp_exit) |
