Skip to content

Commit ada0728

Browse files
committed
hwmon: (i5500_temp) New driver for the Intel 5500/5520/X58 chipsets
The Intel 5500, 5520 and X58 chipsets embed a digital thermal sensor. This new driver supports it. Note that on many boards the sensor seems to be disabled and reports the minimum value (36.5 degrees Celsius) all the time. Signed-off-by: Jean Delvare <[email protected]> Tested-by: Romain Dolbeau <[email protected]> Reviewed-by: Guenter Roeck <[email protected]>
1 parent ec6f34e commit ada0728

File tree

3 files changed

+212
-0
lines changed

3 files changed

+212
-0
lines changed

drivers/hwmon/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,16 @@ config SENSORS_IIO_HWMON
574574
for those channels specified in the map. This map can be provided
575575
either via platform data or the device tree bindings.
576576

577+
config SENSORS_I5500
578+
tristate "Intel 5500/5520/X58 temperature sensor"
579+
depends on X86 && PCI
580+
help
581+
If you say yes here you get support for the temperature
582+
sensor inside the Intel 5500, 5520 and X58 chipsets.
583+
584+
This driver can also be built as a module. If so, the module
585+
will be called i5500_temp.
586+
577587
config SENSORS_CORETEMP
578588
tristate "Intel Core/Core2/Atom temperature sensor"
579589
depends on X86

drivers/hwmon/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
6868
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
6969
obj-$(CONFIG_SENSORS_HTU21) += htu21.o
7070
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
71+
obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
7172
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
7273
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
7374
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o

drivers/hwmon/i5500_temp.c

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* i5500_temp - Driver for Intel 5500/5520/X58 chipset thermal sensor
3+
*
4+
* Copyright (C) 2012, 2014 Jean Delvare <[email protected]>
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*/
16+
17+
#include <linux/module.h>
18+
#include <linux/init.h>
19+
#include <linux/slab.h>
20+
#include <linux/jiffies.h>
21+
#include <linux/pci.h>
22+
#include <linux/hwmon.h>
23+
#include <linux/hwmon-sysfs.h>
24+
#include <linux/err.h>
25+
#include <linux/mutex.h>
26+
27+
/* Register definitions from datasheet */
28+
#define REG_TSTHRCATA 0xE2
29+
#define REG_TSCTRL 0xE8
30+
#define REG_TSTHRRPEX 0xEB
31+
#define REG_TSTHRLO 0xEC
32+
#define REG_TSTHRHI 0xEE
33+
#define REG_CTHINT 0xF0
34+
#define REG_TSFSC 0xF3
35+
#define REG_CTSTS 0xF4
36+
#define REG_TSTHRRQPI 0xF5
37+
#define REG_CTCTRL 0xF7
38+
#define REG_TSTIMER 0xF8
39+
40+
struct i5500_temp_data {
41+
struct device *hwmon_dev;
42+
const char *name;
43+
};
44+
45+
/*
46+
* Sysfs stuff
47+
*/
48+
49+
/* Sensor resolution : 0.5 degree C */
50+
static ssize_t show_temp(struct device *dev,
51+
struct device_attribute *devattr, char *buf)
52+
{
53+
struct pci_dev *pdev = to_pci_dev(dev);
54+
long temp;
55+
u16 tsthrhi;
56+
s8 tsfsc;
57+
58+
pci_read_config_word(pdev, REG_TSTHRHI, &tsthrhi);
59+
pci_read_config_byte(pdev, REG_TSFSC, &tsfsc);
60+
temp = ((long)tsthrhi - tsfsc) * 500;
61+
62+
return sprintf(buf, "%ld\n", temp);
63+
}
64+
65+
static ssize_t show_thresh(struct device *dev,
66+
struct device_attribute *devattr, char *buf)
67+
{
68+
struct pci_dev *pdev = to_pci_dev(dev);
69+
int reg = to_sensor_dev_attr(devattr)->index;
70+
long temp;
71+
u16 tsthr;
72+
73+
pci_read_config_word(pdev, reg, &tsthr);
74+
temp = tsthr * 500;
75+
76+
return sprintf(buf, "%ld\n", temp);
77+
}
78+
79+
static ssize_t show_alarm(struct device *dev,
80+
struct device_attribute *devattr, char *buf)
81+
{
82+
struct pci_dev *pdev = to_pci_dev(dev);
83+
int nr = to_sensor_dev_attr(devattr)->index;
84+
u8 ctsts;
85+
86+
pci_read_config_byte(pdev, REG_CTSTS, &ctsts);
87+
return sprintf(buf, "%u\n", (unsigned int)ctsts & (1 << nr));
88+
}
89+
90+
static ssize_t show_name(struct device *dev, struct device_attribute
91+
*devattr, char *buf)
92+
{
93+
struct i5500_temp_data *data = dev_get_drvdata(dev);
94+
95+
return sprintf(buf, "%s\n", data->name);
96+
}
97+
98+
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
99+
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_thresh, NULL, 0xE2);
100+
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_thresh, NULL, 0xEC);
101+
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_thresh, NULL, 0xEE);
102+
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
103+
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 1);
104+
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
105+
106+
static struct attribute *i5500_temp_attributes[] = {
107+
&dev_attr_temp1_input.attr,
108+
&sensor_dev_attr_temp1_crit.dev_attr.attr,
109+
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
110+
&sensor_dev_attr_temp1_max.dev_attr.attr,
111+
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
112+
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
113+
&dev_attr_name.attr,
114+
NULL
115+
};
116+
117+
static const struct attribute_group i5500_temp_group = {
118+
.attrs = i5500_temp_attributes,
119+
};
120+
121+
static const struct pci_device_id i5500_temp_ids[] = {
122+
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x3438) },
123+
{ 0 },
124+
};
125+
126+
MODULE_DEVICE_TABLE(pci, i5500_temp_ids);
127+
128+
static int i5500_temp_probe(struct pci_dev *pdev,
129+
const struct pci_device_id *id)
130+
{
131+
int err;
132+
struct i5500_temp_data *data;
133+
134+
data = kzalloc(sizeof(struct i5500_temp_data), GFP_KERNEL);
135+
if (!data) {
136+
err = -ENOMEM;
137+
goto exit;
138+
}
139+
140+
data->name = "intel5500";
141+
dev_set_drvdata(&pdev->dev, data);
142+
143+
err = pci_enable_device(pdev);
144+
if (err) {
145+
dev_err(&pdev->dev, "Failed to enable device\n");
146+
goto exit_free;
147+
}
148+
149+
/* Register sysfs hooks */
150+
err = sysfs_create_group(&pdev->dev.kobj, &i5500_temp_group);
151+
if (err)
152+
goto exit_free;
153+
154+
data->hwmon_dev = hwmon_device_register(&pdev->dev);
155+
if (IS_ERR(data->hwmon_dev)) {
156+
err = PTR_ERR(data->hwmon_dev);
157+
goto exit_remove;
158+
}
159+
160+
return 0;
161+
162+
exit_remove:
163+
sysfs_remove_group(&pdev->dev.kobj, &i5500_temp_group);
164+
exit_free:
165+
kfree(data);
166+
exit:
167+
return err;
168+
}
169+
170+
static void i5500_temp_remove(struct pci_dev *pdev)
171+
{
172+
struct i5500_temp_data *data = dev_get_drvdata(&pdev->dev);
173+
174+
hwmon_device_unregister(data->hwmon_dev);
175+
sysfs_remove_group(&pdev->dev.kobj, &i5500_temp_group);
176+
kfree(data);
177+
}
178+
179+
static struct pci_driver i5500_temp_driver = {
180+
.name = "i5500_temp",
181+
.id_table = i5500_temp_ids,
182+
.probe = i5500_temp_probe,
183+
.remove = i5500_temp_remove,
184+
};
185+
186+
static int __init i5500_temp_init(void)
187+
{
188+
return pci_register_driver(&i5500_temp_driver);
189+
}
190+
191+
static void __exit i5500_temp_exit(void)
192+
{
193+
pci_unregister_driver(&i5500_temp_driver);
194+
}
195+
196+
MODULE_AUTHOR("Jean Delvare <[email protected]>");
197+
MODULE_DESCRIPTION("Intel 5500/5520/X58 chipset thermal sensor driver");
198+
MODULE_LICENSE("GPL");
199+
200+
module_init(i5500_temp_init)
201+
module_exit(i5500_temp_exit)

0 commit comments

Comments
 (0)