mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Nitin Joshi <nitjoshi@gmail.com>
To: hansg@kernel.org, ilpo.jarvinen@linux.intel.com
Cc: platform-driver-x86@vger.kernel.org,
	linux-kernel@vger.kernel.org, njoshi1@lenovo.com,
	Nitin Joshi <nitjoshi@gmail.com>,
	Mark Pearson <mpearson-lenovo@squebb.ca>
Subject: [PATCH] platform/x86: thinkpad_acpi: Add support to detect hardware damage detection capability.
Date: Wed,  3 Dec 2025 12:40:32 +0900	[thread overview]
Message-ID: <20251203034032.6018-1-nitjoshi@gmail.com> (raw)

Thinkpads are adding the ability to detect and report hardware damage
status. Add new sysfs interface to identify the impacted component
with status.
Initial support is available for the USB-C replaceable connector.

Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Nitin Joshi<nitjoshi@gmail.com>
---
 .../admin-guide/laptops/thinkpad-acpi.rst     |  26 +++
 drivers/platform/x86/lenovo/thinkpad_acpi.c   | 179 ++++++++++++++++++
 2 files changed, 205 insertions(+)

diff --git a/Documentation/admin-guide/laptops/thinkpad-acpi.rst b/Documentation/admin-guide/laptops/thinkpad-acpi.rst
index 4ab0fef7d440..4a3220529489 100644
--- a/Documentation/admin-guide/laptops/thinkpad-acpi.rst
+++ b/Documentation/admin-guide/laptops/thinkpad-acpi.rst
@@ -54,6 +54,7 @@ detailed description):
 	- Setting keyboard language
 	- WWAN Antenna type
 	- Auxmac
+	- Hardware damage detection capability
 
 A compatibility table by model and feature is maintained on the web
 site, http://ibm-acpi.sf.net/. I appreciate any success or failure
@@ -1576,6 +1577,31 @@ percentage level, above which charging will stop.
 The exact semantics of the attributes may be found in
 Documentation/ABI/testing/sysfs-class-power.
 
+Hardware damage detection capability
+-----------------
+
+sysfs attributes: hwdd_status
+
+Thinkpads are adding the ability to detect and report hardware damage.
+Add new sysfs interface to identify the impacted component.
+Initial support is available for the USB-C replaceable connector.
+
+The available commands are::
+
+        cat /sys/devices/platform/thinkpad_acpi/hwdd_status
+
+This value displays device type and location of device with damage status.
+For example:
+if no damage is detected:
+  No damage detected
+if damage detected:
+  Damage detected:
+  Device: TYPE-C
+  Location: Base, Right side, Center port
+
+The property is read-only. If feature is not supported then sysfs
+class is not created.
+
 Multiple Commands, Module Parameters
 ------------------------------------
 
diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c
index cc19fe520ea9..c3629bed9a8e 100644
--- a/drivers/platform/x86/lenovo/thinkpad_acpi.c
+++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c
@@ -11080,6 +11080,180 @@ static const struct attribute_group auxmac_attr_group = {
 	.attrs = auxmac_attributes,
 };
 
+/*************************************************************************
+ * HWDD subdriver, for the Lenovo Hardware Damage Detection feature.
+ */
+
+#define HWDD_GET_DMG_USBC 0x80000001
+#define HWDD_GET_CAP 0
+#define HWDD_NOT_SUPPORTED BIT(31)
+#define HWDD_SUPPORT_USBC BIT(0)
+#define HWDD_GET_CAP       0
+
+#define DAMAGE_STATUS_BIT  BIT(0)
+#define PORT_STATUS_OFFSET  4
+#define LID_STATUS_OFFSET  8
+#define BASE_STATUS_OFFSET 12
+#define PORT_DETAIL_OFFSET 16
+
+#define PORT_POS_OFFSET    2
+#define PORT_LOC_MASK      0x3
+
+#define PANEL_TOP   0
+#define PANEL_BASE  1
+#define PANEL_LEFT  2
+#define PANEL_RIGHT 3
+
+#define POS_LEFT    0
+#define POS_CENTER  1
+#define POS_RIGHT   2
+
+#define NUM_PORTS 4
+static bool hwdd_support_available;
+static bool ucdd_supported;
+static int hwdd_command(int command, int *output)
+{
+	acpi_handle hwdd_handle;
+
+	if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "HWDD", &hwdd_handle)))
+		return -ENODEV;
+
+	if (!acpi_evalf(hwdd_handle, output, NULL, "dd", command))
+		return -EIO;
+
+	return 0;
+}
+
+static bool display_damage(char *buf, int *count, char *type, unsigned int dmg_status)
+{
+	unsigned char lid_status, base_status, port_status;
+	unsigned char loc_status, pos_status, panel_status;
+	bool damage_detected = false;
+	unsigned int i;
+
+	port_status = (dmg_status >> PORT_STATUS_OFFSET) & 0xF;
+	lid_status = (dmg_status >> LID_STATUS_OFFSET) & 0xF;
+	base_status = (dmg_status >> BASE_STATUS_OFFSET) & 0xF;
+	for (i = 0; i < NUM_PORTS; i++) {
+		if (dmg_status & BIT(i)) {
+			if (port_status & BIT(i)) {
+				*count += sysfs_emit_at(buf, *count, "%s damage detected:\n", type);
+				loc_status = (dmg_status >> (PORT_DETAIL_OFFSET + (4 * i))) & 0xF;
+				pos_status = (loc_status  >> PORT_POS_OFFSET) & PORT_LOC_MASK;
+				panel_status = loc_status & PORT_LOC_MASK;
+
+				*count += sysfs_emit_at(buf, *count, "Location: ");
+				if (lid_status & BIT(i))
+					*count += sysfs_emit_at(buf, *count, "Lid, ");
+				if (base_status & BIT(i))
+					*count += sysfs_emit_at(buf, *count, "Base, ");
+
+				switch (pos_status) {
+				case PANEL_TOP:
+					*count += sysfs_emit_at(buf, *count, "Top, ");
+					break;
+				case PANEL_BASE:
+					*count += sysfs_emit_at(buf, *count, "Bottom, ");
+					break;
+				case PANEL_LEFT:
+					*count += sysfs_emit_at(buf, *count, "Left, ");
+					break;
+				case PANEL_RIGHT:
+					*count += sysfs_emit_at(buf, *count, "Right, ");
+					break;
+				};
+
+				switch (panel_status) {
+				case POS_LEFT:
+					*count += sysfs_emit_at(buf, *count, "Left port\n");
+					break;
+				case POS_CENTER:
+					*count += sysfs_emit_at(buf, *count, "Center port\n");
+					break;
+				case POS_RIGHT:
+					*count += sysfs_emit_at(buf, *count, "Right port\n");
+					break;
+				default:
+					*count += sysfs_emit_at(buf, *count, "Undefined\n");
+					break;
+				};
+				damage_detected = true;
+			}
+		}
+	}
+	return damage_detected;
+}
+
+/* sysfs typc damage detection capability */
+static ssize_t hwdd_status_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	bool damage_detected = false;
+	unsigned int damage_status;
+	int err, count = 0;
+
+	if (ucdd_supported) {
+		/* Get USB TYPE-C damage status */
+		err = hwdd_command(HWDD_GET_DMG_USBC, &damage_status);
+		if (err)
+			return err;
+
+		if (display_damage(buf, &count, "Type-C", damage_status))
+			damage_detected = true;
+	}
+
+	if (!damage_detected)
+		count  = count + sysfs_emit_at(buf, count, "No damage detected\n");
+
+	return count;
+}
+static DEVICE_ATTR_RO(hwdd_status);
+
+static struct attribute *hwdd_attributes[] = {
+	&dev_attr_hwdd_status.attr,
+	NULL
+};
+
+static umode_t hwdd_attr_is_visible(struct kobject *kobj,
+				struct attribute *attr, int n)
+{
+	return hwdd_support_available ? attr->mode : 0;
+}
+
+static const struct attribute_group hwdd_attr_group = {
+	.is_visible = hwdd_attr_is_visible,
+	.attrs = hwdd_attributes,
+};
+
+static int tpacpi_hwdd_init(struct ibm_init_struct *iibm)
+{
+	int err, output;
+
+	/* Below command checks the HWDD damage capability */
+	err = hwdd_command(HWDD_GET_CAP, &output);
+	if (err)
+		return err;
+
+	if (!(output & HWDD_NOT_SUPPORTED))
+		return -ENODEV;
+
+	hwdd_support_available = true;
+
+	/*
+	 * BIT(0) is assigned to check capability of damage detection is
+	 * supported for USB Type-C port or not.
+	 */
+	if (output & HWDD_SUPPORT_USBC)
+		ucdd_supported = true;
+
+	return err;
+}
+
+static struct ibm_struct hwdd_driver_data = {
+	.name = "hwdd",
+};
+
 /* --------------------------------------------------------------------- */
 
 static struct attribute *tpacpi_driver_attributes[] = {
@@ -11139,6 +11313,7 @@ static const struct attribute_group *tpacpi_groups[] = {
 	&kbdlang_attr_group,
 	&dprc_attr_group,
 	&auxmac_attr_group,
+	&hwdd_attr_group,
 	NULL,
 };
 
@@ -11752,6 +11927,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
 		.init = auxmac_init,
 		.data = &auxmac_data,
 	},
+	{
+		.init = tpacpi_hwdd_init,
+		.data = &hwdd_driver_data,
+	},
 };
 
 static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
-- 
2.43.0


             reply	other threads:[~2025-12-03  3:41 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-03  3:40 Nitin Joshi [this message]
2025-12-03  7:52 ` Hans de Goede
2025-12-03 10:18   ` Nitin
2025-12-03 13:16     ` Mark Pearson
2025-12-08  7:19       ` Nitin
2025-12-08  9:32         ` Hans de Goede
2025-12-08 12:04           ` Nitin
2025-12-03  9:44 ` Ilpo Järvinen
2025-12-03 10:34   ` Nitin
2025-12-03 10:39     ` Ilpo Järvinen
2025-12-03 10:53       ` Nitin

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=20251203034032.6018-1-nitjoshi@gmail.com \
    --to=nitjoshi@gmail.com \
    --cc=hansg@kernel.org \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mpearson-lenovo@squebb.ca \
    --cc=njoshi1@lenovo.com \
    --cc=platform-driver-x86@vger.kernel.org \
    /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

all inboxes | Powered by JetHome®