diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 81de88ab2..327116fb9 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -41,6 +41,11 @@ module_param(disable_tap_to_click, bool, 0644);
  MODULE_PARM_DESC(disable_tap_to_click,
  	"Disable Tap-To-Click mode reporting for touchpads (only on the K400 currently).");
  
+static bool holdable_thumb_buttons;
+module_param(holdable_thumb_buttons, bool, 0644);
+MODULE_PARM_DESC(holdable_thumb_buttons,
+	"Make it possible to hold down the thumb buttons (only on the MX Anywhere 3 currently).");
+
  #define REPORT_ID_HIDPP_SHORT			0x10
  #define REPORT_ID_HIDPP_LONG			0x11
  #define REPORT_ID_HIDPP_VERY_LONG		0x12
@@ -64,6 +69,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
  #define HIDPP_QUIRK_CLASS_K400			BIT(2)
  #define HIDPP_QUIRK_CLASS_G920			BIT(3)
  #define HIDPP_QUIRK_CLASS_K750			BIT(4)
+#define HIDPP_QUIRK_CLASS_MX_ANYWHERE_3		BIT(5)
  
  /* bits 2..20 are reserved for classes */
  /* #define HIDPP_QUIRK_CONNECT_EVENTS		BIT(21) disabled */
@@ -3122,6 +3128,106 @@ static int k400_connect(struct hid_device *hdev, bool connected)
  	return k400_disable_tap_to_click(hidpp);
  }
  
+/* ------------------------------------------------------------------------- */
+/* Logitech MX Anywhere 3                                                    */
+/* ------------------------------------------------------------------------- */
+
+/*
+ * On the Logitech MX Anywhere 3 the thumb buttons only activate on release.
+ * This is because the mouse also uses those buttons as modifiers to enable
+ * horizontal scrolling with the mouse wheel.
+ *
+ * Users who don't care about horizontal scrolling and want properly
+ * functioning thumb buttons can set the holdable_thumb_buttons parameter.
+ * If it's set we use the feature 0x1b04 (special keys and mouse buttons)
+ * to divert thumb button events and handle them in software.
+ */
+
+#define HIDPP_PAGE_SPECIAL_KM_BTNS			0x1b04
+#define CMD_SPECIAL_KM_BTNS_SET_CID_REPORTING		0x31
+#define EVENT_SPECIAL_KM_BTNS_DIVERTED_BTNS_EVENT	0x00
+
+struct mx_anywhere_3_private_data {
+	u8 feature_index;
+};
+
+static int mx_anywhere_3_connect(struct hid_device *hdev, bool connected)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	struct mx_anywhere_3_private_data *mxa3 = hidpp->private_data;
+	u8 feature_type;
+	int ret;
+	u8 params[5] = { 0 };
+	struct hidpp_report response;
+
+	if (!holdable_thumb_buttons)
+		return 0;
+
+	ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_SPECIAL_KM_BTNS,
+				     &mxa3->feature_index, &feature_type);
+
+	params[1] = 0x53; // BTN_SIDE
+	params[2] = BIT(1) | BIT(0); // dvalid=1 divert=1
+	ret = hidpp_send_fap_command_sync(hidpp, mxa3->feature_index,
+					  CMD_SPECIAL_KM_BTNS_SET_CID_REPORTING,
+					  params, sizeof(params), &response);
+
+	if (ret)
+		return ret;
+
+	params[1] = 0x56; // BTN_EXTRA
+	return hidpp_send_fap_command_sync(hidpp, mxa3->feature_index,
+					   CMD_SPECIAL_KM_BTNS_SET_CID_REPORTING,
+					   params, sizeof(params), &response);
+}
+
+static int mx_anywhere_3_raw_event(struct hid_device *hdev, u8 *data, int size)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	struct mx_anywhere_3_private_data *mxa3 = hidpp->private_data;
+	struct hidpp_report *report = (struct hidpp_report *)data;
+	bool btn_side = 0, btn_extra = 0;
+	int i;
+
+	if (!mxa3->feature_index ||
+	    report->fap.feature_index != mxa3->feature_index ||
+	    report->fap.funcindex_clientid != EVENT_SPECIAL_KM_BTNS_DIVERTED_BTNS_EVENT)
+		return 0;
+
+	for (i = 0; i < 8; i++) {
+		switch (report->fap.params[i]) {
+			case 0x53:
+				btn_side = 1;
+				break;
+			case 0x56:
+				btn_extra = 1;
+				break;
+		}
+	}
+
+	input_report_key(hidpp->input, BTN_SIDE, btn_side);
+	input_report_key(hidpp->input, BTN_EXTRA, btn_extra);
+
+	input_sync(hidpp->input);
+	return 1;
+}
+
+static int mx_anywhere_3_allocate(struct hid_device *hdev)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	struct mx_anywhere_3_private_data *mxa3;
+
+	mxa3 = devm_kzalloc(&hdev->dev,
+			    sizeof(struct mx_anywhere_3_private_data),
+			    GFP_KERNEL);
+	if (!mxa3)
+		return -ENOMEM;
+
+	hidpp->private_data = mxa3;
+
+	return 0;
+};
+
  /* ------------------------------------------------------------------------- */
  /* Logitech G920 Driving Force Racing Wheel for Xbox One                     */
  /* ------------------------------------------------------------------------- */
@@ -3669,6 +3775,8 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
  		return wtp_raw_event(hdev, data, size);
  	else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
  		return m560_raw_event(hdev, data, size);
+	else if (hidpp->quirks & HIDPP_QUIRK_CLASS_MX_ANYWHERE_3)
+		return mx_anywhere_3_raw_event(hdev, data, size);
  
  	return 0;
  }
@@ -3880,6 +3988,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
  		ret = k400_connect(hdev, connected);
  		if (ret)
  			return;
+	} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_MX_ANYWHERE_3) {
+		ret = mx_anywhere_3_connect(hdev, connected);
+		if (ret)
+			return;
  	}
  
  	if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS) {
@@ -4102,6 +4214,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
  		ret = k400_allocate(hdev);
  		if (ret)
  			return ret;
+	} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_MX_ANYWHERE_3) {
+		ret = mx_anywhere_3_allocate(hdev);
+		if (ret)
+			return ret;
  	}
  
  	INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -4275,6 +4391,8 @@ static const struct hid_device_id hidpp_devices[] = {
  	  LDJ_DEVICE(0x4082), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
  	{ /* Mouse Logitech Performance MX */
  	  LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+	{ /* Mouse Logitech MX Anywhere 3 */
+	  LDJ_DEVICE(0x4090), .driver_data = HIDPP_QUIRK_CLASS_MX_ANYWHERE_3 },
  	{ /* Keyboard logitech K400 */
  	  LDJ_DEVICE(0x4024),
  	  .driver_data = HIDPP_QUIRK_CLASS_K400 },
