To compile this driver as a module, choose M here. The module
          will be called ice.
 
+config ICE_HWMON
+       bool "Intel(R) Ethernet Connection E800 Series Support HWMON support"
+       default y
+       depends on ICE && HWMON && !(ICE=y && HWMON=m)
+       help
+         Say Y if you want to expose thermal sensor data on Intel devices.
+
+         Some of our devices contain internal thermal sensors.
+         This data is available via the hwmon sysfs interface and exposes
+         the onboard sensors.
+
 config ICE_SWITCHDEV
        bool "Switchdev Support"
        default y
 
 ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o
 ice-$(CONFIG_ICE_SWITCHDEV) += ice_eswitch.o ice_eswitch_br.o
 ice-$(CONFIG_GNSS) += ice_gnss.o
+ice-$(CONFIG_ICE_HWMON) += ice_hwmon.o
 
 #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;
 
 #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
        __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 _packed {
+                       s8 temp;
+                       u8 temp_warning_threshold;
+                       u8 temp_critical_threshold;
+                       u8 temp_fatal_threshold;
+                       u8 reserved[4];
+               } s0f0;
+       } data;
+};
+
 struct ice_aqc_link_topo_params {
        u8 lport_num;
        u8 lport_num_valid;
                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;
        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,
 
                  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
                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)
        return status;
 }
 
+/**
+ * ice_aq_get_sensor_reading
+ * @hw: pointer to the HW struct
+ * @data: pointer to data to be read from the sensor
+ *
+ * Get sensor reading (0x0632)
+ */
+int ice_aq_get_sensor_reading(struct ice_hw *hw,
+                             struct ice_aqc_get_sensor_reading_resp *data)
+{
+       struct ice_aqc_get_sensor_reading *cmd;
+       struct ice_aq_desc desc;
+       int status;
+
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_sensor_reading);
+       cmd = &desc.params.get_sensor_reading;
+#define ICE_INTERNAL_TEMP_SENSOR_FORMAT        0
+#define ICE_INTERNAL_TEMP_SENSOR       0
+       cmd->sensor = ICE_INTERNAL_TEMP_SENSOR;
+       cmd->format = ICE_INTERNAL_TEMP_SENSOR_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
 
 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,
+                             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);
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023, Intel Corporation. */
+
+#include "ice.h"
+#include "ice_hwmon.h"
+#include "ice_adminq_cmd.h"
+
+#include <linux/hwmon.h>
+
+#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, &resp);
+       if (ret) {
+               dev_warn_ratelimited(dev,
+                                    "%s HW read failure (%d)\n",
+                                    __func__,
+                                    ret);
+               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_dbg(dev, "%s unsupported attribute (%d)\n",
+                       __func__, attr);
+               return -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)
+{
+       /* Only the first PF will report temperature for a chip.
+        * Note that internal temp reading is not supported
+        * for older FW (< v4.30).
+        */
+       if (pf->hw.pf_id)
+               return false;
+
+       unsigned long sensors = pf->hw.dev_caps.supported_sensors;
+
+       return _test_bit(ICE_SENSOR_SUPPORT_E810_INT_TEMP_BIT, &sensors);
+};
+
+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 (!pf->hwmon_dev)
+               return;
+       hwmon_device_unregister(pf->hwmon_dev);
+}
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023, Intel Corporation. */
+
+#ifndef _ICE_HWMON_H_
+#define _ICE_HWMON_H_
+
+#ifdef CONFIG_ICE_HWMON
+void ice_hwmon_init(struct ice_pf *pf);
+void ice_hwmon_exit(struct ice_pf *pf);
+#else /* CONFIG_ICE_HWMON */
+static inline void ice_hwmon_init(struct ice_pf *pf) { }
+static inline void ice_hwmon_exit(struct ice_pf *pf) { }
+#endif /* CONFIG_ICE_HWMON */
+
+#endif /* _ICE_HWMON_H_ */
 
 #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.
 
        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)
                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);
 
        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;
        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
+        * bit 0 - internal temperature sensor
+        * bit 31:1 - Reserved
+        */
+       u32 supported_sensors;
 };
 
 /* MAC info */