On Thu, Oct 12, 2023 at 09:13:59AM +0200, Konrad Knitter wrote: > Since 4.30 firmware exposes internal thermal sensor reading via admin > queue commands. Expose those readouts via hwmon API when supported. > > Driver provides current reading from HW as well as device specific > thresholds for thermal alarm (Warning, Critical, Fatal) events. > > $ sensors > > Output > ========================================================= > ice-pci-b100 > Adapter: PCI adapter > temp1: +62.0°C (high = +95.0°C, crit = +105.0°C) > (emerg = +115.0°C) > > Co-developed-by: Marcin Domagala <marcinx.domag...@intel.com> > Signed-off-by: Marcin Domagala <marcinx.domag...@intel.com> > Co-developed-by: Eric Joyner <eric.joy...@intel.com> > Signed-off-by: Eric Joyner <eric.joy...@intel.com> > Reviewed-by: Marcin Szycik <marcin.szy...@linux.intel.com> > Reviewed-by: Przemek Kitszel <przemyslaw.kits...@intel.com> > Signed-off-by: Konrad Knitter <konrad.knit...@intel.com> > --- > v3: add SPDX identification to ice_hwmon files > v2: fix formmating issues, added hwmon maintainers to Cc > --- > drivers/net/ethernet/intel/ice/Makefile | 1 +
The code seems to be unconditional, but I see no added dependency on CONFIG_HWMON. Does this compile if HWMON=m and this code is built into the kernel, or if HWMON=n ? > drivers/net/ethernet/intel/ice/ice.h | 1 + > .../net/ethernet/intel/ice/ice_adminq_cmd.h | 28 ++++ > drivers/net/ethernet/intel/ice/ice_common.c | 57 +++++++- > drivers/net/ethernet/intel/ice/ice_common.h | 2 + > drivers/net/ethernet/intel/ice/ice_hwmon.c | 130 ++++++++++++++++++ > drivers/net/ethernet/intel/ice/ice_hwmon.h | 10 ++ > drivers/net/ethernet/intel/ice/ice_main.c | 5 + > drivers/net/ethernet/intel/ice/ice_type.h | 4 + > 9 files changed, 237 insertions(+), 1 deletion(-) > create mode 100644 drivers/net/ethernet/intel/ice/ice_hwmon.c > create mode 100644 drivers/net/ethernet/intel/ice/ice_hwmon.h > > diff --git a/drivers/net/ethernet/intel/ice/Makefile > b/drivers/net/ethernet/intel/ice/Makefile > index 8757bec23fb3..b4c8f5303e57 100644 > --- a/drivers/net/ethernet/intel/ice/Makefile > +++ b/drivers/net/ethernet/intel/ice/Makefile > @@ -36,6 +36,7 @@ ice-y := ice_main.o \ > ice_repr.o \ > ice_tc_lib.o \ > ice_fwlog.o \ > + ice_hwmon.o \ > ice_debugfs.o > ice-$(CONFIG_PCI_IOV) += \ > ice_sriov.o \ > diff --git a/drivers/net/ethernet/intel/ice/ice.h > b/drivers/net/ethernet/intel/ice/ice.h > index ad5614d4449c..61d26be502b2 100644 > --- a/drivers/net/ethernet/intel/ice/ice.h > +++ b/drivers/net/ethernet/intel/ice/ice.h > @@ -650,6 +650,7 @@ struct ice_pf { > #define ICE_MAX_VF_AGG_NODES 32 > struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES]; > struct ice_dplls dplls; > + struct device *hwmon_dev; > }; > > extern struct workqueue_struct *ice_lag_wq; > diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h > b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h > index 1202abfb9eb3..3c4295f8e4ba 100644 > --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h > +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h > @@ -117,6 +117,7 @@ struct ice_aqc_list_caps_elem { > #define ICE_AQC_CAPS_NET_VER 0x004C > #define ICE_AQC_CAPS_PENDING_NET_VER 0x004D > #define ICE_AQC_CAPS_RDMA 0x0051 > +#define ICE_AQC_CAPS_SENSOR_READING 0x0067 > #define ICE_AQC_CAPS_PCIE_RESET_AVOIDANCE 0x0076 > #define ICE_AQC_CAPS_POST_UPDATE_RESET_RESTRICT 0x0077 > #define ICE_AQC_CAPS_NVM_MGMT 0x0080 > @@ -1393,6 +1394,30 @@ struct ice_aqc_get_phy_rec_clk_out { > __le16 node_handle; > }; > > +/* Get sensor reading (direct 0x0632) */ > +struct ice_aqc_get_sensor_reading { > + u8 sensor; > + u8 format; > + u8 reserved[6]; > + __le32 addr_high; > + __le32 addr_low; > +}; > + > +/* Get sensor reading response (direct 0x0632) */ > +struct ice_aqc_get_sensor_reading_resp { > + union { > + u8 raw[8]; > + /* Output data for sensor 0x00, format 0x00 */ > + struct { > + s8 temp; > + u8 temp_warning_threshold; > + u8 temp_critical_threshold; > + u8 temp_fatal_threshold; > + u8 reserved[4]; > + } s0f0; > + } data; > +}; Kind of surprising that this doesn't need packed attributes. > + > struct ice_aqc_link_topo_params { > u8 lport_num; > u8 lport_num_valid; > @@ -2438,6 +2463,8 @@ struct ice_aq_desc { > struct ice_aqc_restart_an restart_an; > struct ice_aqc_set_phy_rec_clk_out set_phy_rec_clk_out; > struct ice_aqc_get_phy_rec_clk_out get_phy_rec_clk_out; > + struct ice_aqc_get_sensor_reading get_sensor_reading; > + struct ice_aqc_get_sensor_reading_resp get_sensor_reading_resp; > struct ice_aqc_gpio read_write_gpio; > struct ice_aqc_sff_eeprom read_write_sff_param; > struct ice_aqc_set_port_id_led set_port_id_led; > @@ -2617,6 +2644,7 @@ enum ice_adminq_opc { > ice_aqc_opc_set_mac_lb = 0x0620, > ice_aqc_opc_set_phy_rec_clk_out = 0x0630, > ice_aqc_opc_get_phy_rec_clk_out = 0x0631, > + ice_aqc_opc_get_sensor_reading = 0x0632, > ice_aqc_opc_get_link_topo = 0x06E0, > ice_aqc_opc_read_i2c = 0x06E2, > ice_aqc_opc_write_i2c = 0x06E3, > diff --git a/drivers/net/ethernet/intel/ice/ice_common.c > b/drivers/net/ethernet/intel/ice/ice_common.c > index 283492314215..e566485a01b2 100644 > --- a/drivers/net/ethernet/intel/ice/ice_common.c > +++ b/drivers/net/ethernet/intel/ice/ice_common.c > @@ -2462,6 +2462,26 @@ ice_parse_fdir_dev_caps(struct ice_hw *hw, struct > ice_hw_dev_caps *dev_p, > dev_p->num_flow_director_fltr); > } > > +/** > + * ice_parse_sensor_reading_cap - Parse ICE_AQC_CAPS_SENSOR_READING cap > + * @hw: pointer to the HW struct > + * @dev_p: pointer to device capabilities structure > + * @cap: capability element to parse > + * > + * Parse ICE_AQC_CAPS_SENSOR_READING for device capability for reading > + * enabled sensors. > + */ > +static void > +ice_parse_sensor_reading_cap(struct ice_hw *hw, struct ice_hw_dev_caps > *dev_p, > + struct ice_aqc_list_caps_elem *cap) > +{ > + dev_p->supported_sensors = le32_to_cpu(cap->number); > + > + ice_debug(hw, ICE_DBG_INIT, > + "dev caps: supported sensors (bitmap) = 0x%x\n", > + dev_p->supported_sensors); > +} > + > /** > * ice_parse_dev_caps - Parse device capabilities > * @hw: pointer to the HW struct > @@ -2507,9 +2527,12 @@ ice_parse_dev_caps(struct ice_hw *hw, struct > ice_hw_dev_caps *dev_p, > case ICE_AQC_CAPS_1588: > ice_parse_1588_dev_caps(hw, dev_p, &cap_resp[i]); > break; > - case ICE_AQC_CAPS_FD: > + case ICE_AQC_CAPS_FD: > ice_parse_fdir_dev_caps(hw, dev_p, &cap_resp[i]); > break; > + case ICE_AQC_CAPS_SENSOR_READING: > + ice_parse_sensor_reading_cap(hw, dev_p, &cap_resp[i]); > + break; > default: > /* Don't list common capabilities as unknown */ > if (!found) > @@ -5292,6 +5315,38 @@ ice_aq_get_phy_rec_clk_out(struct ice_hw *hw, u8 > *phy_output, u8 *port_num, > return status; > } > > +/** > + * ice_aq_get_sensor_reading > + * @hw: pointer to the HW struct > + * @sensor: sensor type > + * @format: requested response format > + * @data: pointer to data to be read from the sensor > + * > + * Get sensor reading (0x0632) > + */ > +int ice_aq_get_sensor_reading(struct ice_hw *hw, u8 sensor, u8 format, > + struct ice_aqc_get_sensor_reading_resp *data) Are "sensor" and "format" ever going to be != 0 ? If not, those parameters are just noise. > +{ > + struct ice_aqc_get_sensor_reading *cmd; > + struct ice_aq_desc desc; > + int status; > + > + if (!data) > + return -EINVAL; This is never called with a NULL pointer. The check is pointless. > + > + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_sensor_reading); > + cmd = &desc.params.get_sensor_reading; > + cmd->sensor = sensor; > + cmd->format = format; > + > + status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL); > + if (!status) > + memcpy(data, &desc.params.get_sensor_reading_resp, > + sizeof(*data)); > + > + return status; > +} > + > /** > * ice_replay_pre_init - replay pre initialization > * @hw: pointer to the HW struct > diff --git a/drivers/net/ethernet/intel/ice/ice_common.h > b/drivers/net/ethernet/intel/ice/ice_common.h > index 4a75c0c89301..e23787c17505 100644 > --- a/drivers/net/ethernet/intel/ice/ice_common.h > +++ b/drivers/net/ethernet/intel/ice/ice_common.h > @@ -240,6 +240,8 @@ ice_aq_set_phy_rec_clk_out(struct ice_hw *hw, u8 > phy_output, bool enable, > int > ice_aq_get_phy_rec_clk_out(struct ice_hw *hw, u8 *phy_output, u8 *port_num, > u8 *flags, u16 *node_handle); > +int ice_aq_get_sensor_reading(struct ice_hw *hw, u8 sensor, u8 format, > + struct ice_aqc_get_sensor_reading_resp *data); > void > ice_stat_update40(struct ice_hw *hw, u32 reg, bool prev_stat_loaded, > u64 *prev_stat, u64 *cur_stat); > diff --git a/drivers/net/ethernet/intel/ice/ice_hwmon.c > b/drivers/net/ethernet/intel/ice/ice_hwmon.c > new file mode 100644 > index 000000000000..6b23ae27169c > --- /dev/null > +++ b/drivers/net/ethernet/intel/ice/ice_hwmon.c > @@ -0,0 +1,130 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* Copyright (C) 2022, Intel Corporation. */ > + > +#include "ice.h" > +#include "ice_hwmon.h" > +#include "ice_adminq_cmd.h" > + > +#include <linux/hwmon.h> > + > +#define ICE_INTERNAL_TEMP_SENSOR 0 > +#define ICE_INTERNAL_TEMP_SENSOR_FORMAT 0 > + Personally I very much prefer #define<space>NAME<tab>value but obviously that is a maintainer decision to make. > +#define TEMP_FROM_REG(reg) ((reg) * 1000) > + > +static const struct hwmon_channel_info *ice_hwmon_info[] = { > + HWMON_CHANNEL_INFO(temp, > + HWMON_T_INPUT | HWMON_T_MAX | > + HWMON_T_CRIT | HWMON_T_EMERGENCY), > + NULL > +}; > + > +static int ice_hwmon_read(struct device *dev, enum hwmon_sensor_types type, > + u32 attr, int channel, long *val) > +{ > + struct ice_aqc_get_sensor_reading_resp resp; > + struct ice_pf *pf = dev_get_drvdata(dev); > + int ret; > + > + if (type != hwmon_temp) > + return -EOPNOTSUPP; > + > + ret = ice_aq_get_sensor_reading(&pf->hw, > + ICE_INTERNAL_TEMP_SENSOR, > + ICE_INTERNAL_TEMP_SENSOR_FORMAT, > + &resp); > + if (ret) { > + dev_warn(dev, "%s HW read failure (%d)\n", __func__, ret); Up to maintainers to decide, but I do not support error messages as result of normal operation because it may end up clogging the log if the underlying HW has a problem. > + return ret; > + } > + > + switch (attr) { > + case hwmon_temp_input: > + *val = TEMP_FROM_REG(resp.data.s0f0.temp); > + break; > + case hwmon_temp_max: > + *val = TEMP_FROM_REG(resp.data.s0f0.temp_warning_threshold); > + break; > + case hwmon_temp_crit: > + *val = TEMP_FROM_REG(resp.data.s0f0.temp_critical_threshold); > + break; > + case hwmon_temp_emergency: > + *val = TEMP_FROM_REG(resp.data.s0f0.temp_fatal_threshold); > + break; > + default: > + dev_warn(dev, "%s unsupported attribute (%d)\n", > + __func__, attr); Same here, especially since this won't ever happen and the code just exists to make the compiler happy. > + return -EINVAL; Should be -EOPNOTSUPP. > + } > + > + return 0; > +} > + > +static umode_t ice_hwmon_is_visible(const void *data, > + enum hwmon_sensor_types type, u32 attr, > + int channel) > +{ > + if (type != hwmon_temp) > + return 0; > + > + switch (attr) { > + case hwmon_temp_input: > + case hwmon_temp_crit: > + case hwmon_temp_max: > + case hwmon_temp_emergency: > + return 0444; > + } > + > + return 0; > +} > + > +static const struct hwmon_ops ice_hwmon_ops = { > + .is_visible = ice_hwmon_is_visible, > + .read = ice_hwmon_read > +}; > + > +static const struct hwmon_chip_info ice_chip_info = { > + .ops = &ice_hwmon_ops, > + .info = ice_hwmon_info > +}; > + > +static bool ice_is_internal_reading_supported(struct ice_pf *pf) > +{ > + if (pf->hw.pf_id) > + return false; > + This should be explained. From the rest of the code, it appears that pf_id==0 reflects the first "pf". Why should this device not register a hwmon device, and / or why is pf->hw.dev_caps.supported_sensors unsupported or not set correctly for it ? > + unsigned long sensors = pf->hw.dev_caps.supported_sensors; > + > + if (!_test_bit(ICE_SENSOR_SUPPORT_E810_INT_TEMP_BIT, &sensors)) > + return false; > + > + return true; return _test_bit(ICE_SENSOR_SUPPORT_E810_INT_TEMP_BIT, &sensors); would do the same. > +}; > + > +void ice_hwmon_init(struct ice_pf *pf) > +{ > + struct device *dev = ice_pf_to_dev(pf); > + struct device *hdev; > + > + if (!ice_is_internal_reading_supported(pf)) > + return; > + > + hdev = hwmon_device_register_with_info(dev, "ice", pf, &ice_chip_info, > + NULL); > + if (IS_ERR(hdev)) { > + dev_warn(dev, > + "hwmon_device_register_with_info returns error (%ld)", > + PTR_ERR(hdev)); > + return; > + } > + pf->hwmon_dev = hdev; > +} > + > +void ice_hwmon_exit(struct ice_pf *pf) > +{ > + if (!ice_is_internal_reading_supported(pf)) > + return; In this case hwmon_dev would be NULL, making the above check unnecessary. > + if (!pf->hwmon_dev) > + return; > + hwmon_device_unregister(pf->hwmon_dev); > +} > diff --git a/drivers/net/ethernet/intel/ice/ice_hwmon.h > b/drivers/net/ethernet/intel/ice/ice_hwmon.h > new file mode 100644 > index 000000000000..8c74d19933d7 > --- /dev/null > +++ b/drivers/net/ethernet/intel/ice/ice_hwmon.h > @@ -0,0 +1,10 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* Copyright (C) 2022, Intel Corporation. */ > + > +#ifndef _ICE_HWMON_H_ > +#define _ICE_HWMON_H_ > + > +void ice_hwmon_init(struct ice_pf *pf); > +void ice_hwmon_exit(struct ice_pf *pf); > + > +#endif /* _ICE_HWMON_H_ */ > diff --git a/drivers/net/ethernet/intel/ice/ice_main.c > b/drivers/net/ethernet/intel/ice/ice_main.c > index afe19219a640..8f7e901a6c95 100644 > --- a/drivers/net/ethernet/intel/ice/ice_main.c > +++ b/drivers/net/ethernet/intel/ice/ice_main.c > @@ -14,6 +14,7 @@ > #include "ice_dcb_lib.h" > #include "ice_dcb_nl.h" > #include "ice_devlink.h" > +#include "ice_hwmon.h" > /* Including ice_trace.h with CREATE_TRACE_POINTS defined will generate the > * ice tracepoint functions. This must be done exactly once across the > * ice driver. > @@ -4785,6 +4786,8 @@ static void ice_init_features(struct ice_pf *pf) > > if (ice_init_lag(pf)) > dev_warn(dev, "Failed to init link aggregation support\n"); > + > + ice_hwmon_init(pf); > } > > static void ice_deinit_features(struct ice_pf *pf) > @@ -5310,6 +5313,8 @@ static void ice_remove(struct pci_dev *pdev) > ice_free_vfs(pf); > } > > + ice_hwmon_exit(pf); > + > ice_service_task_stop(pf); > ice_aq_cancel_waiting_tasks(pf); > set_bit(ICE_DOWN, pf->state); > diff --git a/drivers/net/ethernet/intel/ice/ice_type.h > b/drivers/net/ethernet/intel/ice/ice_type.h > index 877a92099ef0..0b5425d33adf 100644 > --- a/drivers/net/ethernet/intel/ice/ice_type.h > +++ b/drivers/net/ethernet/intel/ice/ice_type.h > @@ -378,6 +378,8 @@ struct ice_hw_func_caps { > struct ice_ts_func_info ts_func_info; > }; > > +#define ICE_SENSOR_SUPPORT_E810_INT_TEMP_BIT 0 > + > /* Device wide capabilities */ > struct ice_hw_dev_caps { > struct ice_hw_common_caps common_cap; > @@ -386,6 +388,8 @@ struct ice_hw_dev_caps { > u32 num_flow_director_fltr; /* Number of FD filters available */ > struct ice_ts_dev_info ts_dev_info; > u32 num_funcs; > + /* bitmap of supported sensors */ > + u32 supported_sensors; > }; > > /* MAC info */ > > base-commit: 2318d58f358e7aef726c038aff87a68bec8f09e0 > -- > 2.35.3 > _______________________________________________ Intel-wired-lan mailing list Intel-wired-lan@osuosl.org https://lists.osuosl.org/mailman/listinfo/intel-wired-lan