From: Guenter Roeck <linux@roeck-us.net>
To: linux-hwmon@vger.kernel.org
Cc: Tomasz Bojanowski <tomasz@bojanowski.me>,
Guenter Roeck <linux@roeck-us.net>
Subject: [PATCH 4/4] hwmon: (msi_mystic_light) Add support for additional boards
Date: Sat, 7 Nov 2026 16:01:31 -0800 [thread overview]
Message-ID: <20261108000131.1249664-5-linux@roeck-us.net> (raw)
In-Reply-To: <20261108000131.1249664-1-linux@roeck-us.net>
Add voltage monitoring support for three MSI 500- and 600-series
motherboards using USB vendor ID 0x1462:
- MAG B550M MORTAR MAX WIFI (MS-7C94, USB ID 1462:7c94):
CPU VDDP, PROM Core
- MAG B650 TOMAHAWK WIFI (MS-7D75, USB ID 1462:7d75):
CPU 1P8, CHIP Core, VDD MISC
- MAG B650M MORTAR WIFI (MS-7D76, USB ID 1462:7d76):
CPU 1P8, CHIP Core, VDD MISC
Unlike the 800-series MS-7E61 controller, which returns sensor data
synchronously via HID_REQ_GET_REPORT on feature report 0xf0, the
controllers on these boards require sending a 64-byte output report
(report ID 0xfa, command 0xa0, page 0x01 0xfc) via
hid_hw_output_report(), after which the controller reports the sensor
readings asynchronously via an interrupt IN transfer handled by the HID
raw_event callback.
Implement msi_ml_read_report_7c94() and msi_ml_raw_event() synchronized
with a completion and spinlock:
- Initialize the completion in the completed state in msi_ml_probe()
so unsolicited raw events are ignored when no read is in progress.
- Call hid_device_io_start() in msi_ml_probe() before the initial
sensor check so raw events are delivered during probe, and stop I/O
on the probe error path.
- Reinitialize the completion before sending the output report, parse
the voltage channels in msi_ml_raw_event() when a matching response
arrives while a request is pending, and wait up to 500 ms for
completion.
Update Documentation/hwmon/msi_mystic_light.rst with the new USB IDs,
communication protocol description, and per-board channel tables.
Cc: Tomasz Bojanowski <tomasz@bojanowski.me>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
Documentation/hwmon/msi_mystic_light.rst | 59 +++++++++--
drivers/hwmon/msi_mystic_light.c | 125 +++++++++++++++++++++++
2 files changed, 178 insertions(+), 6 deletions(-)
diff --git a/Documentation/hwmon/msi_mystic_light.rst b/Documentation/hwmon/msi_mystic_light.rst
index f5847a6b96cc..2b16d811681b 100644
--- a/Documentation/hwmon/msi_mystic_light.rst
+++ b/Documentation/hwmon/msi_mystic_light.rst
@@ -5,6 +5,15 @@ Kernel driver msi_mystic_light
Supported devices:
+* MSI Mystic Light USB controller (USB ID 1462:7c94) on MSI
+ MAG B550M MORTAR MAX WIFI (MS-7C94)
+
+* MSI Mystic Light USB controller (USB ID 1462:7d75) on MSI
+ MAG B650 TOMAHAWK WIFI (MS-7D75)
+
+* MSI Mystic Light USB controller (USB ID 1462:7d76) on MSI
+ MAG B650M MORTAR WIFI (MS-7D76)
+
* MSI Mystic Light USB controller (USB ID 0db0:0076) on these MSI
800-series motherboards:
@@ -15,7 +24,7 @@ Author: Tomasz Bojanowski <tomasz@bojanowski.me>
Description
-----------
-On MSI 800-series motherboards the RGB LED headers ("Mystic Light") are
+On various MSI series motherboards the RGB LED headers ("Mystic Light") are
driven by a microcontroller attached over USB. The controller also measures
a few voltage rails that are not monitored by the Super I/O chip (see
nct6683). This driver reports these voltages.
@@ -31,11 +40,17 @@ the report cannot be read or does not have the expected format, the driver
does not bind to the device. Only the rails that return a reading are
exposed.
-The driver never writes to the controller. The only request it sends is
-a read of HID feature report 0xf0. While the driver is bound, the device
-is kept open, so its interrupt endpoint is polled as it is with the
-vendor software. The hidraw interface remains available, so user space
-tools that control the LEDs keep working.
+On 500- and 600-series motherboards, the driver requests sensor data with
+an output report. Sensor data is subsequently reported by the controller
+with a raw event.
+
+On 800-series motherboards, the driver never writes to the controller.
+The only request it sends is a read of HID feature report 0xf0.
+
+While the driver is bound, the device is kept open, so its interrupt endpoint
+is polled as it is with the vendor software. The hidraw interface remains
+available, so user space tools that control the LEDs keep working.
+
Usage Notes
-----------
@@ -52,6 +67,38 @@ in[0-2]_input Voltage in millivolts
in[0-2]_label Rail name
=============== =========================================================
+MSI MAG B550M MORTAR MAX WIFI (MS-7C94)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+======= ========= ========================================
+Channel Label Typical value
+======= ========= ========================================
+in0 CPU VDDP varies (may be 0 on systems with no APU)
+in1 PROM Core 1.04 V
+======= ========= ========================================
+
+MSI MAG B650 TOMAHAWK WIFI (MS-7D75)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+======= ========= ==========================
+Channel Label Typical value
+======= ========= ==========================
+in0 CPU 1P8 1.80 V
+in1 CHIP Core 1.04 V
+in2 VDD MISC 1.10 V
+======= ========= ==========================
+
+MSI MAG B650M MORTAR WIFI (MS-7D76)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+======= ========= ==========================
+Channel Label Typical value
+======= ========= ==========================
+in0 CPU 1P8 1.80 V
+in1 CHIP Core 1.04 V
+in2 VDD MISC 1.10 V
+======= ========= ==========================
+
MSI MAG B850M MORTAR WIFI (MS-7E61)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/drivers/hwmon/msi_mystic_light.c b/drivers/hwmon/msi_mystic_light.c
index 098b4ecd0596..630ce349dab3 100644
--- a/drivers/hwmon/msi_mystic_light.c
+++ b/drivers/hwmon/msi_mystic_light.c
@@ -18,6 +18,7 @@
#include <linux/bits.h>
#include <linux/build_bug.h>
+#include <linux/completion.h>
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/hid.h>
@@ -25,12 +26,18 @@
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/unaligned.h>
#define USB_VENDOR_ID_MSI_MB 0x0db0
#define USB_PRODUCT_ID_MYSTIC_LIGHT 0x0076
+#define USB_VENDOR_ID_MSI_MB_500 0x1462
+#define USB_PRODUCT_ID_MYSTIC_LIGHT_7C94 0x7c94
+#define USB_PRODUCT_ID_MYSTIC_LIGHT_7D75 0x7d75
+#define USB_PRODUCT_ID_MYSTIC_LIGHT_7D76 0x7d76
+
#define MSI_ML_SENSOR_REPORT_ID 0xf0
#define MSI_ML_SENSOR_REPORT_SIZE 64
@@ -48,6 +55,9 @@ struct msi_ml_data {
struct hid_device *hdev;
struct device *hwmon_dev;
const struct msi_ml_board *board;
+ struct completion done;
+ /* For reinitializing the completion below */
+ spinlock_t report_lock;
u8 *buf; /* report buffer, must be DMA safe */
u16 in[MSI_ML_MAX_IN]; /* mV */
unsigned int in_mask; /* rails with a reading in probe */
@@ -62,12 +72,67 @@ struct msi_ml_board {
int (*read_report)(struct msi_ml_data *priv);
};
+static int msi_ml_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
+{
+ struct msi_ml_data *priv = hid_get_drvdata(hdev);
+ const struct msi_ml_board *board = priv->board;
+ unsigned long flags;
+ int i;
+
+ if (size != MSI_ML_SENSOR_REPORT_SIZE ||
+ data[0] != 0xfa || data[1] != MSI_ML_SENSOR_CMD ||
+ data[2] != 0x01 || data[MSI_ML_SENSOR_PAGE_OFFSET] != MSI_ML_SENSOR_PAGE_HI ||
+ data[MSI_ML_SENSOR_PAGE_OFFSET + 1] != MSI_ML_SENSOR_PAGE_LO)
+ return 0;
+
+ /* only read data when requested */
+ spin_lock_irqsave(&priv->report_lock, flags);
+ if (!completion_done(&priv->done)) {
+ for (i = 0; i < board->num_in; i++)
+ priv->in[i] = get_unaligned_le16(data + board->in_offset[i]);
+ complete_all(&priv->done);
+ }
+ spin_unlock_irqrestore(&priv->report_lock, flags);
+
+ return 0;
+}
+
/*
* Read the sensor report. Called from probe before the hwmon device is
* registered, and afterwards only from the hwmon read callback, which the
* hwmon core serializes. No additional locking is needed for the buffer
* and the cached values.
*/
+
+static int msi_ml_read_report_7c94(struct msi_ml_data *priv)
+{
+ struct hid_device *hdev = priv->hdev;
+ u8 *buf = priv->buf;
+ int ret;
+
+ memset(buf, 0, MSI_ML_SENSOR_REPORT_SIZE);
+ buf[0] = 0xfa;
+ buf[1] = MSI_ML_SENSOR_CMD;
+ buf[MSI_ML_SENSOR_PAGE_OFFSET] = MSI_ML_SENSOR_PAGE_HI;
+ buf[MSI_ML_SENSOR_PAGE_OFFSET + 1] = MSI_ML_SENSOR_PAGE_LO;
+
+ spin_lock_irq(&priv->report_lock);
+ reinit_completion(&priv->done);
+ spin_unlock_irq(&priv->report_lock);
+
+ ret = hid_hw_output_report(hdev, buf, MSI_ML_SENSOR_REPORT_SIZE);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&priv->done, msecs_to_jiffies(500));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ priv->updated = get_jiffies_64();
+
+ return 0;
+}
+
static int msi_ml_read_report_7e61(struct msi_ml_data *priv)
{
const struct msi_ml_board *board = priv->board;
@@ -207,6 +272,18 @@ static int msi_ml_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
goto fail_stop;
+ spin_lock_init(&priv->report_lock);
+ init_completion(&priv->done);
+
+ /*
+ * Start completion in the completed state so msi_ml_raw_event()
+ * only sees !completion_done() while the read function is awaiting
+ * a response.
+ */
+ complete_all(&priv->done);
+
+ hid_device_io_start(hdev);
+
/* Check that the sensor report works, and which rails have a reading */
ret = msi_ml_read_report(priv);
if (ret) {
@@ -240,6 +317,7 @@ static int msi_ml_probe(struct hid_device *hdev, const struct hid_device_id *id)
fail_close:
hid_hw_close(hdev);
+ hid_device_io_stop(hdev);
fail_stop:
hid_hw_stop(hdev);
return ret;
@@ -274,6 +352,28 @@ static const struct msi_ml_board msi_ml_7e61 = {
.read_report = msi_ml_read_report_7e61,
};
+static const char *const msi_ml_7c94_in_label[] = {
+ "CPU VDDP", "PROM Core",
+};
+
+static const u8 msi_ml_7c94_in_offset[] = { 11, 13 };
+
+static const struct msi_ml_board msi_ml_7c94 = {
+ .num_in = ARRAY_SIZE(msi_ml_7c94_in_label),
+ .in_label = msi_ml_7c94_in_label,
+ .in_offset = msi_ml_7c94_in_offset,
+ .read_report = msi_ml_read_report_7c94,
+};
+
+static const u8 msi_ml_7d75_in_offset[] = { 13, 15, 11 };
+
+static const struct msi_ml_board msi_ml_7d75 = {
+ .num_in = ARRAY_SIZE(msi_ml_7e61_in_label),
+ .in_label = msi_ml_7e61_in_label,
+ .in_offset = msi_ml_7d75_in_offset,
+ .read_report = msi_ml_read_report_7c94,
+};
+
static const struct dmi_system_id msi_ml_dmi_table[] __initconst = {
{
.matches = {
@@ -282,11 +382,35 @@ static const struct dmi_system_id msi_ml_dmi_table[] __initconst = {
},
.driver_data = (void *)&msi_ml_7e61,
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-7C94"),
+ },
+ .driver_data = (void *)&msi_ml_7c94,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-7D75"),
+ },
+ .driver_data = (void *)&msi_ml_7d75,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-7D76"),
+ },
+ .driver_data = (void *)&msi_ml_7d75,
+ },
{ }
};
MODULE_DEVICE_TABLE(dmi, msi_ml_dmi_table);
static const struct hid_device_id msi_ml_table[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI_MB_500, USB_PRODUCT_ID_MYSTIC_LIGHT_7C94) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI_MB_500, USB_PRODUCT_ID_MYSTIC_LIGHT_7D75) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI_MB_500, USB_PRODUCT_ID_MYSTIC_LIGHT_7D76) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MSI_MB, USB_PRODUCT_ID_MYSTIC_LIGHT) },
{ }
};
@@ -296,6 +420,7 @@ static struct hid_driver msi_ml_driver = {
.id_table = msi_ml_table,
.probe = msi_ml_probe,
.remove = msi_ml_remove,
+ .raw_event = msi_ml_raw_event,
};
static int __init msi_ml_init(void)
--
2.45.2
next prev parent reply other threads:[~2026-11-08 0:01 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-08 0:01 [PATCH 0/4] hwmon: (msi_mystic_light) Add support for additional MSI motherboards Guenter Roeck
2026-11-08 0:01 ` [PATCH 1/4] hwmon: (msi_mystic_light) Rearrange code to simplify adding per-board read functions Guenter Roeck
2026-11-08 0:01 ` [PATCH 2/4] hwmon: (msi_mystic_light) Stop calling hid_hw_power() Guenter Roeck
2026-11-08 0:01 ` [PATCH 3/4] hwmon: (msi_mystic_light) Make sensor read function board specific Guenter Roeck
2026-11-08 0:01 ` Guenter Roeck [this message]
2026-11-08 8:14 ` [PATCH 0/4] hwmon: (msi_mystic_light) Add support for additional MSI motherboards Tomasz Bojanowski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261108000131.1249664-5-linux@roeck-us.net \
--to=linux@roeck-us.net \
--cc=linux-hwmon@vger.kernel.org \
--cc=tomasz@bojanowski.me \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox