|
@@ -36,6 +36,7 @@
|
|
|
#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>
|
|
@@ -176,20 +177,33 @@ static ssize_t show_temp(struct device *dev,
|
|
|
/* 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;
|
|
|
- }
|
|
|
+ /*
|
|
|
+ * 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 = 1;
|
|
|
tdata->last_updated = jiffies;
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&tdata->update_lock);
|
|
|
- return tdata->valid ? sprintf(buf, "%d\n", tdata->temp) : -EAGAIN;
|
|
|
+ return sprintf(buf, "%d\n", tdata->temp);
|
|
|
}
|
|
|
|
|
|
+struct tjmax_pci {
|
|
|
+ unsigned int device;
|
|
|
+ int tjmax;
|
|
|
+};
|
|
|
+
|
|
|
+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;
|
|
@@ -198,9 +212,6 @@ struct tjmax {
|
|
|
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 {
|
|
@@ -222,8 +233,11 @@ static const struct tjmax_model tjmax_model_table[] = {
|
|
|
* 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) */
|
|
|
+ { 0x35, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */
|
|
|
+ { 0x36, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx)
|
|
|
+ * Also matches S12x0 (stepping 9), covered by
|
|
|
+ * PCI table
|
|
|
+ */
|
|
|
};
|
|
|
|
|
|
static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
|
@@ -236,8 +250,20 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
|
|
int err;
|
|
|
u32 eax, edx;
|
|
|
int i;
|
|
|
+ struct pci_dev *host_bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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)
|
|
|
+ return tjmax_pci_table[i].tjmax;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- /* explicit tjmax table entries override heuristics */
|
|
|
for (i = 0; i < ARRAY_SIZE(tjmax_table); i++) {
|
|
|
if (strstr(c->x86_model_id, tjmax_table[i].id))
|
|
|
return tjmax_table[i].tjmax;
|
|
@@ -343,12 +369,12 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
|
|
if (cpu_has_tjmax(c))
|
|
|
dev_warn(dev, "Unable to read TjMax from CPU %u\n", id);
|
|
|
} else {
|
|
|
- val = (eax >> 16) & 0xff;
|
|
|
+ val = (eax >> 16) & 0x7f;
|
|
|
/*
|
|
|
* If the TjMax is not plausible, an assumption
|
|
|
* will be used
|
|
|
*/
|
|
|
- if (val) {
|
|
|
+ if (val >= 85) {
|
|
|
dev_dbg(dev, "TjMax is %d degrees C\n", val);
|
|
|
return val * 1000;
|
|
|
}
|