diff options
Diffstat (limited to 'drivers/hwmon/k8temp.c')
-rw-r--r-- | drivers/hwmon/k8temp.c | 55 |
1 files changed, 46 insertions, 9 deletions
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index bd2bde0ef95e..1fe995111841 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/hwmon-sysfs.h> | 31 | #include <linux/hwmon-sysfs.h> |
32 | #include <linux/err.h> | 32 | #include <linux/err.h> |
33 | #include <linux/mutex.h> | 33 | #include <linux/mutex.h> |
34 | #include <asm/processor.h> | ||
34 | 35 | ||
35 | #define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000) | 36 | #define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000) |
36 | #define REG_TEMP 0xe4 | 37 | #define REG_TEMP 0xe4 |
@@ -47,6 +48,8 @@ struct k8temp_data { | |||
47 | /* registers values */ | 48 | /* registers values */ |
48 | u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */ | 49 | u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */ |
49 | u32 temp[2][2]; /* core, place */ | 50 | u32 temp[2][2]; /* core, place */ |
51 | u8 swap_core_select; /* meaning of SEL_CORE is inverted */ | ||
52 | u32 temp_offset; | ||
50 | }; | 53 | }; |
51 | 54 | ||
52 | static struct k8temp_data *k8temp_update_device(struct device *dev) | 55 | static struct k8temp_data *k8temp_update_device(struct device *dev) |
@@ -114,10 +117,15 @@ static ssize_t show_temp(struct device *dev, | |||
114 | to_sensor_dev_attr_2(devattr); | 117 | to_sensor_dev_attr_2(devattr); |
115 | int core = attr->nr; | 118 | int core = attr->nr; |
116 | int place = attr->index; | 119 | int place = attr->index; |
120 | int temp; | ||
117 | struct k8temp_data *data = k8temp_update_device(dev); | 121 | struct k8temp_data *data = k8temp_update_device(dev); |
118 | 122 | ||
119 | return sprintf(buf, "%d\n", | 123 | if (data->swap_core_select) |
120 | TEMP_FROM_REG(data->temp[core][place])); | 124 | core = core ? 0 : 1; |
125 | |||
126 | temp = TEMP_FROM_REG(data->temp[core][place]) + data->temp_offset; | ||
127 | |||
128 | return sprintf(buf, "%d\n", temp); | ||
121 | } | 129 | } |
122 | 130 | ||
123 | /* core, place */ | 131 | /* core, place */ |
@@ -141,20 +149,49 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, | |||
141 | int err; | 149 | int err; |
142 | u8 scfg; | 150 | u8 scfg; |
143 | u32 temp; | 151 | u32 temp; |
152 | u8 model, stepping; | ||
144 | struct k8temp_data *data; | 153 | struct k8temp_data *data; |
145 | u32 cpuid = cpuid_eax(1); | ||
146 | |||
147 | /* this feature should be available since SH-C0 core */ | ||
148 | if ((cpuid == 0xf40) || (cpuid == 0xf50) || (cpuid == 0xf51)) { | ||
149 | err = -ENODEV; | ||
150 | goto exit; | ||
151 | } | ||
152 | 154 | ||
153 | if (!(data = kzalloc(sizeof(struct k8temp_data), GFP_KERNEL))) { | 155 | if (!(data = kzalloc(sizeof(struct k8temp_data), GFP_KERNEL))) { |
154 | err = -ENOMEM; | 156 | err = -ENOMEM; |
155 | goto exit; | 157 | goto exit; |
156 | } | 158 | } |
157 | 159 | ||
160 | model = boot_cpu_data.x86_model; | ||
161 | stepping = boot_cpu_data.x86_mask; | ||
162 | |||
163 | switch (boot_cpu_data.x86) { | ||
164 | case 0xf: | ||
165 | /* feature available since SH-C0, exclude older revisions */ | ||
166 | if (((model == 4) && (stepping == 0)) || | ||
167 | ((model == 5) && (stepping <= 1))) { | ||
168 | err = -ENODEV; | ||
169 | goto exit_free; | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | * AMD NPT family 0fh, i.e. RevF and RevG: | ||
174 | * meaning of SEL_CORE bit is inverted | ||
175 | */ | ||
176 | if (model >= 0x40) { | ||
177 | data->swap_core_select = 1; | ||
178 | dev_warn(&pdev->dev, "Temperature readouts might be " | ||
179 | "wrong - check erratum #141\n"); | ||
180 | } | ||
181 | |||
182 | if ((model >= 0x69) && | ||
183 | !(model == 0xc1 || model == 0x6c || model == 0x7c)) { | ||
184 | /* | ||
185 | * RevG desktop CPUs (i.e. no socket S1G1 parts) | ||
186 | * need additional offset, otherwise reported | ||
187 | * temperature is below ambient temperature | ||
188 | */ | ||
189 | data->temp_offset = 21000; | ||
190 | } | ||
191 | |||
192 | break; | ||
193 | } | ||
194 | |||
158 | pci_read_config_byte(pdev, REG_TEMP, &scfg); | 195 | pci_read_config_byte(pdev, REG_TEMP, &scfg); |
159 | scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ | 196 | scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ |
160 | pci_write_config_byte(pdev, REG_TEMP, scfg); | 197 | pci_write_config_byte(pdev, REG_TEMP, scfg); |