18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright(c) 2007 - 2018 Intel Corporation. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include "igb.h" 58c2ecf20Sopenharmony_ci#include "e1000_82575.h" 68c2ecf20Sopenharmony_ci#include "e1000_hw.h" 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 118c2ecf20Sopenharmony_ci#include <linux/kobject.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 158c2ecf20Sopenharmony_ci#include <linux/pci.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#ifdef CONFIG_IGB_HWMON 188c2ecf20Sopenharmony_cistatic struct i2c_board_info i350_sensor_info = { 198c2ecf20Sopenharmony_ci I2C_BOARD_INFO("i350bb", (0Xf8 >> 1)), 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* hwmon callback functions */ 238c2ecf20Sopenharmony_cistatic ssize_t igb_hwmon_show_location(struct device *dev, 248c2ecf20Sopenharmony_ci struct device_attribute *attr, 258c2ecf20Sopenharmony_ci char *buf) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct hwmon_attr *igb_attr = container_of(attr, struct hwmon_attr, 288c2ecf20Sopenharmony_ci dev_attr); 298c2ecf20Sopenharmony_ci return sprintf(buf, "loc%u\n", 308c2ecf20Sopenharmony_ci igb_attr->sensor->location); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic ssize_t igb_hwmon_show_temp(struct device *dev, 348c2ecf20Sopenharmony_ci struct device_attribute *attr, 358c2ecf20Sopenharmony_ci char *buf) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct hwmon_attr *igb_attr = container_of(attr, struct hwmon_attr, 388c2ecf20Sopenharmony_ci dev_attr); 398c2ecf20Sopenharmony_ci unsigned int value; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* reset the temp field */ 428c2ecf20Sopenharmony_ci igb_attr->hw->mac.ops.get_thermal_sensor_data(igb_attr->hw); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci value = igb_attr->sensor->temp; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* display millidegree */ 478c2ecf20Sopenharmony_ci value *= 1000; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic ssize_t igb_hwmon_show_cautionthresh(struct device *dev, 538c2ecf20Sopenharmony_ci struct device_attribute *attr, 548c2ecf20Sopenharmony_ci char *buf) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct hwmon_attr *igb_attr = container_of(attr, struct hwmon_attr, 578c2ecf20Sopenharmony_ci dev_attr); 588c2ecf20Sopenharmony_ci unsigned int value = igb_attr->sensor->caution_thresh; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* display millidegree */ 618c2ecf20Sopenharmony_ci value *= 1000; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic ssize_t igb_hwmon_show_maxopthresh(struct device *dev, 678c2ecf20Sopenharmony_ci struct device_attribute *attr, 688c2ecf20Sopenharmony_ci char *buf) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct hwmon_attr *igb_attr = container_of(attr, struct hwmon_attr, 718c2ecf20Sopenharmony_ci dev_attr); 728c2ecf20Sopenharmony_ci unsigned int value = igb_attr->sensor->max_op_thresh; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* display millidegree */ 758c2ecf20Sopenharmony_ci value *= 1000; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* igb_add_hwmon_attr - Create hwmon attr table for a hwmon sysfs file. 818c2ecf20Sopenharmony_ci * @ adapter: pointer to the adapter structure 828c2ecf20Sopenharmony_ci * @ offset: offset in the eeprom sensor data table 838c2ecf20Sopenharmony_ci * @ type: type of sensor data to display 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * For each file we want in hwmon's sysfs interface we need a device_attribute 868c2ecf20Sopenharmony_ci * This is included in our hwmon_attr struct that contains the references to 878c2ecf20Sopenharmony_ci * the data structures we need to get the data to display. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic int igb_add_hwmon_attr(struct igb_adapter *adapter, 908c2ecf20Sopenharmony_ci unsigned int offset, int type) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci int rc; 938c2ecf20Sopenharmony_ci unsigned int n_attr; 948c2ecf20Sopenharmony_ci struct hwmon_attr *igb_attr; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci n_attr = adapter->igb_hwmon_buff->n_hwmon; 978c2ecf20Sopenharmony_ci igb_attr = &adapter->igb_hwmon_buff->hwmon_list[n_attr]; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci switch (type) { 1008c2ecf20Sopenharmony_ci case IGB_HWMON_TYPE_LOC: 1018c2ecf20Sopenharmony_ci igb_attr->dev_attr.show = igb_hwmon_show_location; 1028c2ecf20Sopenharmony_ci snprintf(igb_attr->name, sizeof(igb_attr->name), 1038c2ecf20Sopenharmony_ci "temp%u_label", offset + 1); 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci case IGB_HWMON_TYPE_TEMP: 1068c2ecf20Sopenharmony_ci igb_attr->dev_attr.show = igb_hwmon_show_temp; 1078c2ecf20Sopenharmony_ci snprintf(igb_attr->name, sizeof(igb_attr->name), 1088c2ecf20Sopenharmony_ci "temp%u_input", offset + 1); 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci case IGB_HWMON_TYPE_CAUTION: 1118c2ecf20Sopenharmony_ci igb_attr->dev_attr.show = igb_hwmon_show_cautionthresh; 1128c2ecf20Sopenharmony_ci snprintf(igb_attr->name, sizeof(igb_attr->name), 1138c2ecf20Sopenharmony_ci "temp%u_max", offset + 1); 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case IGB_HWMON_TYPE_MAX: 1168c2ecf20Sopenharmony_ci igb_attr->dev_attr.show = igb_hwmon_show_maxopthresh; 1178c2ecf20Sopenharmony_ci snprintf(igb_attr->name, sizeof(igb_attr->name), 1188c2ecf20Sopenharmony_ci "temp%u_crit", offset + 1); 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci default: 1218c2ecf20Sopenharmony_ci rc = -EPERM; 1228c2ecf20Sopenharmony_ci return rc; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* These always the same regardless of type */ 1268c2ecf20Sopenharmony_ci igb_attr->sensor = 1278c2ecf20Sopenharmony_ci &adapter->hw.mac.thermal_sensor_data.sensor[offset]; 1288c2ecf20Sopenharmony_ci igb_attr->hw = &adapter->hw; 1298c2ecf20Sopenharmony_ci igb_attr->dev_attr.store = NULL; 1308c2ecf20Sopenharmony_ci igb_attr->dev_attr.attr.mode = 0444; 1318c2ecf20Sopenharmony_ci igb_attr->dev_attr.attr.name = igb_attr->name; 1328c2ecf20Sopenharmony_ci sysfs_attr_init(&igb_attr->dev_attr.attr); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci adapter->igb_hwmon_buff->attrs[n_attr] = &igb_attr->dev_attr.attr; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ++adapter->igb_hwmon_buff->n_hwmon; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void igb_sysfs_del_adapter(struct igb_adapter *adapter) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* called from igb_main.c */ 1468c2ecf20Sopenharmony_civoid igb_sysfs_exit(struct igb_adapter *adapter) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci igb_sysfs_del_adapter(adapter); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* called from igb_main.c */ 1528c2ecf20Sopenharmony_ciint igb_sysfs_init(struct igb_adapter *adapter) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct hwmon_buff *igb_hwmon; 1558c2ecf20Sopenharmony_ci struct i2c_client *client; 1568c2ecf20Sopenharmony_ci struct device *hwmon_dev; 1578c2ecf20Sopenharmony_ci unsigned int i; 1588c2ecf20Sopenharmony_ci int rc = 0; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* If this method isn't defined we don't support thermals */ 1618c2ecf20Sopenharmony_ci if (adapter->hw.mac.ops.init_thermal_sensor_thresh == NULL) 1628c2ecf20Sopenharmony_ci goto exit; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* Don't create thermal hwmon interface if no sensors present */ 1658c2ecf20Sopenharmony_ci rc = (adapter->hw.mac.ops.init_thermal_sensor_thresh(&adapter->hw)); 1668c2ecf20Sopenharmony_ci if (rc) 1678c2ecf20Sopenharmony_ci goto exit; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci igb_hwmon = devm_kzalloc(&adapter->pdev->dev, sizeof(*igb_hwmon), 1708c2ecf20Sopenharmony_ci GFP_KERNEL); 1718c2ecf20Sopenharmony_ci if (!igb_hwmon) { 1728c2ecf20Sopenharmony_ci rc = -ENOMEM; 1738c2ecf20Sopenharmony_ci goto exit; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci adapter->igb_hwmon_buff = igb_hwmon; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (i = 0; i < E1000_MAX_SENSORS; i++) { 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Only create hwmon sysfs entries for sensors that have 1808c2ecf20Sopenharmony_ci * meaningful data. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci if (adapter->hw.mac.thermal_sensor_data.sensor[i].location == 0) 1838c2ecf20Sopenharmony_ci continue; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Bail if any hwmon attr struct fails to initialize */ 1868c2ecf20Sopenharmony_ci rc = igb_add_hwmon_attr(adapter, i, IGB_HWMON_TYPE_CAUTION); 1878c2ecf20Sopenharmony_ci if (rc) 1888c2ecf20Sopenharmony_ci goto exit; 1898c2ecf20Sopenharmony_ci rc = igb_add_hwmon_attr(adapter, i, IGB_HWMON_TYPE_LOC); 1908c2ecf20Sopenharmony_ci if (rc) 1918c2ecf20Sopenharmony_ci goto exit; 1928c2ecf20Sopenharmony_ci rc = igb_add_hwmon_attr(adapter, i, IGB_HWMON_TYPE_TEMP); 1938c2ecf20Sopenharmony_ci if (rc) 1948c2ecf20Sopenharmony_ci goto exit; 1958c2ecf20Sopenharmony_ci rc = igb_add_hwmon_attr(adapter, i, IGB_HWMON_TYPE_MAX); 1968c2ecf20Sopenharmony_ci if (rc) 1978c2ecf20Sopenharmony_ci goto exit; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* init i2c_client */ 2018c2ecf20Sopenharmony_ci client = i2c_new_client_device(&adapter->i2c_adap, &i350_sensor_info); 2028c2ecf20Sopenharmony_ci if (IS_ERR(client)) { 2038c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 2048c2ecf20Sopenharmony_ci "Failed to create new i2c device.\n"); 2058c2ecf20Sopenharmony_ci rc = PTR_ERR(client); 2068c2ecf20Sopenharmony_ci goto exit; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci adapter->i2c_client = client; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci igb_hwmon->groups[0] = &igb_hwmon->group; 2118c2ecf20Sopenharmony_ci igb_hwmon->group.attrs = igb_hwmon->attrs; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(&adapter->pdev->dev, 2148c2ecf20Sopenharmony_ci client->name, 2158c2ecf20Sopenharmony_ci igb_hwmon, 2168c2ecf20Sopenharmony_ci igb_hwmon->groups); 2178c2ecf20Sopenharmony_ci if (IS_ERR(hwmon_dev)) { 2188c2ecf20Sopenharmony_ci rc = PTR_ERR(hwmon_dev); 2198c2ecf20Sopenharmony_ci goto err; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci goto exit; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cierr: 2258c2ecf20Sopenharmony_ci igb_sysfs_del_adapter(adapter); 2268c2ecf20Sopenharmony_ciexit: 2278c2ecf20Sopenharmony_ci return rc; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci#endif 230