From nobody Sat Jun 13 14:00:49 2026 Received: from mail-pf1-f176.google.com (mail-pf1-f176.google.com [209.85.210.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D21362DCF74 for ; Thu, 7 May 2026 03:32:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778124747; cv=none; b=TiMTrpIgV/+Y2HiQGk93EMZXmtkXqcp6yRCmdP9AZyirN9l7nCllR2EdIlqZOLXAWORfSFA5WB0vzHLcONuWhVm2+vfMQ6IC0mBmwYDIaMfcVOt/2XUmdPRoKSJ2xd6jRAIlJT3enLkJQZcyQ3bK19jnPX7U0aJqPvC6QxZQW6o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778124747; c=relaxed/simple; bh=yyDfmEPivb0/5E/2IFHBQISFwnPHdmmiGmpkGrnEdrU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sruiOPZrijz+qWPQYpbJSFIuwZOz4LBdM4C47wd1TKm5MXYtJHdZ3Ok7M9RyTPo6KzVZzldBFG5+QYZCyDLzrglqHq/qntoZWyjRHH6c0eQX3pa1FuDy3oVXaJ4eLNXLvpQg1IzeuXW7spACVlveodi6fIb53gHmVAmDU9NKAuM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=bWGEfUWP; arc=none smtp.client-ip=209.85.210.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bWGEfUWP" Received: by mail-pf1-f176.google.com with SMTP id d2e1a72fcca58-8353dfdad62so212655b3a.1 for ; Wed, 06 May 2026 20:32:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778124745; x=1778729545; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=3hz0A98B9puJCvkh6JtrxfTFZ4RXGUWv+B5oj53yvm4=; b=bWGEfUWPB/jRoRnLY91v+qR2cLSCazlrEPzIvGqh5nwOi2iXQ0BwszfXjnm5NuIMpX PL0jQTpnG/F6ti1VdhCmLBr8ad/hKvq7P+iUrT3AMPRnMFxOZhoH0/75l2Fqy/6tcCBN AvkyzkN4kYTdXSKkV5DyIFc40zTxtF1XBebJE+zw/4yqEZhZLK9vm52PYBJlF2xKPPGO zxco1wfLYzsoumKbvyhzMJxkYMZx6OVhKnTuoixIwp5FpDVI1Q1MEUs7fmzqGJw+04Gs pc5gc0Zcw4A+FFx4rFfFoBy5H0tkk3kpabYZuiAHxqf8iljzvPx11EkYJTF9EHSKlgPf 3DCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778124745; x=1778729545; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=3hz0A98B9puJCvkh6JtrxfTFZ4RXGUWv+B5oj53yvm4=; b=XW9AcwlVjyzYv70E/Wk3shmrnv0THT6bZ2gPanvpGggu3pLCfEVmmThJ/vfa4JKpMS 5OSMVpDquG0UgkzMqvWTBBLPGJlooZanV361AYJW0h7v0dIBt5CofsJpl1UYu8wiDTBJ +B2YzTg0UZhYCT4x3B3m1w3yzIuS1r1qV0Stjpu4FSKRVpaz7tnDLYQB30zoE/lBdcU8 QKSkIuT18gIbJcd5QQJYzOJDh62Rj+hN2T+aq4IDQxsm22GhdX6dD/3JHw8fDiDmMLhW 5775MS+7/S/Q9A23yCDPGeok0rburMp1ke6EbPbqVktyjWhz4ptTOMjlKKaPV3fP/FTQ JmgA== X-Forwarded-Encrypted: i=1; AFNElJ/PTwJmWTsf8dUPw8jM1LMRjG1DR8ZONVw2+vEBG1QueJ9uU0rAFFk6wNBhqtqsBGw/jsfWjpzNxbYi4Xw=@vger.kernel.org X-Gm-Message-State: AOJu0YxaHZ00MMcB/HewqXfrKTS80CLZmBMvYu/Zem6tKyKe3FXc+DpF sNsG7vPQb6wRU7xbPNSGnhWy1Zduf8B54MiOeKL3Ayxsz29FF94U2UzT X-Gm-Gg: AeBDiev6wygHNUZ71Ab4npYrj1NTahRBduebDTfDfyPg42TFg03b0m+c5DNXBCekhM3 3EogOoB0eTRpHz1iZ0oTtBvW6Kx5nS6D1VewczhyHe632Wb1PB9nfBxEukeUMhEG8J5wp5+HqDf cg0XoQCLhTNKrxRNOjXOcyXVuPmJ6rPqSEQ73b6jaex1YJ7fJSSNiARJ7qHU5PGqldNjAiItif4 tFIOqATicnz6b49SaKjN72VCHN/fn6wXo2QFbhPnD3G0M/2cJxBfOrzFn5N/ufZYPmDtmeNt0Y0 vAFK9OC/0/1tSpcUDwXkdNzlj4g4NIrDiJvRMO6imXDfG8FjDDl0znD4In/kqUr5UUYmj1/iT+w 8UEWWIxjVsoe0UnPFIDYkcuEtGdx7yzeb4ylXuHyRtfkIV1b2w35qa5AwLMAPDiHYF/OZiUcmKv f9HxOk/G21KOuk5KCdg6X4R3Srnyhp6jw= X-Received: by 2002:a05:6a00:b48:b0:82f:280a:d888 with SMTP id d2e1a72fcca58-83a5bbd5fd8mr6171391b3a.12.1778124745097; Wed, 06 May 2026 20:32:25 -0700 (PDT) Received: from mincom1 ([14.67.155.79]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-839682a103esm9171580b3a.51.2026.05.06.20.32.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 20:32:24 -0700 (PDT) From: Jihong Min To: Greg Kroah-Hartman , Mathias Nyman Cc: Guenter Roeck , Jonathan Corbet , Shuah Khan , Mario Limonciello , Basavaraj Natikar , linux-usb@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-doc@vger.kernel.org, linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, Jihong Min Subject: [PATCH v3 1/2] usb: xhci-pci: add generic auxiliary device interface Date: Thu, 7 May 2026 12:31:58 +0900 Message-ID: X-Mailer: git-send-email 2.53.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Some xHCI PCI controllers expose controller-specific functionality that is not part of generic xHCI operation and is better handled by optional child drivers in other subsystems. Add a small auxiliary device registration path for selected xHCI PCI controllers. The initial PCI ID match table lists AMD Promontory 21 (PROM21) 1022:43fd controllers. For matching controllers, xhci-pci creates an auxiliary device and stores it in devres so the remove path destroys it before HCD teardown. Subsystem-specific child drivers can then bind to those devices through the auxiliary bus and keep their hardware-specific logic outside xhci-pci. Assisted-by: Codex:gpt-5.5 Signed-off-by: Jihong Min --- drivers/usb/host/Kconfig | 10 +++++ drivers/usb/host/xhci-pci.c | 83 +++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 0a277a07cf70..e0c2c7ac5c97 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -42,6 +42,16 @@ config USB_XHCI_PCI depends on USB_PCI default y =20 +config USB_XHCI_PCI_AUXDEV + bool "xHCI PCI auxiliary device support" + depends on USB_XHCI_PCI + select AUXILIARY_BUS + help + This enables xHCI PCI support for registering auxiliary devices + for selected controllers. It is used by optional child drivers + that bind to xHCI PCI controller-specific functionality through + the auxiliary bus. + config USB_XHCI_PCI_RENESAS tristate "Support for additional Renesas xHCI controller with firmware" depends on USB_XHCI_PCI diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 585b2f3117b0..618d6840e108 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -8,6 +8,8 @@ * Some code borrowed from the Linux EHCI driver. */ =20 +#include +#include #include #include #include @@ -80,6 +82,7 @@ #define PCI_DEVICE_ID_AMD_RAVEN_15E1_XHCI 0x15e1 #define PCI_DEVICE_ID_AMD_RAVEN2_XHCI 0x15e5 #define PCI_DEVICE_ID_AMD_RENOIR_XHCI 0x1639 +#define PCI_DEVICE_ID_AMD_PROM21_XHCI 0x43fd #define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9 #define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba #define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb @@ -103,6 +106,80 @@ static int xhci_pci_run(struct usb_hcd *hcd); static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_devi= ce *hdev, struct usb_tt *tt, gfp_t mem_flags); =20 +static const struct pci_device_id pci_ids_have_aux[] =3D { + { PCI_DEVICE_DATA(AMD, PROM21_XHCI, "prom21_hwmon") }, + { /* end: all zeroes */ } +}; + +struct xhci_pci_aux_devres { + struct auxiliary_device *auxdev; +}; + +static const char *xhci_pci_aux_dev_name(struct pci_dev *pdev) +{ + const struct pci_device_id *id; + + id =3D pci_match_id(pci_ids_have_aux, pdev); + if (!id) + return NULL; + + return (const char *)id->driver_data; +} + +static void xhci_pci_aux_devres_release(struct device *dev, void *res) +{ + struct xhci_pci_aux_devres *devres =3D res; + + if (devres->auxdev) + auxiliary_device_destroy(devres->auxdev); +} + +static void xhci_pci_try_add_aux_device(struct pci_dev *pdev) +{ + struct xhci_pci_aux_devres *devres; + struct auxiliary_device *auxdev; + const char *aux_dev_name; + + aux_dev_name =3D xhci_pci_aux_dev_name(pdev); + if (!aux_dev_name) + return; + + devres =3D devres_alloc(xhci_pci_aux_devres_release, sizeof(*devres), + GFP_KERNEL); + if (!devres) { + dev_warn(&pdev->dev, + "failed to allocate auxiliary device state\n"); + return; + } + + auxdev =3D auxiliary_device_create(&pdev->dev, KBUILD_MODNAME, + aux_dev_name, NULL, + (pci_domain_nr(pdev->bus) << 16) | + pci_dev_id(pdev)); + if (!auxdev) { + devres_free(devres); + dev_warn(&pdev->dev, "failed to add %s auxiliary device\n", + aux_dev_name); + return; + } + + devres->auxdev =3D auxdev; + devres_add(&pdev->dev, devres); +} + +static void xhci_pci_try_remove_aux_device(struct pci_dev *pdev) +{ + struct xhci_pci_aux_devres *devres; + + devres =3D devres_find(&pdev->dev, xhci_pci_aux_devres_release, NULL, + NULL); + if (!devres || !devres->auxdev) + return; + + auxiliary_device_destroy(devres->auxdev); + devres->auxdev =3D NULL; +} + static const struct xhci_driver_overrides xhci_pci_overrides __initconst = =3D { .reset =3D xhci_pci_setup, .start =3D xhci_pci_run, @@ -677,6 +754,9 @@ int xhci_pci_common_probe(struct pci_dev *dev, const st= ruct pci_device_id *id) if (device_property_read_bool(&dev->dev, "ti,pwron-active-high")) pci_clear_and_set_config_dword(dev, 0xE0, 0, 1 << 22); =20 + if (IS_ENABLED(CONFIG_USB_XHCI_PCI_AUXDEV)) + xhci_pci_try_add_aux_device(dev); + return 0; =20 put_usb3_hcd: @@ -713,6 +793,9 @@ void xhci_pci_remove(struct pci_dev *dev) xhci =3D hcd_to_xhci(pci_get_drvdata(dev)); set_power_d3 =3D xhci->quirks & XHCI_SPURIOUS_WAKEUP; =20 + if (IS_ENABLED(CONFIG_USB_XHCI_PCI_AUXDEV)) + xhci_pci_try_remove_aux_device(dev); + xhci->xhc_state |=3D XHCI_STATE_REMOVING; =20 if (pci_choose_state(dev, PMSG_SUSPEND) =3D=3D PCI_D0) --=20 2.53.0 From nobody Sat Jun 13 14:00:49 2026 Received: from mail-pf1-f176.google.com (mail-pf1-f176.google.com [209.85.210.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5F5DE299931 for ; Thu, 7 May 2026 03:32:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778124752; cv=none; b=gE7gwrZ4zmLvmga5gUTly2DkJSpkWTOQ8kzRPBSPlCuGLGKK7u4Jvi8TSroFO+Rv3oKpyc8q08ilhe6XJric2wfIcD+glvJ1/1ujfHRJB3YjIOc2tQCR9gkSkqXA+95c1soGWbETlw0B/7mvl9IEcsmdBcNKE5/X9qXILN5IXBA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778124752; c=relaxed/simple; bh=ORhq2WDO7G386LW9/s0FeNCzLmO7VAx5a4PnwuQg/2o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NnbxfZgykZmjbWfdpseKYsCBX/jCS5YePZ6CEnFOjy8TYGlTQ7uJ7X2eykeEoVabSQrSZ9hscZDV84T2cQT1bk+cm++TehX6HiHFlC7itiXtIcHEnp+qOCkskwD6tRvaV9tZTAQ8RxZIwRgHPQw47TLkxO94d4ndPOPLw9cKnM0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ffS7Tg6Z; arc=none smtp.client-ip=209.85.210.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ffS7Tg6Z" Received: by mail-pf1-f176.google.com with SMTP id d2e1a72fcca58-839dc688d6cso106945b3a.2 for ; Wed, 06 May 2026 20:32:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778124749; x=1778729549; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=QUVy+5q9S7S6S1WSUJI/Uw4Ov4EFwj2BrsC/teNmfkM=; b=ffS7Tg6ZswgLakyFHZM5Bs378MGWNUdniuvrOIuYVw6V6wP8J3VxEcz6nauEn1+5/T v2Nj1+G/jNNaEXc9L+ab81G/wpok9p79WLwsZ6IntkCfZEzlZ2Iz85KTtUzQUIgpwjEb omG/hy3uj/EGVg0xyP++n7H1iq7AMF38sslj2NVMi7QroqRgrFbuejBX4LdLhlXg0s/v XMoDml82CH2rjx5nZIaApIzPBPG89J5oMj+OZE2EkIXqDRC4PO1Koa6ltGNXGjAAHktL +SZB7hM9zgSoGJtFCAKjBPOTKKqIA8AIAl51j1M66ihnIASbcFAfZOGlHOsXVI0pg7Y2 mStA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778124749; x=1778729549; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=QUVy+5q9S7S6S1WSUJI/Uw4Ov4EFwj2BrsC/teNmfkM=; b=nyxRm5ZAWP84foIjvVOSmGjUcTY2sV7pFKBIU8ZePfg+PdPKiJo+52tq1W7yqKC7o4 abWmCJspUUAZjLSvYiwZks6Vdn8k6eVNQLo+NQ3pXWHq7YsXR0+A5DIw+eJ07wf1240N QsU5K/UahIkhzz/Ab2EpI7N7EqzKXuqjyqxWbSsUJkhSP0RddhyfOGXE1l3Wr9eDsCYq jkn1jZWZoDE5gGV53MgmbgptXat59L67zqoflMaCVko1MfHNGsZLjFOYuNkRLyMNSIw9 bv8qvWOTCZxLU2EqcNIMTn49pK2LpF+Q1k96zuhTj/yOB4KiPIQwIfvR59YWPRC7Mi8e bh9g== X-Forwarded-Encrypted: i=1; AFNElJ8Qtcnud7kc8YJkS15o33Rj7KS5c55CeK6p1YmL0C01Fvms3a5JFdrJ2EXu0Ya+Pyb00QTPx5baezm+jeM=@vger.kernel.org X-Gm-Message-State: AOJu0YzayK3QgUz6JNGgnlGInDV7OUuYF9jOjnDXWZH2vpN/7KTbUa/5 I5/r42pJ163dwiZyraTVM9TZomm1do7GRMWZb1z3QJiqTnZPr5LMwmx3 X-Gm-Gg: AeBDieu1XXYmVwJe2Smxc0HElvyJgQIYniS/zJ3RYz6iXZYdjQ/+8i0wrtPItfv765k X45OBIen2NY2MKLAvpnZiaXFNSzqYj/D78iVlnKfaHCW67/oznJJ5/Gg/CnRAcW1w5VlgD1Y8RQ ydDZU4psMCRBOxc5xSAlFcR/z0FpYawZeM3kSq77y7dkuOhrmMgnnrHJdDvJTDaGKgw78IqCPbl ReLmIljRk4WzXxz4lWkwXIcbjmpgk4dj/tMqBZjde7pfAdT2A8fjoJNtHw7yOeHhbOGJ72WdAwy Cg6K08T8RSADgGFRpjxHV67CkFljHIVR5hTf+LoaeiYTqs/RDobmlZ6KooLeH0X84Bjo9fkYHGj VZWIxccL4zxDhGdwBstIWmmfVcbVbvPjG17IOK9Q9x4zD9bUoThD30bPQyvahsZbB/mYEgd+sbw HCUiYR/Kbf5F12tWq5HHQCcd1AtsY7dtM= X-Received: by 2002:a05:6a00:4195:b0:835:3949:3c22 with SMTP id d2e1a72fcca58-83a5d285a4cmr5938356b3a.27.1778124748618; Wed, 06 May 2026 20:32:28 -0700 (PDT) Received: from mincom1 ([14.67.155.79]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-839682a103esm9171580b3a.51.2026.05.06.20.32.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 20:32:28 -0700 (PDT) From: Jihong Min To: Greg Kroah-Hartman , Mathias Nyman Cc: Guenter Roeck , Jonathan Corbet , Shuah Khan , Mario Limonciello , Basavaraj Natikar , linux-usb@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-doc@vger.kernel.org, linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, Jihong Min Subject: [PATCH v3 2/2] hwmon: add AMD Promontory 21 xHCI temperature sensor support Date: Thu, 7 May 2026 12:31:59 +0900 Message-ID: <0c35058bb088213397b42fca8d51e9ad0bba5169.1778123510.git.hurryman2212@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" PROM21 xHCI controllers expose an 8-bit temperature value through a vendor-specific index/data register pair in the xHCI PCI MMIO BAR region. Add an auxiliary hwmon driver for PROM21 controllers with PCI ID 1022:43fd. PROM21 is an AMD chipset IP used in single-chip or daisy-chained configurations to build AMD 6xx/8xx series chipsets. The vendor index register is at byte offset 0x3000 from the xHCI MMIO BAR base and the vendor data register is at byte offset 0x3008. The driver writes register selector 0x0001e520 to the index register, reads the raw temperature value from the low 8 bits of the data register, and restores the previous index before returning. Expose temp1_input and an xHCI label through hwmon. Register the hwmon device under the parent PCI function so userspace reports it as a PCI adapter, while the auxiliary driver still owns the hwmon lifetime and unregisters it from the auxiliary remove path. No public AMD reference is available for this value. The conversion formula is derived from observed temperature readings: temp[C] =3D raw * 0.9066 - 78.624 Testing showed that the temperature register does not return a valid value while the xHCI PCI function is runtime suspended. By default, the driver does not wake the parent PCI device from hwmon reads and returns -EPERM while the device is suspended. Document the supported device, register access, conversion formula, module parameter, sysfs attributes, and sysfs lookup method. Assisted-by: Codex:gpt-5.5 Signed-off-by: Jihong Min --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/prom21-hwmon.rst | 86 ++++++++ drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/prom21-hwmon.c | 293 +++++++++++++++++++++++++++ 5 files changed, 392 insertions(+) create mode 100644 Documentation/hwmon/prom21-hwmon.rst create mode 100644 drivers/hwmon/prom21-hwmon.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 8b655e5d6b68..41072977f0ef 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -216,6 +216,7 @@ Hardware Monitoring Kernel Drivers pmbus powerz powr1220 + prom21-hwmon pt5161l pxe1610 pwm-fan diff --git a/Documentation/hwmon/prom21-hwmon.rst b/Documentation/hwmon/pro= m21-hwmon.rst new file mode 100644 index 000000000000..0ba763e68ae9 --- /dev/null +++ b/Documentation/hwmon/prom21-hwmon.rst @@ -0,0 +1,86 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver prom21-hwmon +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D + +Supported chips: + + * AMD Promontory 21 (PROM21) xHCI + + Prefix: 'prom21_hwmon' + + PCI ID: 1022:43fd + +Author: + + - Jihong Min + +Description +----------- + +This driver exposes the temperature sensor in AMD PROM21 xHCI controllers. + +The driver binds to an auxiliary device created by the xHCI PCI driver for +supported controllers. The sensor value is accessed through a vendor-speci= fic +index/data register pair in the controller's PCI MMIO BAR. + +PROM21 is an AMD chipset IP used in single-chip or daisy-chained configura= tions +to build AMD 6xx/8xx series chipsets. Since the xHCI controllers are +integrated in PROM21, this temperature can also be used as a monitor for a +temperature close to the AMD chipset temperature. + +Register access +--------------- + +The temperature value is read through a vendor-specific index/data register +pair in the xHCI PCI MMIO BAR. The driver uses the following byte offsets = from +the MMIO BAR base: + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D +0x3000 Vendor index register +0x3008 Vendor data register +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D + +The driver saves the current vendor index register value, writes the +temperature selector ``0x0001e520`` to the vendor index register, reads the +vendor data register, and restores the previous vendor index value before +returning. The raw temperature value is the low 8 bits of the vendor data +register value. + +No public AMD reference is available for the raw value. The temperature +conversion formula is derived from observed PROM21 xHCI temperature readin= gs: + + temp[C] =3D raw * 0.9066 - 78.624 + +Module parameters +----------------- + +pm: bool + Allow runtime PM state changes for device memory access. This is disabled + by default. If disabled, the driver does not wake the xHCI PCI device fr= om + a temperature read. It reads the temperature only when the device is act= ive. + A read from a suspended device returns ``-EPERM``. + +Sysfs entries +------------- + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D +temp1_input Temperature in millidegrees Celsius +temp1_label "xHCI" +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D + +The hwmon device name is ``prom21_hwmon``. The sysfs path depends on the h= wmon +device number assigned by the kernel. Userspace can locate the device by +matching the ``name`` attribute: + +.. code-block:: sh + + for hwmon in /sys/class/hwmon/hwmon*; do + [ "$(cat "$hwmon/name")" =3D "prom21_hwmon" ] || continue + cat "$hwmon/temp1_label" + cat "$hwmon/temp1_input" + done + +``temp1_input`` reports millidegrees Celsius, so a value of ``50113`` means +50.113 degrees Celsius. If the raw register value is invalid, ``temp1_inpu= t`` +returns ``-ENODATA``. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 14e4cea48acc..06d81cc29fec 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -940,6 +940,17 @@ config SENSORS_POWERZ This driver can also be built as a module. If so, the module will be called powerz. =20 +config SENSORS_PROM21 + tristate "AMD Promontory 21 xHCI temperature sensor" + depends on USB_XHCI_PCI + select USB_XHCI_PCI_AUXDEV + help + If you say yes here you get support for the AMD Promontory 21 + (PROM21) xHCI temperature sensor. + + This driver can also be built as a module. If so, the module + will be called prom21-hwmon. + config SENSORS_POWR1220 tristate "Lattice POWR1220 Power Monitoring" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4788996aa137..7693ed3b3f72 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -196,6 +196,7 @@ obj-$(CONFIG_SENSORS_PC87427) +=3D pc87427.o obj-$(CONFIG_SENSORS_PCF8591) +=3D pcf8591.o obj-$(CONFIG_SENSORS_POWERZ) +=3D powerz.o obj-$(CONFIG_SENSORS_POWR1220) +=3D powr1220.o +obj-$(CONFIG_SENSORS_PROM21) +=3D prom21-hwmon.o obj-$(CONFIG_SENSORS_PT5161L) +=3D pt5161l.o obj-$(CONFIG_SENSORS_PWM_FAN) +=3D pwm-fan.o obj-$(CONFIG_SENSORS_QNAP_MCU_HWMON) +=3D qnap-mcu-hwmon.o diff --git a/drivers/hwmon/prom21-hwmon.c b/drivers/hwmon/prom21-hwmon.c new file mode 100644 index 000000000000..1c137304d65d --- /dev/null +++ b/drivers/hwmon/prom21-hwmon.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD PROM21 xHCI Hwmon Implementation + * (only temperature monitoring is supported) + * + * This can be effectively used as the alternative chipset temperature mon= itor. + * + * Copyright (C) 2026 Jihong Min + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROM21_INDEX 0x3000 +#define PROM21_DATA 0x3008 +#define PROM21_TEMP_REG 0x0001e520 + +#define PROM21_HWMON_NAME "prom21_hwmon" +#define PROM21_TEMP_LABEL "xHCI" + +struct prom21_hwmon { + struct pci_dev *pdev; + struct device *hwmon_dev; + void __iomem *regs; + bool removing; + struct mutex lock; /* protects removing and the index/data registers */ +}; + +static bool pm; +module_param(pm, bool, 0444); +MODULE_PARM_DESC(pm, "Allow runtime PM state changes for device memory acc= ess"); + +static void prom21_hwmon_invalidate(struct prom21_hwmon *hwmon) +{ + mutex_lock(&hwmon->lock); + hwmon->removing =3D true; + mutex_unlock(&hwmon->lock); +} + +static int prom21_hwmon_pm_get(struct prom21_hwmon *hwmon, bool *pm_ref) +{ + struct device *dev =3D &hwmon->pdev->dev; + int ret; + + *pm_ref =3D false; + + /* + * PROM21 temperature register access does not return a valid value while + * the parent xHCI PCI function is suspended. By default, only read when + * runtime PM reports the device as active, or when runtime PM is disabled + * and the device is not marked as suspended. If pm=3DY, allow runtime PM + * state changes while accessing the temperature register. + */ + if (pm) { + ret =3D pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + *pm_ref =3D true; + return 0; + } + + ret =3D pm_runtime_get_if_active(dev); + if (ret > 0) { + *pm_ref =3D true; + return 0; + } + + if (ret =3D=3D -EINVAL && !pm_runtime_status_suspended(dev)) + return 0; + + if (!ret || pm_runtime_status_suspended(dev)) + return -EPERM; + + return ret; +} + +/* + * This is not a pure MMIO read. The PROM21 vendor data register is select= ed + * by temporarily writing PROM21_TEMP_REG to the vendor index register. Ke= ep + * the sequence short and restore the previous index before returning. + */ +static int prom21_hwmon_read_temp_raw_restore_index(struct prom21_hwmon *h= wmon, + u8 *raw) +{ + struct device *dev =3D &hwmon->pdev->dev; + bool pm_ref; + u32 index; + u32 data; + int ret; + + /* + * The xHCI PCI remove path destroys the auxiliary device before HCD + * teardown. Keep runtime PM and MMIO inside the critical section so a + * sysfs read cannot use the vendor register pair after remove starts. + */ + mutex_lock(&hwmon->lock); + if (hwmon->removing) { + mutex_unlock(&hwmon->lock); + return -ENODEV; + } + + ret =3D prom21_hwmon_pm_get(hwmon, &pm_ref); + if (ret) { + mutex_unlock(&hwmon->lock); + return ret; + } + + index =3D readl(hwmon->regs + PROM21_INDEX); + /* Select the PROM21 temperature register through the vendor index. */ + writel(PROM21_TEMP_REG, hwmon->regs + PROM21_INDEX); + data =3D readl(hwmon->regs + PROM21_DATA); + /* Restore the previous vendor index register value. */ + writel(index, hwmon->regs + PROM21_INDEX); + readl(hwmon->regs + PROM21_INDEX); + + if (pm_ref) { + /* + * Use autosuspend so repeated sysfs reads do not suspend the + * controller immediately after each successful register access. + */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + mutex_unlock(&hwmon->lock); + + *raw =3D data & 0xff; + if (!*raw || *raw =3D=3D 0xff) + return -ENODATA; + + return 0; +} + +static long prom21_hwmon_raw_to_millicelsius(u8 raw) +{ + /* + * No public AMD reference is available for this value. + * The scale was derived from observed PROM21 xHCI temperature readings: + * temp[C] =3D raw * 0.9066 - 78.624 + */ + return DIV_ROUND_CLOSEST(raw * 9066, 10) - 78624; +} + +static umode_t prom21_hwmon_is_visible(const void *drvdata, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + if (type !=3D hwmon_temp || channel) + return 0; + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; + default: + return 0; + } +} + +static int prom21_hwmon_read(struct device *dev, enum hwmon_sensor_types t= ype, + u32 attr, int channel, long *val) +{ + struct prom21_hwmon *hwmon =3D dev_get_drvdata(dev); + u8 raw; + int ret; + + if (type !=3D hwmon_temp || attr !=3D hwmon_temp_input || channel) + return -EOPNOTSUPP; + + ret =3D prom21_hwmon_read_temp_raw_restore_index(hwmon, &raw); + if (ret) + return ret; + + *val =3D prom21_hwmon_raw_to_millicelsius(raw); + return 0; +} + +static int prom21_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + if (type !=3D hwmon_temp || attr !=3D hwmon_temp_label || channel) + return -EOPNOTSUPP; + + *str =3D PROM21_TEMP_LABEL; + return 0; +} + +static const struct hwmon_ops prom21_hwmon_ops =3D { + .is_visible =3D prom21_hwmon_is_visible, + .read =3D prom21_hwmon_read, + .read_string =3D prom21_hwmon_read_string, +}; + +static const struct hwmon_channel_info *const prom21_hwmon_info[] =3D { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), + NULL, +}; + +static const struct hwmon_chip_info prom21_hwmon_chip_info =3D { + .ops =3D &prom21_hwmon_ops, + .info =3D prom21_hwmon_info, +}; + +static int prom21_hwmon_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct device *dev =3D &auxdev->dev; + struct device *parent =3D dev->parent; + struct prom21_hwmon *hwmon; + struct pci_dev *pdev; + struct usb_hcd *hcd; + int ret; + + if (!parent || !dev_is_pci(parent)) + return -ENODEV; + + pdev =3D to_pci_dev(parent); + hcd =3D pci_get_drvdata(pdev); + if (!hcd) + return dev_err_probe(dev, -ENODEV, + "xHCI HCD data unavailable\n"); + + if (!hcd->regs || hcd->rsrc_len < PROM21_DATA + sizeof(u32)) + return dev_err_probe(dev, -ENODEV, "invalid MMIO resource\n"); + + hwmon =3D devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + ret =3D devm_mutex_init(dev, &hwmon->lock); + if (ret) + return ret; + + hwmon->pdev =3D pdev; + hwmon->regs =3D hcd->regs; + auxiliary_set_drvdata(auxdev, hwmon); + + /* + * Use the PCI function as the hwmon parent so user space reports it as + * a PCI adapter. Lifetime is still owned by this auxiliary driver; + * remove() unregisters the hwmon device before xhci-pci tears down the + * HCD. + */ + hwmon->hwmon_dev =3D + hwmon_device_register_with_info(&pdev->dev, PROM21_HWMON_NAME, + hwmon, &prom21_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon->hwmon_dev)) + return PTR_ERR(hwmon->hwmon_dev); + + return 0; +} + +static void prom21_hwmon_remove(struct auxiliary_device *auxdev) +{ + struct prom21_hwmon *hwmon =3D auxiliary_get_drvdata(auxdev); + + if (hwmon) { + prom21_hwmon_invalidate(hwmon); + hwmon_device_unregister(hwmon->hwmon_dev); + } +} + +static const struct auxiliary_device_id prom21_hwmon_id_table[] =3D { + { .name =3D "xhci_pci." PROM21_HWMON_NAME }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, prom21_hwmon_id_table); + +static struct auxiliary_driver prom21_hwmon_driver =3D { + .name =3D "prom21-hwmon", + .probe =3D prom21_hwmon_probe, + .remove =3D prom21_hwmon_remove, + .id_table =3D prom21_hwmon_id_table, +}; +module_auxiliary_driver(prom21_hwmon_driver); + +MODULE_AUTHOR("Jihong Min "); +MODULE_DESCRIPTION("AMD PROM21 xHCI hwmon driver"); +MODULE_LICENSE("GPL"); --=20 2.53.0