18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * i5500_temp - Driver for Intel 5500/5520/X58 chipset thermal sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012, 2014 Jean Delvare <jdelvare@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 158c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Register definitions from datasheet */ 208c2ecf20Sopenharmony_ci#define REG_TSTHRCATA 0xE2 218c2ecf20Sopenharmony_ci#define REG_TSCTRL 0xE8 228c2ecf20Sopenharmony_ci#define REG_TSTHRRPEX 0xEB 238c2ecf20Sopenharmony_ci#define REG_TSTHRLO 0xEC 248c2ecf20Sopenharmony_ci#define REG_TSTHRHI 0xEE 258c2ecf20Sopenharmony_ci#define REG_CTHINT 0xF0 268c2ecf20Sopenharmony_ci#define REG_TSFSC 0xF3 278c2ecf20Sopenharmony_ci#define REG_CTSTS 0xF4 288c2ecf20Sopenharmony_ci#define REG_TSTHRRQPI 0xF5 298c2ecf20Sopenharmony_ci#define REG_CTCTRL 0xF7 308c2ecf20Sopenharmony_ci#define REG_TSTIMER 0xF8 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Sysfs stuff 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Sensor resolution : 0.5 degree C */ 378c2ecf20Sopenharmony_cistatic ssize_t temp1_input_show(struct device *dev, 388c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->parent); 418c2ecf20Sopenharmony_ci long temp; 428c2ecf20Sopenharmony_ci u16 tsthrhi; 438c2ecf20Sopenharmony_ci s8 tsfsc; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci pci_read_config_word(pdev, REG_TSTHRHI, &tsthrhi); 468c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, REG_TSFSC, &tsfsc); 478c2ecf20Sopenharmony_ci temp = ((long)tsthrhi - tsfsc) * 500; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", temp); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic ssize_t thresh_show(struct device *dev, 538c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->parent); 568c2ecf20Sopenharmony_ci int reg = to_sensor_dev_attr(devattr)->index; 578c2ecf20Sopenharmony_ci long temp; 588c2ecf20Sopenharmony_ci u16 tsthr; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci pci_read_config_word(pdev, reg, &tsthr); 618c2ecf20Sopenharmony_ci temp = tsthr * 500; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", temp); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic ssize_t alarm_show(struct device *dev, 678c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->parent); 708c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(devattr)->index; 718c2ecf20Sopenharmony_ci u8 ctsts; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, REG_CTSTS, &ctsts); 748c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (unsigned int)ctsts & (1 << nr)); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(temp1_input); 788c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_crit, thresh, 0xE2); 798c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, thresh, 0xEC); 808c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max, thresh, 0xEE); 818c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 0); 828c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic struct attribute *i5500_temp_attrs[] = { 858c2ecf20Sopenharmony_ci &dev_attr_temp1_input.attr, 868c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit.dev_attr.attr, 878c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, 888c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 898c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, 908c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, 918c2ecf20Sopenharmony_ci NULL 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(i5500_temp); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic const struct pci_device_id i5500_temp_ids[] = { 978c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x3438) }, 988c2ecf20Sopenharmony_ci { 0 }, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i5500_temp_ids); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int i5500_temp_probe(struct pci_dev *pdev, 1048c2ecf20Sopenharmony_ci const struct pci_device_id *id) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int err; 1078c2ecf20Sopenharmony_ci struct device *hwmon_dev; 1088c2ecf20Sopenharmony_ci u32 tstimer; 1098c2ecf20Sopenharmony_ci s8 tsfsc; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci err = pcim_enable_device(pdev); 1128c2ecf20Sopenharmony_ci if (err) { 1138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable device\n"); 1148c2ecf20Sopenharmony_ci return err; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, REG_TSFSC, &tsfsc); 1188c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, REG_TSTIMER, &tstimer); 1198c2ecf20Sopenharmony_ci if (tsfsc == 0x7F && tstimer == 0x07D30D40) { 1208c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Sensor seems to be disabled\n"); 1218c2ecf20Sopenharmony_ci return -ENODEV; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, 1258c2ecf20Sopenharmony_ci "intel5500", NULL, 1268c2ecf20Sopenharmony_ci i5500_temp_groups); 1278c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic struct pci_driver i5500_temp_driver = { 1318c2ecf20Sopenharmony_ci .name = "i5500_temp", 1328c2ecf20Sopenharmony_ci .id_table = i5500_temp_ids, 1338c2ecf20Sopenharmony_ci .probe = i5500_temp_probe, 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cimodule_pci_driver(i5500_temp_driver); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 1398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel 5500/5520/X58 chipset thermal sensor driver"); 1408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 141