Recent Intel chipsets have support for a PCI device providing access to on-board thermal monitoring. Previous versions have merely used this to allow the BIOS to set trip points, but Ibex Peak documents a set of registers to read values. This driver implements an hwmon interface to read them. Signed-off-by: Matthew Garrett <mjg@xxxxxxxxxx> --- The CPU temperature is somewhat off on the test hardware I have here - I'm in the process of determining whether the BIOS has programmed incorrect values or whether I'm missing a correction step. MAINTAINERS | 6 + drivers/hwmon/Kconfig | 9 + drivers/hwmon/Makefile | 1 + drivers/hwmon/intel_thermal.c | 376 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 392 insertions(+), 0 deletions(-) create mode 100644 drivers/hwmon/intel_thermal.c diff --git a/MAINTAINERS b/MAINTAINERS index 98d5ca1..e038300 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2821,6 +2821,12 @@ F: drivers/net/igb/ F: drivers/net/ixgb/ F: drivers/net/ixgbe/ +INTEL PCI THERMAL SUBSYSTEM DRIVER +M: Matthew Garrett <mjg@xxxxxxxxxx> +L: lm-sensors@xxxxxxxxxxxxxx +S: Maintained +F: drivers/hwmon/intel_thermal.c + INTEL PRO/WIRELESS 2100 NETWORK CONNECTION SUPPORT M: Zhu Yi <yi.zhu@xxxxxxxxx> M: Reinette Chatre <reinette.chatre@xxxxxxxxx> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 9e640c6..f923f7a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -414,6 +414,15 @@ config SENSORS_IBMPEX This driver can also be built as a module. If so, the module will be called ibmpex. +config SENSORS_INTEL + tristate "Intel Thermal Subsystem" + help + Driver for the Intel Thermal Subsystem found in some PM55-based + machines. + + This driver can also be built as a module. If so, the module will + be called intel_thermal. + config SENSORS_IT87 tristate "ITE IT87xx and compatibles" select HWMON_VID diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 33c2ee1..a138df9 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o +obj-$(CONFIG_SENSORS_INTEL) += intel_thermal.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o diff --git a/drivers/hwmon/intel_thermal.c b/drivers/hwmon/intel_thermal.c new file mode 100644 index 0000000..9b8296e --- /dev/null +++ b/drivers/hwmon/intel_thermal.c @@ -0,0 +1,376 @@ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/io.h> + +#define INTEL_THERMAL_SENSOR_TEMP 0x03 +#define INTEL_THERMAL_CORE_TEMP 0x30 +#define INTEL_THERMAL_PROCESSOR_TEMP 0x60 +#define INTEL_THERMAL_DIMM_TEMP 0xb0 +#define INTEL_THERMAL_INTERNAL_TEMP 0xd8 + +static void __iomem *intel_thermal_addr; +static struct device *intel_thermal_dev; + +static struct pci_device_id intel_thermal_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x3b32) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, intel_thermal_pci_ids); + +struct intel_thermal_sensor { + int (*read_sensor)(void); + char *name; + struct attribute_group *attrs; +}; + +static struct intel_thermal_sensor intel_thermal_sensors[]; + +static ssize_t intel_thermal_sensor_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int sensor = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%s\n", intel_thermal_sensors[sensor].name); +} + +static ssize_t intel_thermal_sensor_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int sensor = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", + intel_thermal_sensors[sensor].read_sensor()); +} + +static int intel_thermal_sensor_temp(void) +{ + int value = readb(intel_thermal_addr + INTEL_THERMAL_SENSOR_TEMP); + + if (value > 0x7f) + return -1; + + return value * 1000; +} + +static int intel_thermal_core_temp(int core) +{ + int temp; + int value; + + value = readw(intel_thermal_addr + INTEL_THERMAL_CORE_TEMP + 2 * core); + + if (!value || (value & 0x8000)) + return -1; + + temp = ((value & 0xffc0) >> 6) * 1000; + temp += ((value & 0x3f) * 1000) / 64; + + return temp; +} + +static int intel_thermal_core0_temp(void) +{ + return intel_thermal_core_temp(0); +} + +static int intel_thermal_core1_temp(void) +{ + return intel_thermal_core_temp(1); +} + +static int intel_thermal_processor_temp(void) +{ + int value = readw(intel_thermal_addr + INTEL_THERMAL_PROCESSOR_TEMP); + + return (value & 0xff) * 1000; +} + +static int intel_thermal_dimm_temp(int dimm) +{ + int value = readl(intel_thermal_addr + INTEL_THERMAL_DIMM_TEMP); + int shift = dimm * 8; + int temp = (value & (0xff << shift)) >> shift; + + if (!temp || temp == 0xff) + return -1; + + return temp * 1000; +} + +static int intel_thermal_dimm0_temp(void) +{ + return intel_thermal_dimm_temp(0); +} + +static int intel_thermal_dimm1_temp(void) +{ + return intel_thermal_dimm_temp(1); +} + +static int intel_thermal_dimm2_temp(void) +{ + return intel_thermal_dimm_temp(2); +} + +static int intel_thermal_dimm3_temp(void) +{ + return intel_thermal_dimm_temp(3); +} + +static int intel_thermal_internal_temp(void) +{ + int value = readl(intel_thermal_addr + INTEL_THERMAL_INTERNAL_TEMP); + int temp = value & 0xff; + + if (!temp) + return -1; + + return temp * 1000; +} + +SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, intel_thermal_sensor_show, NULL, 0); +SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, intel_thermal_sensor_label, NULL, 0); +SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, intel_thermal_sensor_show, NULL, 1); +SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, intel_thermal_sensor_label, NULL, 1); +SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, intel_thermal_sensor_show, NULL, 2); +SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, intel_thermal_sensor_label, NULL, 2); +SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, intel_thermal_sensor_show, NULL, 3); +SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, intel_thermal_sensor_label, NULL, 3); +SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, intel_thermal_sensor_show, NULL, 4); +SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, intel_thermal_sensor_label, NULL, 4); +SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, intel_thermal_sensor_show, NULL, 5); +SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO, intel_thermal_sensor_label, NULL, 5); +SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, intel_thermal_sensor_show, NULL, 6); +SENSOR_DEVICE_ATTR(temp7_label, S_IRUGO, intel_thermal_sensor_label, NULL, 6); +SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, intel_thermal_sensor_show, NULL, 7); +SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, intel_thermal_sensor_label, NULL, 7); +SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, intel_thermal_sensor_show, NULL, 8); +SENSOR_DEVICE_ATTR(temp9_label, S_IRUGO, intel_thermal_sensor_label, NULL, 8); + +static struct attribute *sensor_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_sensor_attribute_group = { + .attrs = sensor_attributes, +}; + +static struct attribute *core0_attributes[] = { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_core0_attribute_group = { + .attrs = core0_attributes, +}; + +static struct attribute *core1_attributes[] = { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_core1_attribute_group = { + .attrs = core1_attributes, +}; + +static struct attribute *processor_attributes[] = { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_processor_attribute_group = { + .attrs = processor_attributes, +}; + +static struct attribute *dimm0_attributes[] = { + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_dimm0_attribute_group = { + .attrs = dimm0_attributes, +}; + +static struct attribute *dimm1_attributes[] = { + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_dimm1_attribute_group = { + .attrs = dimm1_attributes, +}; + +static struct attribute *dimm2_attributes[] = { + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp7_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_dimm2_attribute_group = { + .attrs = dimm2_attributes, +}; + +static struct attribute *dimm3_attributes[] = { + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp8_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_dimm3_attribute_group = { + .attrs = dimm3_attributes, +}; + +static struct attribute *internal_attributes[] = { + &sensor_dev_attr_temp9_input.dev_attr.attr, + &sensor_dev_attr_temp9_label.dev_attr.attr, + NULL, +}; + +static struct attribute_group intel_internal_attribute_group = { + .attrs = internal_attributes, +}; + +static struct intel_thermal_sensor intel_thermal_sensors[] = { + {intel_thermal_sensor_temp, "thermal sensor", + &intel_sensor_attribute_group}, + {intel_thermal_core0_temp, "core 0", &intel_core0_attribute_group}, + {intel_thermal_core1_temp, "core 1", &intel_core1_attribute_group}, + {intel_thermal_processor_temp, "processor", + &intel_processor_attribute_group}, + {intel_thermal_dimm0_temp, "dimm 0", &intel_dimm0_attribute_group}, + {intel_thermal_dimm1_temp, "dimm 1", &intel_dimm1_attribute_group}, + {intel_thermal_dimm2_temp, "dimm 2", &intel_dimm2_attribute_group}, + {intel_thermal_dimm3_temp, "dimm 3", &intel_dimm3_attribute_group}, + {intel_thermal_internal_temp, "internal temperature", + &intel_internal_attribute_group}, + {0, }, +}; + +static ssize_t show_name(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "intel\n"); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static int __devinit intel_thermal_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret; + int i; + + ret = pci_request_region(pdev, 0, "Intel Thermal Subsystem"); + if (ret) { + dev_err(&pdev->dev, "failed to request region\n"); + return -ENODEV; + } + + intel_thermal_addr = pci_ioremap_bar(pdev, 0); + if (!intel_thermal_addr) { + dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENODEV; + goto release; + } + + for (i = 0; intel_thermal_sensors[i].read_sensor != NULL; i++) { + int temp = intel_thermal_sensors[i].read_sensor(); + + if (temp <= 0) + continue; + + ret = sysfs_create_group(&pdev->dev.kobj, + intel_thermal_sensors[i].attrs); + if (ret) { + dev_err(&pdev->dev, "failed to create attrs\n"); + ret = -ENOMEM; + goto device; + } + } + + ret = device_create_file(&pdev->dev, &dev_attr_name); + + if (ret) { + dev_err(&pdev->dev, "failed to create attr\n"); + ret = -ENOMEM; + goto device; + } + + intel_thermal_dev = hwmon_device_register(&pdev->dev); + + if (!intel_thermal_dev) { + dev_err(&pdev->dev, "failed to create hwmon device\n"); + ret = -ENOMEM; + goto groups; + } + + return 0; +device: + device_remove_file(&pdev->dev, &dev_attr_name); + hwmon_device_unregister(intel_thermal_dev); +groups: + for (i = 0; intel_thermal_sensors[i].read_sensor; i++) { + sysfs_remove_group(&pdev->dev.kobj, + intel_thermal_sensors[i].attrs); + } + iounmap(intel_thermal_addr); +release: + pci_release_region(pdev, 0); + return ret; +} + +static void __devexit intel_thermal_pci_remove(struct pci_dev *pdev) +{ + int i; + + device_remove_file(&pdev->dev, &dev_attr_name); + + for (i = 0; intel_thermal_sensors[i].read_sensor; i++) { + sysfs_remove_group(&pdev->dev.kobj, + intel_thermal_sensors[i].attrs); + } + + hwmon_device_unregister(intel_thermal_dev); + + iounmap(intel_thermal_addr); + pci_release_region(pdev, 0); +} + +static struct pci_driver intel_thermal_pci_driver = { + .name = "intel-thermal", + .id_table = intel_thermal_pci_ids, + .probe = intel_thermal_pci_probe, + .remove = intel_thermal_pci_remove, +}; + +static int __init intel_thermal_init(void) +{ + return pci_register_driver(&intel_thermal_pci_driver); +} + +static void __exit intel_thermal_exit(void) +{ + pci_unregister_driver(&intel_thermal_pci_driver); +} + +module_init(intel_thermal_init) +module_exit(intel_thermal_exit); + +MODULE_AUTHOR("Matthew Garrett <mjg@xxxxxxxxxx>"); +MODULE_DESCRIPTION("Intel thermal subsystem hwmon driver"); +MODULE_LICENSE("GPL"); -- 1.6.5.2 _______________________________________________ lm-sensors mailing list lm-sensors@xxxxxxxxxxxxxx http://lists.lm-sensors.org/mailman/listinfo/lm-sensors