diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c
index ed70415512f6..80cfb343c92d 100644
--- a/drivers/iio/humidity/hdc3020.c
+++ b/drivers/iio/humidity/hdc3020.c
@@ -14,11 +14,13 @@
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 
 #include <asm/unaligned.h>
 
+#include <linux/iio/events.h>
 #include <linux/iio/iio.h>
 
 #define HDC3020_HEATER_CMD_MSB		0x30 /* shared by all heater commands */
@@ -26,21 +28,47 @@
 #define HDC3020_HEATER_DISABLE		0x66
 #define HDC3020_HEATER_CONFIG		0x6E
 
+#define HDC3020_THRESH_TEMP_MASK	GENMASK(8, 0)
+#define HDC3020_THRESH_TEMP_SHIFT	7
+#define HDC3020_THRESH_HUM_MASK		GENMASK(15, 9)
+
+#define HDC3020_STATUS_T_LOW_ALERT	BIT(6)
+#define HDC3020_STATUS_T_HIGH_ALERT	BIT(7)
+#define HDC3020_STATUS_RH_LOW_ALERT	BIT(8)
+#define HDC3020_STATUS_RH_HIGH_ALERT	BIT(9)
+
 #define HDC3020_READ_RETRY_TIMES	10
 #define HDC3020_BUSY_DELAY_MS		10
 
 #define HDC3020_CRC8_POLYNOMIAL		0x31
 
+#define HDC3020_MIN_TEMP		-40
+#define HDC3020_MAX_TEMP		125
+
 static const u8 HDC3020_S_AUTO_10HZ_MOD0[2] = { 0x27, 0x37 };
 
+static const u8 HDC3020_S_STATUS[2] = { 0x30, 0x41 };
+
 static const u8 HDC3020_EXIT_AUTO[2] = { 0x30, 0x93 };
 
+static const u8 HDC3020_S_T_RH_THRESH_LOW[2] = { 0x61, 0x00 };
+static const u8 HDC3020_S_T_RH_THRESH_LOW_CLR[2] = { 0x61, 0x0B };
+static const u8 HDC3020_S_T_RH_THRESH_HIGH_CLR[2] = { 0x61, 0x16 };
+static const u8 HDC3020_S_T_RH_THRESH_HIGH[2] = { 0x61, 0x1D };
+
 static const u8 HDC3020_R_T_RH_AUTO[2] = { 0xE0, 0x00 };
 static const u8 HDC3020_R_T_LOW_AUTO[2] = { 0xE0, 0x02 };
 static const u8 HDC3020_R_T_HIGH_AUTO[2] = { 0xE0, 0x03 };
 static const u8 HDC3020_R_RH_LOW_AUTO[2] = { 0xE0, 0x04 };
 static const u8 HDC3020_R_RH_HIGH_AUTO[2] = { 0xE0, 0x05 };
 
+static const u8 HDC3020_R_T_RH_THRESH_LOW[2] = { 0xE1, 0x02 };
+static const u8 HDC3020_R_T_RH_THRESH_LOW_CLR[2] = { 0xE1, 0x09 };
+static const u8 HDC3020_R_T_RH_THRESH_HIGH_CLR[2] = { 0xE1, 0x14 };
+static const u8 HDC3020_R_T_RH_THRESH_HIGH[2] = { 0xE1, 0x1F };
+
+static const u8 HDC3020_R_STATUS[2] = { 0xF3, 0x2D };
+
 struct hdc3020_data {
 	struct i2c_client *client;
 	/*
@@ -50,22 +78,54 @@ struct hdc3020_data {
 	 * if the device does not respond).
 	 */
 	struct mutex lock;
+	/*
+	 * Temperature and humidity thresholds are packed together into a single
+	 * 16 bit value. Each threshold is represented by a truncated 16 bit
+	 * value. The 7 MSBs of a relative humidity threshold are concatenated
+	 * with the 9 MSBs of a temperature threshold, where the temperature
+	 * threshold resides in the 7 LSBs. Due to the truncated representation,
+	 * there is a resolution loss of 0.5 degree celsius in temperature and a
+	 * 1% resolution loss in relative humidity.
+	 */
+	u16 t_rh_thresh_low;
+	u16 t_rh_thresh_high;
+	u16 t_rh_thresh_low_clr;
+	u16 t_rh_thresh_high_clr;
 };
 
 static const int hdc3020_heater_vals[] = {0, 1, 0x3FFF};
 
+static const struct iio_event_spec hdc3020_t_rh_event[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_RISING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
+		BIT(IIO_EV_INFO_HYSTERESIS),
+	},
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_FALLING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
+		BIT(IIO_EV_INFO_HYSTERESIS),
+	},
+};
+
 static const struct iio_chan_spec hdc3020_channels[] = {
 	{
 		.type = IIO_TEMP,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) |
 		BIT(IIO_CHAN_INFO_TROUGH) | BIT(IIO_CHAN_INFO_OFFSET),
+		.event_spec = hdc3020_t_rh_event,
+		.num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event),
 	},
 	{
 		.type = IIO_HUMIDITYRELATIVE,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) |
 		BIT(IIO_CHAN_INFO_TROUGH),
+		.event_spec = hdc3020_t_rh_event,
+		.num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event),
 	},
 	{
 		/*
@@ -389,10 +449,241 @@ static int hdc3020_write_raw(struct iio_dev *indio_dev,
 	return -EINVAL;
 }
 
+static int hdc3020_write_thresh(struct iio_dev *indio_dev,
+				const struct iio_chan_spec *chan,
+				enum iio_event_type type,
+				enum iio_event_direction dir,
+				enum iio_event_info info,
+				int val, int val2)
+{
+	struct hdc3020_data *data = iio_priv(indio_dev);
+	u16 *thresh;
+	u8 buf[5];
+	int ret;
+
+	/* Supported temperature range is from –40 to 125 degree celsius */
+	if (val < HDC3020_MIN_TEMP || val > HDC3020_MAX_TEMP)
+		return -EINVAL;
+
+	/* Select threshold and associated register */
+	if (info == IIO_EV_INFO_VALUE) {
+		if (dir == IIO_EV_DIR_RISING) {
+			thresh = &data->t_rh_thresh_high;
+			memcpy(buf, HDC3020_S_T_RH_THRESH_HIGH, 2);
+		} else {
+			thresh = &data->t_rh_thresh_low;
+			memcpy(buf, HDC3020_S_T_RH_THRESH_LOW, 2);
+		}
+	} else {
+		if (dir == IIO_EV_DIR_RISING) {
+			thresh = &data->t_rh_thresh_high_clr;
+			memcpy(buf, HDC3020_S_T_RH_THRESH_HIGH_CLR, 2);
+		} else {
+			thresh = &data->t_rh_thresh_low_clr;
+			memcpy(buf, HDC3020_S_T_RH_THRESH_LOW_CLR, 2);
+		}
+	}
+
+	guard(mutex)(&data->lock);
+	switch (chan->type) {
+	case IIO_TEMP:
+		/*
+		 * Store truncated temperature threshold into 9 LSBs while
+		 * keeping the old humidity threshold in the 7 MSBs.
+		 */
+		val = (((val + 45) * 65535 / 175) >> HDC3020_THRESH_TEMP_SHIFT);
+		val &= HDC3020_THRESH_TEMP_MASK;
+		val |= (*thresh & HDC3020_THRESH_HUM_MASK);
+		break;
+	case IIO_HUMIDITYRELATIVE:
+		/*
+		 * Store truncated humidity threshold into 7 MSBs while
+		 * keeping the old temperature threshold in the 9 LSBs.
+		 */
+		val = ((val * 65535 / 100) & HDC3020_THRESH_HUM_MASK);
+		val |= (*thresh & HDC3020_THRESH_TEMP_MASK);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	put_unaligned_be16(val, &buf[2]);
+	buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);
+	ret = hdc3020_write_bytes(data, buf, 5);
+	if (ret)
+		return ret;
+
+	/* Update threshold */
+	*thresh = val;
+
+	return 0;
+}
+
+static int _hdc3020_read_thresh(struct hdc3020_data *data,
+				enum iio_event_info info,
+				enum iio_event_direction dir, u16 *thresh)
+{
+	u8 crc, buf[3];
+	const u8 *cmd;
+	int ret;
+
+	if (info == IIO_EV_INFO_VALUE) {
+		if (dir == IIO_EV_DIR_RISING)
+			cmd = HDC3020_R_T_RH_THRESH_HIGH;
+		else
+			cmd = HDC3020_R_T_RH_THRESH_LOW;
+	} else {
+		if (dir == IIO_EV_DIR_RISING)
+			cmd = HDC3020_R_T_RH_THRESH_HIGH_CLR;
+		else
+			cmd = HDC3020_R_T_RH_THRESH_LOW_CLR;
+	}
+
+	ret = hdc3020_read_bytes(data, cmd, buf, 3);
+	if (ret < 0)
+		return ret;
+
+	/* CRC check of the threshold */
+	crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
+	if (crc != buf[2])
+		return -EINVAL;
+
+	*thresh = get_unaligned_be16(buf);
+
+	return 0;
+}
+
+static int hdc3020_read_thresh(struct iio_dev *indio_dev,
+			       const struct iio_chan_spec *chan,
+			       enum iio_event_type type,
+			       enum iio_event_direction dir,
+			       enum iio_event_info info,
+			       int *val, int *val2)
+{
+	struct hdc3020_data *data = iio_priv(indio_dev);
+	u16 *thresh;
+
+	/* Select threshold */
+	if (info == IIO_EV_INFO_VALUE) {
+		if (dir == IIO_EV_DIR_RISING)
+			thresh = &data->t_rh_thresh_high;
+		else
+			thresh = &data->t_rh_thresh_low;
+	} else {
+		if (dir == IIO_EV_DIR_RISING)
+			thresh = &data->t_rh_thresh_high_clr;
+		else
+			thresh = &data->t_rh_thresh_low_clr;
+	}
+
+	guard(mutex)(&data->lock);
+	switch (chan->type) {
+	case IIO_TEMP:
+		/* Get the truncated temperature threshold from 9 LSBs,
+		 * shift them and calculate the threshold according to the
+		 * formula in the datasheet.
+		 */
+		*val = ((*thresh) & HDC3020_THRESH_TEMP_MASK) <<
+			HDC3020_THRESH_TEMP_SHIFT;
+		*val = -2949075 + (175 * (*val));
+		*val2 = 65535;
+		break;
+	case IIO_HUMIDITYRELATIVE:
+		/* Get the truncated humidity threshold from 7 MSBs, and
+		 * calculate the threshold according to the formula in the
+		 * datasheet.
+		 */
+		*val = 100 * ((*thresh) & HDC3020_THRESH_HUM_MASK);
+		*val2 = 65535;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return IIO_VAL_FRACTIONAL;
+}
+
+static int hdc3020_clear_status(struct hdc3020_data *data)
+{
+	return hdc3020_write_bytes(data, HDC3020_S_STATUS, 2);
+}
+
+static int hdc3020_read_status(struct hdc3020_data *data, u16 *stat)
+{
+	u8 crc, buf[3];
+	int ret;
+
+	ret = hdc3020_read_bytes(data, HDC3020_R_STATUS, buf, 3);
+	if (ret < 0)
+		return ret;
+
+	/* CRC check of the status */
+	crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
+	if (crc != buf[2])
+		return -EINVAL;
+
+	*stat = get_unaligned_be16(buf);
+
+	return 0;
+}
+
+static irqreturn_t hdc3020_interrupt_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct hdc3020_data *data;
+	u16 stat;
+	int ret;
+
+	data = iio_priv(indio_dev);
+	ret = hdc3020_read_status(data, &stat);
+	if (ret)
+		return IRQ_NONE;
+
+	if (!(stat & (HDC3020_STATUS_T_HIGH_ALERT | HDC3020_STATUS_T_LOW_ALERT |
+		HDC3020_STATUS_RH_HIGH_ALERT | HDC3020_STATUS_RH_LOW_ALERT)))
+		return IRQ_NONE;
+
+	if (stat & HDC3020_STATUS_T_HIGH_ALERT)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
+						  IIO_NO_MOD,
+						  IIO_EV_TYPE_THRESH,
+						  IIO_EV_DIR_RISING),
+						  iio_get_time_ns(indio_dev));
+
+	if (stat & HDC3020_STATUS_T_LOW_ALERT)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
+						  IIO_NO_MOD,
+						  IIO_EV_TYPE_THRESH,
+						  IIO_EV_DIR_FALLING),
+						  iio_get_time_ns(indio_dev));
+
+	if (stat & HDC3020_STATUS_RH_HIGH_ALERT)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0,
+						  IIO_NO_MOD,
+						  IIO_EV_TYPE_THRESH,
+						  IIO_EV_DIR_RISING),
+						  iio_get_time_ns(indio_dev));
+
+	if (stat & HDC3020_STATUS_RH_LOW_ALERT)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0,
+						  IIO_NO_MOD,
+						  IIO_EV_TYPE_THRESH,
+						  IIO_EV_DIR_FALLING),
+						  iio_get_time_ns(indio_dev));
+
+	return IRQ_HANDLED;
+}
+
 static const struct iio_info hdc3020_info = {
 	.read_raw = hdc3020_read_raw,
 	.write_raw = hdc3020_write_raw,
 	.read_avail = hdc3020_read_available,
+	.read_event_value = hdc3020_read_thresh,
+	.write_event_value = hdc3020_write_thresh,
 };
 
 static void hdc3020_stop(void *data)
@@ -402,6 +693,7 @@ static void hdc3020_stop(void *data)
 
 static int hdc3020_probe(struct i2c_client *client)
 {
+	const struct i2c_device_id *dev_id;
 	struct iio_dev *indio_dev;
 	struct hdc3020_data *data;
 	int ret;
@@ -413,6 +705,8 @@ static int hdc3020_probe(struct i2c_client *client)
 	if (!indio_dev)
 		return -ENOMEM;
 
+	dev_id = i2c_client_get_device_id(client);
+
 	data = iio_priv(indio_dev);
 	data->client = client;
 	mutex_init(&data->lock);
@@ -425,6 +719,54 @@ static int hdc3020_probe(struct i2c_client *client)
 	indio_dev->channels = hdc3020_channels;
 	indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels);
 
+	/* Read out upper and lower thresholds and hysteresis, which can be the
+	 * default values or values programmed into non-volatile memory.
+	 */
+	ret = _hdc3020_read_thresh(data, IIO_EV_INFO_VALUE, IIO_EV_DIR_FALLING,
+				   &data->t_rh_thresh_low);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Unable to get low thresholds\n");
+
+	ret = _hdc3020_read_thresh(data, IIO_EV_INFO_VALUE, IIO_EV_DIR_RISING,
+				   &data->t_rh_thresh_high);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Unable to get high thresholds\n");
+
+	ret = _hdc3020_read_thresh(data, IIO_EV_INFO_HYSTERESIS,
+				   IIO_EV_DIR_FALLING,
+				   &data->t_rh_thresh_low_clr);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Unable to get low hysteresis\n");
+
+	ret = _hdc3020_read_thresh(data, IIO_EV_INFO_HYSTERESIS,
+				   IIO_EV_DIR_RISING,
+				   &data->t_rh_thresh_high_clr);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Unable to get high hysteresis\n");
+
+	if (client->irq) {
+		/* The alert output is activated by default upon power up, hardware
+		 * reset, and soft reset. Clear the status register before enabling
+		 * the interrupt.
+		 */
+		ret = hdc3020_clear_status(data);
+		if (ret)
+			return ret;
+
+		ret = devm_request_threaded_irq(&client->dev, client->irq,
+						NULL, hdc3020_interrupt_handler,
+						IRQF_TRIGGER_RISING |
+						IRQF_ONESHOT,
+						dev_id->name, indio_dev);
+		if (ret)
+			return dev_err_probe(&client->dev, ret,
+					     "Failed to request IRQ\n");
+	}
+
 	ret = hdc3020_write_bytes(data, HDC3020_S_AUTO_10HZ_MOD0, 2);
 	if (ret)
 		return dev_err_probe(&client->dev, ret,
