Skip to content

Commit

Permalink
igb: Enable hwmon data output for thermal sensors via I2C.
Browse files Browse the repository at this point in the history
Some of our adapters have internal sensors that report thermal data.  This
patch enables reporting of that data via sysfs.

Signed-off-by: Carolyn Wyborny <carolyn.wyborny@intel.com>
Tested-by: Jeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
  • Loading branch information
Carolyn Wyborny authored and Jeff Kirsher committed Jan 18, 2013
1 parent aca5dae commit e428893
Show file tree
Hide file tree
Showing 8 changed files with 469 additions and 7 deletions.
11 changes: 11 additions & 0 deletions drivers/net/ethernet/intel/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@ config IGB
To compile this driver as a module, choose M here. The module
will be called igb.

config IGB_HWMON
bool "Intel(R) PCI-Express Gigabit adapters HWMON support"
default y
depends on IGB && HWMON && !(IGB=y && HWMON=m)
---help---
Say Y if you want to expose thermal sensor data on Intel devices.

Some of our devices contain thermal sensors, both external and internal.
This data is available via the hwmon sysfs interface and exposes
the onboard sensors.

config IGB_DCA
bool "Direct Cache Access (DCA) Support"
default y
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ethernet/intel/igb/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ obj-$(CONFIG_IGB) += igb.o

igb-objs := igb_main.o igb_ethtool.o e1000_82575.o \
e1000_mac.o e1000_nvm.o e1000_phy.o e1000_mbx.o \
e1000_i210.o igb_ptp.o
e1000_i210.o igb_ptp.o igb_hwmon.o
137 changes: 137 additions & 0 deletions drivers/net/ethernet/intel/igb/e1000_82575.c
Original file line number Diff line number Diff line change
Expand Up @@ -2303,12 +2303,149 @@ s32 igb_set_eee_i350(struct e1000_hw *hw)
return ret_val;
}

static const u8 e1000_emc_temp_data[4] = {
E1000_EMC_INTERNAL_DATA,
E1000_EMC_DIODE1_DATA,
E1000_EMC_DIODE2_DATA,
E1000_EMC_DIODE3_DATA
};
static const u8 e1000_emc_therm_limit[4] = {
E1000_EMC_INTERNAL_THERM_LIMIT,
E1000_EMC_DIODE1_THERM_LIMIT,
E1000_EMC_DIODE2_THERM_LIMIT,
E1000_EMC_DIODE3_THERM_LIMIT
};

/* igb_get_thermal_sensor_data_generic - Gathers thermal sensor data
* @hw: pointer to hardware structure
*
* Updates the temperatures in mac.thermal_sensor_data
*/
s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
{
s32 status = E1000_SUCCESS;
u16 ets_offset;
u16 ets_cfg;
u16 ets_sensor;
u8 num_sensors;
u8 sensor_index;
u8 sensor_location;
u8 i;
struct e1000_thermal_sensor_data *data = &hw->mac.thermal_sensor_data;

if ((hw->mac.type != e1000_i350) || (hw->bus.func != 0))
return E1000_NOT_IMPLEMENTED;

data->sensor[0].temp = (rd32(E1000_THMJT) & 0xFF);

/* Return the internal sensor only if ETS is unsupported */
hw->nvm.ops.read(hw, NVM_ETS_CFG, 1, &ets_offset);
if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF))
return status;

hw->nvm.ops.read(hw, ets_offset, 1, &ets_cfg);
if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT)
!= NVM_ETS_TYPE_EMC)
return E1000_NOT_IMPLEMENTED;

num_sensors = (ets_cfg & NVM_ETS_NUM_SENSORS_MASK);
if (num_sensors > E1000_MAX_SENSORS)
num_sensors = E1000_MAX_SENSORS;

for (i = 1; i < num_sensors; i++) {
hw->nvm.ops.read(hw, (ets_offset + i), 1, &ets_sensor);
sensor_index = ((ets_sensor & NVM_ETS_DATA_INDEX_MASK) >>
NVM_ETS_DATA_INDEX_SHIFT);
sensor_location = ((ets_sensor & NVM_ETS_DATA_LOC_MASK) >>
NVM_ETS_DATA_LOC_SHIFT);

if (sensor_location != 0)
hw->phy.ops.read_i2c_byte(hw,
e1000_emc_temp_data[sensor_index],
E1000_I2C_THERMAL_SENSOR_ADDR,
&data->sensor[i].temp);
}
return status;
}

/* igb_init_thermal_sensor_thresh_generic - Sets thermal sensor thresholds
* @hw: pointer to hardware structure
*
* Sets the thermal sensor thresholds according to the NVM map
* and save off the threshold and location values into mac.thermal_sensor_data
*/
s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
{
s32 status = E1000_SUCCESS;
u16 ets_offset;
u16 ets_cfg;
u16 ets_sensor;
u8 low_thresh_delta;
u8 num_sensors;
u8 sensor_index;
u8 sensor_location;
u8 therm_limit;
u8 i;
struct e1000_thermal_sensor_data *data = &hw->mac.thermal_sensor_data;

if ((hw->mac.type != e1000_i350) || (hw->bus.func != 0))
return E1000_NOT_IMPLEMENTED;

memset(data, 0, sizeof(struct e1000_thermal_sensor_data));

data->sensor[0].location = 0x1;
data->sensor[0].caution_thresh =
(rd32(E1000_THHIGHTC) & 0xFF);
data->sensor[0].max_op_thresh =
(rd32(E1000_THLOWTC) & 0xFF);

/* Return the internal sensor only if ETS is unsupported */
hw->nvm.ops.read(hw, NVM_ETS_CFG, 1, &ets_offset);
if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF))
return status;

hw->nvm.ops.read(hw, ets_offset, 1, &ets_cfg);
if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT)
!= NVM_ETS_TYPE_EMC)
return E1000_NOT_IMPLEMENTED;

low_thresh_delta = ((ets_cfg & NVM_ETS_LTHRES_DELTA_MASK) >>
NVM_ETS_LTHRES_DELTA_SHIFT);
num_sensors = (ets_cfg & NVM_ETS_NUM_SENSORS_MASK);

for (i = 1; i <= num_sensors; i++) {
hw->nvm.ops.read(hw, (ets_offset + i), 1, &ets_sensor);
sensor_index = ((ets_sensor & NVM_ETS_DATA_INDEX_MASK) >>
NVM_ETS_DATA_INDEX_SHIFT);
sensor_location = ((ets_sensor & NVM_ETS_DATA_LOC_MASK) >>
NVM_ETS_DATA_LOC_SHIFT);
therm_limit = ets_sensor & NVM_ETS_DATA_HTHRESH_MASK;

hw->phy.ops.write_i2c_byte(hw,
e1000_emc_therm_limit[sensor_index],
E1000_I2C_THERMAL_SENSOR_ADDR,
therm_limit);

if ((i < E1000_MAX_SENSORS) && (sensor_location != 0)) {
data->sensor[i].location = sensor_location;
data->sensor[i].caution_thresh = therm_limit;
data->sensor[i].max_op_thresh = therm_limit -
low_thresh_delta;
}
}
return status;
}

static struct e1000_mac_operations e1000_mac_ops_82575 = {
.init_hw = igb_init_hw_82575,
.check_for_link = igb_check_for_link_82575,
.rar_set = igb_rar_set,
.read_mac_addr = igb_read_mac_addr_82575,
.get_speed_and_duplex = igb_get_speed_and_duplex_copper,
#ifdef CONFIG_IGB_HWMON
.get_thermal_sensor_data = igb_get_thermal_sensor_data_generic,
.init_thermal_sensor_thresh = igb_init_thermal_sensor_thresh_generic,
#endif
};

static struct e1000_phy_operations e1000_phy_ops_82575 = {
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/intel/igb/e1000_82575.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ void igb_vmdq_set_loopback_pf(struct e1000_hw *, bool);
void igb_vmdq_set_replication_pf(struct e1000_hw *, bool);
u16 igb_rxpbs_adjust_82580(u32 data);
s32 igb_set_eee_i350(struct e1000_hw *);
s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *);
s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw);

#define E1000_I2C_THERMAL_SENSOR_ADDR 0xF8
#define E1000_EMC_INTERNAL_DATA 0x00
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/intel/igb/e1000_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ struct e1000_mac_operations {
s32 (*get_speed_and_duplex)(struct e1000_hw *, u16 *, u16 *);
s32 (*acquire_swfw_sync)(struct e1000_hw *, u16);
void (*release_swfw_sync)(struct e1000_hw *, u16);
#ifdef CONFIG_IGB_HWMON
s32 (*get_thermal_sensor_data)(struct e1000_hw *);
s32 (*init_thermal_sensor_thresh)(struct e1000_hw *);
#endif

};

Expand Down
29 changes: 29 additions & 0 deletions drivers/net/ethernet/intel/igb/igb.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,27 @@ struct igb_i2c_client_list {
struct igb_i2c_client_list *next;
};

#ifdef CONFIG_IGB_HWMON

#define IGB_HWMON_TYPE_LOC 0
#define IGB_HWMON_TYPE_TEMP 1
#define IGB_HWMON_TYPE_CAUTION 2
#define IGB_HWMON_TYPE_MAX 3

struct hwmon_attr {
struct device_attribute dev_attr;
struct e1000_hw *hw;
struct e1000_thermal_diode_data *sensor;
char name[12];
};

struct hwmon_buff {
struct device *device;
struct hwmon_attr *hwmon_list;
unsigned int n_hwmon;
};
#endif

/* board specific private data structure */
struct igb_adapter {
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
Expand Down Expand Up @@ -398,6 +419,10 @@ struct igb_adapter {
struct timecounter tc;

char fw_version[32];
#ifdef CONFIG_IGB_HWMON
struct hwmon_buff igb_hwmon_buff;
bool ets;
#endif
struct i2c_algo_bit_data i2c_algo;
struct i2c_adapter i2c_adap;
struct igb_i2c_client_list *i2c_clients;
Expand Down Expand Up @@ -476,6 +501,10 @@ static inline void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector,

extern int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
struct ifreq *ifr, int cmd);
#ifdef CONFIG_IGB_HWMON
extern void igb_sysfs_exit(struct igb_adapter *adapter);
extern int igb_sysfs_init(struct igb_adapter *adapter);
#endif
static inline s32 igb_reset_phy(struct e1000_hw *hw)
{
if (hw->phy.ops.reset)
Expand Down
Loading

0 comments on commit e428893

Please sign in to comment.