#include <linux/hwmon-sysfs.h>
 #include <linux/hwmon.h>
+#include <linux/jiffies.h>
 #include <linux/types.h>
 #include <linux/units.h>
 
        REG_PKG_POWER_SKU_UNIT,
        REG_GT_PERF_STATUS,
        REG_PKG_ENERGY_STATUS,
+       REG_FAN_SPEED,
 };
 
 enum xe_hwmon_reg_operation {
        CHANNEL_MAX,
 };
 
+enum xe_fan_channel {
+       FAN_1,
+       FAN_2,
+       FAN_3,
+       FAN_MAX,
+};
+
 /*
  * SF_* - scale factors for particular quantities according to hwmon spec.
  */
        long accum_energy;
 };
 
+/**
+ * struct xe_hwmon_fan_info - to cache previous fan reading
+ */
+struct xe_hwmon_fan_info {
+       /** @reg_val_prev: previous fan reg val */
+       u32 reg_val_prev;
+       /** @time_prev: previous timestamp */
+       u64 time_prev;
+};
+
 /**
  * struct xe_hwmon - xe hwmon data structure
  */
        int scl_shift_time;
        /** @ei: Energy info for energyN_input */
        struct xe_hwmon_energy_info ei[CHANNEL_MAX];
+       /** @fi: Fan info for fanN_input */
+       struct xe_hwmon_fan_info fi[FAN_MAX];
 };
 
 static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg,
                        return PCU_CR_PACKAGE_ENERGY_STATUS;
                }
                break;
+       case REG_FAN_SPEED:
+               if (channel == FAN_1)
+                       return BMG_FAN_1_SPEED;
+               else if (channel == FAN_2)
+                       return BMG_FAN_2_SPEED;
+               else if (channel == FAN_3)
+                       return BMG_FAN_3_SPEED;
+               break;
        default:
                drm_warn(&xe->drm, "Unknown xe hwmon reg id: %d\n", hwmon_reg);
                break;
        HWMON_CHANNEL_INFO(curr, HWMON_C_LABEL, HWMON_C_CRIT | HWMON_C_LABEL),
        HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
        HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
+       HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT),
        NULL
 };
 
                              (uval & POWER_SETUP_I1_DATA_MASK));
 }
 
+static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u32 *uval)
+{
+       struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+
+       /* Platforms that don't return correct value */
+       if (hwmon->xe->info.platform == XE_DG2 && subcmd == FSC_READ_NUM_FANS) {
+               *uval = 2;
+               return 0;
+       }
+
+       return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, 0), uval, NULL);
+}
+
 static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
                                         long *value, u32 scale_factor)
 {
        }
 }
 
+static umode_t
+xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
+{
+       u32 uval;
+
+       if (!hwmon->xe->info.has_fan_control)
+               return 0;
+
+       switch (attr) {
+       case hwmon_fan_input:
+               if (xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, &uval))
+                       return 0;
+
+               return channel < uval ? 0444 : 0;
+       default:
+               return 0;
+       }
+}
+
+static int
+xe_hwmon_fan_input_read(struct xe_hwmon *hwmon, int channel, long *val)
+{
+       struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
+       struct xe_hwmon_fan_info *fi = &hwmon->fi[channel];
+       u64 rotations, time_now, time;
+       u32 reg_val;
+       int ret = 0;
+
+       mutex_lock(&hwmon->hwmon_lock);
+
+       reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_FAN_SPEED, channel));
+       time_now = get_jiffies_64();
+
+       /*
+        * HW register value is accumulated count of pulses from PWM fan with the scale
+        * of 2 pulses per rotation.
+        */
+       rotations = (reg_val - fi->reg_val_prev) / 2;
+
+       time = jiffies_delta_to_msecs(time_now - fi->time_prev);
+       if (unlikely(!time)) {
+               ret = -EAGAIN;
+               goto unlock;
+       }
+
+       /*
+        * Calculate fan speed in RPM by time averaging two subsequent readings in minutes.
+        * RPM = number of rotations * msecs per minute / time in msecs
+        */
+       *val = DIV_ROUND_UP_ULL(rotations * (MSEC_PER_SEC * 60), time);
+
+       fi->reg_val_prev = reg_val;
+       fi->time_prev = time_now;
+unlock:
+       mutex_unlock(&hwmon->hwmon_lock);
+       return ret;
+}
+
+static int
+xe_hwmon_fan_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
+{
+       switch (attr) {
+       case hwmon_fan_input:
+               return xe_hwmon_fan_input_read(hwmon, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static umode_t
 xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
                    u32 attr, int channel)
        case hwmon_energy:
                ret = xe_hwmon_energy_is_visible(hwmon, attr, channel);
                break;
+       case hwmon_fan:
+               ret = xe_hwmon_fan_is_visible(hwmon, attr, channel);
+               break;
        default:
                ret = 0;
                break;
        case hwmon_energy:
                ret = xe_hwmon_energy_read(hwmon, attr, channel, val);
                break;
+       case hwmon_fan:
+               ret = xe_hwmon_fan_read(hwmon, attr, channel, val);
+               break;
        default:
                ret = -EOPNOTSUPP;
                break;
 xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
 {
        struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
-       long energy;
+       long energy, fan_speed;
        u64 val_sku_unit = 0;
        int channel;
        struct xe_reg pkg_power_sku_unit;
        for (channel = 0; channel < CHANNEL_MAX; channel++)
                if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, channel))
                        xe_hwmon_energy_get(hwmon, channel, &energy);
+
+       /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
+       for (channel = 0; channel < FAN_MAX; channel++)
+               if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
+                       xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
 }
 
 static void xe_hwmon_mutex_destroy(void *arg)