Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH v2] Apply the quirk HID_QUIRK_ALWAYS_POLL to the Edifier QR30 (2d99:a101).
From: Terry Junge @ 2025-11-12  5:20 UTC (permalink / raw)
  To: Rodrigo Lugathe da Conceição Alves, michal.pecio
  Cc: bentiss, dmitry.torokhov, jikos, linux-input, linux-sound,
	linux-usb, linuxsound, stern
In-Reply-To: <20251112015356.1919586-1-lugathe2@gmail.com>

Hi Rodrigo,

You might want to wait a couple of days before sending a v3 so other 
reviewers can get a chance to make comments.

You should include the HID: tag in the subject--
...] HID: Apply...

The patch should be sent

To:  the HID subsystem maintainers
bentiss@kernel.org
jikos@kernel.org

Cc:  at a minimum
linux-input@vger.kernel.org
linux-kernel@vger.kernel.org

plus any of the others already part of this thread.

On 11/11/2025 5:53 PM, Rodrigo Lugathe da Conceição Alves wrote:
> The USB speaker has a bug that causes it to reboot when changing the
> brightness using the physical knob.
> 
> Add a new vendor and product ID entry in hid-ids.h, and register
> the corresponding device in hid-quirks.c with the required quirk.
> 
> ---
> v2:
>   - Fixed title
>   - Simplified commit message
> 
> Signed-off-by: Rodrigo Lugathe da Conceição Alves <lugathe2@gmail.com>
> ---
>   drivers/hid/hid-ids.h    | 3 +++
>   drivers/hid/hid-quirks.c | 1 +
>   2 files changed, 4 insertions(+)
> 
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 0723b4b1c9ec..ece1bea0faa5 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -734,6 +734,9 @@
>   #define USB_VENDOR_ID_JESS2		0x0f30
>   #define USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD 0x0111
>   
> +#define USB_VENDOR_ID_JIELI_TECHNOLOGY		0x2d99
> +#define USB_DEVICE_ID_EDIFIER_QR30	0xa101	/* EDIFIER Hal0 2.0 SE */

Vendor ID 0x2d99 belongs to Edifier International Limited not Jieli
Can you change to USB_VENDOR_ID_EDIFIER instead and move to the 
alphabetically correct location?

> +
>   #define USB_VENDOR_ID_KBGEAR		0x084e
>   #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO	0x1001
>   
> diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
> index bcd4bccf1a7c..d104f4829d93 100644
> --- a/drivers/hid/hid-quirks.c
> +++ b/drivers/hid/hid-quirks.c
> @@ -110,6 +110,7 @@ static const struct hid_device_id hid_quirks[] = {
>   	{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_1f4a), HID_QUIRK_ALWAYS_POLL },
>   	{ HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6680), HID_QUIRK_MULTI_INPUT },
>   	{ HID_USB_DEVICE(USB_VENDOR_ID_INNOMEDIA, USB_DEVICE_ID_INNEX_GENESIS_ATARI), HID_QUIRK_MULTI_INPUT },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_JIELI_TECHNOLOGY, USB_DEVICE_ID_EDIFIER_QR30), HID_QUIRK_ALWAYS_POLL },

as above mentioned rename and move

>   	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2), HID_QUIRK_ALWAYS_POLL },
>   	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M406), HID_QUIRK_MULTI_INPUT },
>   	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M506), HID_QUIRK_MULTI_INPUT },

Thanks for the patch!
Regards,
Terry Junge

^ permalink raw reply

* [PATCH] HID: apple: Add "SONiX KN85 Keyboard" to the list of non-apple keyboards
From: linux @ 2025-11-12  6:06 UTC (permalink / raw)
  Cc: linux-kernel, linux-input, bentiss, jikos

From: Joey Bednar <linux@joeybednar.com>

The SoNiX KN85 keyboard identifies as the "Apple, Inc. Aluminium
Keyboard" and is not recognized as a non-apple keyboard. Adding "SoNiX
KN85 Keyboard" to the list of non-apple keyboards fixes the function
keys.

Signed-off-by: Joey Bednar <linux@joeybednar.com>
---
 drivers/hid/hid-apple.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 61404d7a43ee..01488ffc6dcf 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -354,6 +354,7 @@ static const struct apple_key_translation swapped_fn_leftctrl_keys[] = {
 };
 
 static const struct apple_non_apple_keyboard non_apple_keyboards[] = {
+	{ "SONiX KN85 Keyboard" },
 	{ "SONiX USB DEVICE" },
 	{ "Keychron" },
 	{ "AONE" },
-- 
2.51.2




^ permalink raw reply related

* [PATCH v5 00/11] mfd: macsmc: add rtc, hwmon and hid subdevices
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc, Mark Kettenis, Hector Martin

Hi all,

This series adds support for the remaining SMC subdevices. These are the
RTC, hwmon, and HID devices. They are being submitted together as the RTC
and hwmon drivers both require changes to the SMC DT schema.

The RTC driver is responsible for getting and setting the system clock,
and requires an NVMEM cell. This series replaces Sven's original RTC driver
submission [1].

The hwmon function is an interesting one. While each Apple Silicon device
exposes pretty similar sets of sensors, these all seem to be paired to
different SMC keys in the firmware interface. This is true even when the
sensors are on the SoC. For example, an M1 MacBook Pro will use different
keys to access the LITTLE core temperature sensors to an M1 Mac mini. This
necessitates describing which keys correspond to which sensors for each
device individually, and populating the hwmon structs at runtime. We do
this with a node in the device tree. This series includes only the keys
for sensors which we know to be common to all devices. The SMC is also
responsible for monitoring and controlling fan speeds on systems with fans,
which we expose via the hwmon driver.

The SMC also handles the hardware power button and lid switch. Power
button presses and lid opening/closing are emitted as HID events, so we
add an input subdevice to handle them.

Since there are no real dependencies between the components of this series,
it should be fine for each subsystem to take the relevant patches through
their trees. The mfd one-liners should be taken in order to avoid trivial
conflicts. Per [2], the hwmon driver should be merged along with the preceding
mfd patch adding the __SMC_KEY macro to avoid build errors.

Regards,

James

[1] https://lore.kernel.org/asahi/CAEg-Je84XxLWH7vznQmPRfjf6GxWOu75ZetwN7AdseAwfMLLrQ@mail.gmail.com/T/#t
[2] https://lore.kernel.org/asahi/20251106140050.GQ8064@google.com/

---
Changes in v5:
- Drop inadvertent mfd core includes in rtc and input drivers
- Link to v4: https://lore.kernel.org/r/20251025-macsmc-subdevs-v4-0-374d5c9eba0e@gmail.com

Changes in v4:
- Added Rob's R-b to hwmon Devicetree schema
- Added missing include to hwmon driver
- Dropped superfluous dev_info() from hwmon probe
- Added Guenter's A-b to hwmon driver
- Renamed INPUT_MACSMC_INPUT to INPUT_MACSMC
- Dropped leftover mention of HID in input driver
- Reinstated input driver MODULE_ALIAS
- Trigger a hard wakeup on power button/lid switch when coming out of
  s2idle
- Suppress KEY_POWER event on wakeup to prevent an immediate shutdown
  when waking up
- Squashed hwmon Devicetree commits into one
- Link to v3: https://lore.kernel.org/r/20251007-macsmc-subdevs-v3-0-d7d3bfd7ae02@gmail.com

Changes in v3:
- Renamed macsmc-hid to macsmc-input
- Switched to pm_wakeup_event in macsmc-input
- macsmc-input now configures its capabilities before registering the device
- Renamed macsmc_hwmon to macsmc-hwmon
- Dropped module aliases in macsmc-input and macsmc_hwmon
- Introduced new SMC FourCC macro to silence GCC errors
- Condensed hwmon binding using $defs
- Made label property optional for hwmon sensors
- Fixed incorrect hwmon is_visible implementation
- Dropped 64-bit math from SMC float ops
- Fixed incorrect use of error numbers in hwmon driver
- Replaced a number of non-fatal dev_errs with dev_dbgs in hwmon driver
- Added hwmon driver documentation
- Added hwmon subdevice directly to the DT SMC node
- Included "common" hwmon sensors in SoC .dtsi files
- Fixed typo in hwmon-common.dtsi
- Added Neal's R-b to series
- Added required nodes to t602x Devicetrees
- Link to v2: https://lore.kernel.org/r/20250827-macsmc-subdevs-v2-0-ce5e99d54c28@gmail.com

Changes in v2:
- Added Rob's R-b tag to RTC DT binding
- Removed redundant nesting from hwmon DT binding
- Dedpulicated property definitions in hwmon DT schema
- Made label a required property for hwmon DT nodes
- Clarified semantics in hwmon DT schema definitions
- Split mfd tree changes into separate commits
- Fixed numerous style errors in hwmon driver
- Removed log messages sysfs read/write functions in hwmon driver
- Removed ignored errors from hwmon driver
- Removed uses of dev_err for non-errors in hwmon driver
- Made it more obvious that a number of hwmon fan properties are optional
- Modified hwmon driver to reflect DT schema changes
- Added compatible property to hwmon node
- Link to v1: https://lore.kernel.org/r/20250819-macsmc-subdevs-v1-0-57df6c3e5f19@gmail.com

---
Hector Martin (2):
      rtc: Add new rtc-macsmc driver for Apple Silicon Macs
      input: macsmc-input: New driver to handle the Apple Mac SMC buttons/lid

James Calligeros (7):
      dt-bindings: hwmon: Add Apple System Management Controller hwmon schema
      mfd: macsmc: Wire up Apple SMC RTC subdevice
      mfd: macsmc: Add new __SMC_KEY macro
      hwmon: Add Apple Silicon SMC hwmon driver
      mfd: macsmc: Wire up Apple SMC hwmon subdevice
      mfd: macsmc: Wire up Apple SMC input subdevice
      arm64: dts: apple: t8103, t8112, t60xx: Add hwmon SMC subdevice

Sven Peter (2):
      dt-bindings: rtc: Add Apple SMC RTC
      arm64: dts: apple: t8103,t60xx,t8112: Add SMC RTC node

 .../bindings/hwmon/apple,smc-hwmon.yaml  |  86 +++
 .../bindings/mfd/apple,smc.yaml          |  45 ++
 .../bindings/rtc/apple,smc-rtc.yaml      |  35 +
 Documentation/hwmon/macsmc-hwmon.rst     |  71 +++
 MAINTAINERS                              |   6 +
 .../boot/dts/apple/hwmon-common.dtsi     |  33 +
 .../boot/dts/apple/hwmon-fan-dual.dtsi   |  22 +
 arch/arm64/boot/dts/apple/hwmon-fan.dtsi |  17 +
 .../boot/dts/apple/hwmon-laptop.dtsi     |  33 +
 .../boot/dts/apple/hwmon-mac-mini.dtsi   |  15 +
 .../arm64/boot/dts/apple/t6001-j375c.dts |   2 +
 arch/arm64/boot/dts/apple/t6001.dtsi     |   2 +
 .../arm64/boot/dts/apple/t6002-j375d.dts |   2 +
 .../arm64/boot/dts/apple/t600x-die0.dtsi |  10 +
 .../boot/dts/apple/t600x-j314-j316.dtsi  |   3 +
 .../arm64/boot/dts/apple/t602x-die0.dtsi |  10 +
 arch/arm64/boot/dts/apple/t8103-j274.dts |   2 +
 arch/arm64/boot/dts/apple/t8103-j293.dts |   3 +
 arch/arm64/boot/dts/apple/t8103-j313.dts |   2 +
 arch/arm64/boot/dts/apple/t8103-j456.dts |   2 +
 arch/arm64/boot/dts/apple/t8103-j457.dts |   2 +
 arch/arm64/boot/dts/apple/t8103.dtsi     |  11 +
 arch/arm64/boot/dts/apple/t8112-j413.dts |   2 +
 arch/arm64/boot/dts/apple/t8112-j473.dts |   2 +
 arch/arm64/boot/dts/apple/t8112-j493.dts |   3 +
 arch/arm64/boot/dts/apple/t8112.dtsi     |  11 +
 drivers/hwmon/Kconfig                    |  12 +
 drivers/hwmon/Makefile                   |   1 +
 drivers/hwmon/macsmc-hwmon.c             | 851 +++++++++++++++++++++++++
 drivers/input/misc/Kconfig               |  11 +
 drivers/input/misc/Makefile              |   1 +
 drivers/input/misc/macsmc-input.c        | 207 ++++++
 drivers/mfd/macsmc.c                     |   3 +
 drivers/rtc/Kconfig                      |  11 +
 drivers/rtc/Makefile                     |   1 +
 drivers/rtc/rtc-macsmc.c                 | 140 ++++
 include/linux/mfd/macsmc.h               |   1 +
 37 files changed, 1671 insertions(+)
---
base-commit: 24172e0d79900908cf5ebf366600616d29c9b417
change-id: 20250816-macsmc-subdevs-87032c017d0c

Best regards,
-- 
James Calligeros <jcalligeros99@gmail.com>


^ permalink raw reply

* [PATCH v5 01/11] dt-bindings: rtc: Add Apple SMC RTC
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc, Mark Kettenis
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

From: Sven Peter <sven@kernel.org>

Apple Silicon Macs (M1, etc.) have an RTC that is part of the PMU IC,
but most of the PMU functionality is abstracted out by the SMC.
An additional RTC offset stored inside NVMEM is required to compute
the current date/time.

Reviewed-by: Mark Kettenis <kettenis@openbsd.org>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Sven Peter <sven@kernel.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 .../bindings/mfd/apple,smc.yaml          |  9 +++++++
 .../bindings/rtc/apple,smc-rtc.yaml      | 35 +++++++++++++++++++++++++
 MAINTAINERS                              |  1 +
 3 files changed, 45 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/apple,smc.yaml b/Documentation/devicetree/bindings/mfd/apple,smc.yaml
index 5429538f7e2e..0410e712c900 100644
--- a/Documentation/devicetree/bindings/mfd/apple,smc.yaml
+++ b/Documentation/devicetree/bindings/mfd/apple,smc.yaml
@@ -46,6 +46,9 @@ properties:
   reboot:
     $ref: /schemas/power/reset/apple,smc-reboot.yaml
 
+  rtc:
+    $ref: /schemas/rtc/apple,smc-rtc.yaml
+
 additionalProperties: false
 
 required:
@@ -80,5 +83,11 @@ examples:
           nvmem-cell-names = "shutdown_flag", "boot_stage",
                              "boot_error_count", "panic_count";
         };
+
+        rtc {
+          compatible = "apple,smc-rtc";
+          nvmem-cells = <&rtc_offset>;
+          nvmem-cell-names = "rtc_offset";
+       };
       };
     };
diff --git a/Documentation/devicetree/bindings/rtc/apple,smc-rtc.yaml b/Documentation/devicetree/bindings/rtc/apple,smc-rtc.yaml
new file mode 100644
index 000000000000..607b610665a2
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/apple,smc-rtc.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/apple,smc-rtc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple SMC RTC
+
+description:
+  Apple Silicon Macs (M1, etc.) have an RTC that is part of the PMU IC,
+  but most of the PMU functionality is abstracted out by the SMC.
+  An additional RTC offset stored inside NVMEM is required to compute
+  the current date/time.
+
+maintainers:
+  - Sven Peter <sven@kernel.org>
+
+properties:
+  compatible:
+    const: apple,smc-rtc
+
+  nvmem-cells:
+    items:
+      - description: 48bit RTC offset, specified in 32768 (2^15) Hz clock ticks
+
+  nvmem-cell-names:
+    items:
+      - const: rtc_offset
+
+required:
+  - compatible
+  - nvmem-cells
+  - nvmem-cell-names
+
+additionalProperties: false
diff --git a/MAINTAINERS b/MAINTAINERS
index f0c8b85baa6b..51942a9a9b43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2439,6 +2439,7 @@ F:	Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
 F:	Documentation/devicetree/bindings/power/apple*
 F:	Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml
 F:	Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml
+F:	Documentation/devicetree/bindings/rtc/apple,smc-rtc.yaml
 F:	Documentation/devicetree/bindings/spi/apple,spi.yaml
 F:	Documentation/devicetree/bindings/spmi/apple,spmi.yaml
 F:	Documentation/devicetree/bindings/watchdog/apple,wdt.yaml

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 02/11] dt-bindings: hwmon: Add Apple System Management Controller hwmon schema
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

Apple Silicon devices integrate a vast array of sensors, monitoring
current, power, temperature, and voltage across almost every part of
the system. The sensors themselves are all connected to the System
Management Controller (SMC). The SMC firmware exposes the data
reported by these sensors via its standard FourCC-based key-value
API. The SMC is also responsible for monitoring and controlling any
fans connected to the system, exposing them in the same way.

For reasons known only to Apple, each device exposes its sensors with
an almost totally unique set of keys. This is true even for devices
which share an SoC. An M1 Mac mini, for example, will report its core
temperatures on different keys to an M1 MacBook Pro. Worse still, the
SMC does not provide a way to enumerate the available keys at runtime,
nor do the keys follow any sort of reasonable or consistent naming
rules that could be used to deduce their purpose. We must therefore
know which keys are present on any given device, and which function
they serve, ahead of time.

Add a schema so that we can describe the available sensors for a given
Apple Silicon device in the Devicetree.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 .../bindings/hwmon/apple,smc-hwmon.yaml  | 86 +++++++++++++++++++++++++
 .../bindings/mfd/apple,smc.yaml          | 36 +++++++++++
 MAINTAINERS                              |  1 +
 3 files changed, 123 insertions(+)

diff --git a/Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml
new file mode 100644
index 000000000000..2eec317bc4b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/apple,smc-hwmon.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple SMC Hardware Monitoring
+
+description:
+  Apple's System Management Controller (SMC) exposes a vast array of
+  hardware monitoring sensors, including temperature probes, current and
+  voltage sense, power meters, and fan speeds. It also provides endpoints
+  to manually control the speed of each fan individually. Each Apple
+  Silicon device exposes a different set of endpoints via SMC keys. This
+  is true even when two machines share an SoC. The CPU core temperature
+  sensor keys on an M1 Mac mini are different to those on an M1 MacBook
+  Pro, for example.
+
+maintainers:
+  - James Calligeros <jcalligeros99@gmail.com>
+
+$defs:
+  sensor:
+    type: object
+
+    properties:
+      apple,key-id:
+        $ref: /schemas/types.yaml#/definitions/string
+        pattern: "^[A-Za-z0-9]{4}$"
+        description: The SMC FourCC key of the desired sensor.
+          Must match the node's suffix.
+
+      label:
+        description: Human-readable name for the sensor
+
+    required:
+      - apple,key-id
+
+properties:
+  compatible:
+    const: apple,smc-hwmon
+
+patternProperties:
+  "^current-[A-Za-z0-9]{4}$":
+    $ref: "#/$defs/sensor"
+    unevaluatedProperties: false
+
+  "^fan-[A-Za-z0-9]{4}$":
+    $ref: "#/$defs/sensor"
+    unevaluatedProperties: false
+
+    properties:
+      apple,fan-minimum:
+        $ref: /schemas/types.yaml#/definitions/string
+        pattern: "^[A-Za-z0-9]{4}$"
+        description: SMC key containing the fan's minimum speed
+
+      apple,fan-maximum:
+        $ref: /schemas/types.yaml#/definitions/string
+        pattern: "^[A-Za-z0-9]{4}$"
+        description: SMC key containing the fan's maximum speed
+
+      apple,fan-target:
+        $ref: /schemas/types.yaml#/definitions/string
+        pattern: "^[A-Za-z0-9]{4}$"
+        description: Writeable endpoint for setting desired fan speed
+
+      apple,fan-mode:
+        $ref: /schemas/types.yaml#/definitions/string
+        pattern: "^[A-Za-z0-9]{4}$"
+        description: Writeable key to enable/disable manual fan control
+
+
+  "^power-[A-Za-z0-9]{4}$":
+    $ref: "#/$defs/sensor"
+    unevaluatedProperties: false
+
+  "^temperature-[A-Za-z0-9]{4}$":
+    $ref: "#/$defs/sensor"
+    unevaluatedProperties: false
+
+  "^voltage-[A-Za-z0-9]{4}$":
+    $ref: "#/$defs/sensor"
+    unevaluatedProperties: false
+
+additionalProperties: false
diff --git a/Documentation/devicetree/bindings/mfd/apple,smc.yaml b/Documentation/devicetree/bindings/mfd/apple,smc.yaml
index 0410e712c900..34ce048619f5 100644
--- a/Documentation/devicetree/bindings/mfd/apple,smc.yaml
+++ b/Documentation/devicetree/bindings/mfd/apple,smc.yaml
@@ -49,6 +49,9 @@ properties:
   rtc:
     $ref: /schemas/rtc/apple,smc-rtc.yaml
 
+  hwmon:
+    $ref: /schemas/hwmon/apple,smc-hwmon.yaml
+
 additionalProperties: false
 
 required:
@@ -89,5 +92,38 @@ examples:
           nvmem-cells = <&rtc_offset>;
           nvmem-cell-names = "rtc_offset";
        };
+
+        hwmon {
+          compatible = "apple,smc-hwmon";
+
+          current-ID0R {
+            apple,key-id = "ID0R";
+            label = "AC Input Current";
+          };
+
+          fan-F0Ac {
+            apple,key-id = "F0Ac";
+            apple,fan-minimum = "F0Mn";
+            apple,fan-maximum = "F0Mx";
+            apple,fan-target = "F0Tg";
+            apple,fan-mode = "F0Md";
+            label = "Fan 1";
+          };
+
+          power-PSTR {
+            apple,key-id = "PSTR";
+            label = "Total System Power";
+          };
+
+          temperature-TW0P {
+            apple,key-id = "TW0P";
+            label = "WiFi/BT Module Temperature";
+          };
+
+          voltage-VD0R {
+            apple,key-id = "VD0R";
+            label = "AC Input Voltage";
+          };
+        };
       };
     };
diff --git a/MAINTAINERS b/MAINTAINERS
index 51942a9a9b43..6e5e219c5fe6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2422,6 +2422,7 @@ F:	Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml
 F:	Documentation/devicetree/bindings/dma/apple,admac.yaml
 F:	Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml
 F:	Documentation/devicetree/bindings/gpu/apple,agx.yaml
+F:	Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml
 F:	Documentation/devicetree/bindings/i2c/apple,i2c.yaml
 F:	Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/apple,*

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 03/11] rtc: Add new rtc-macsmc driver for Apple Silicon Macs
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc, Hector Martin
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

From: Hector Martin <marcan@marcan.st>

Apple Silicon Macs (M1, etc.) have an RTC that is part of the PMU IC,
but most of the PMU functionality is abstracted out by the SMC.
On T600x machines, the RTC counter must be accessed via the SMC to
get full functionality, and it seems likely that future machines
will move towards making SMC handle all RTC functionality.

The SMC RTC counter access is implemented on all current machines
as of the time of this writing, on firmware 12.x. However, the RTC
offset (needed to set the time) is still only accessible via direct
PMU access. To handle this, we expose the RTC offset as an NVMEM
cell from the SPMI PMU device node, and this driver consumes that
cell and uses it to compute/set the current time.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Sven Peter <sven@kernel.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 MAINTAINERS              |   1 +
 drivers/rtc/Kconfig      |  11 ++
 drivers/rtc/Makefile     |   1 +
 drivers/rtc/rtc-macsmc.c | 140 +++++++++++++++++++++++++
 4 files changed, 153 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 6e5e219c5fe6..5cf253a25831 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2464,6 +2464,7 @@ F:	drivers/nvmem/apple-spmi-nvmem.c
 F:	drivers/pinctrl/pinctrl-apple-gpio.c
 F:	drivers/power/reset/macsmc-reboot.c
 F:	drivers/pwm/pwm-apple.c
+F:	drivers/rtc/rtc-macsmc.c
 F:	drivers/soc/apple/*
 F:	drivers/spi/spi-apple.c
 F:	drivers/spmi/spmi-apple-controller.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2933c41c77c8..cf3fa1475ffd 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -2074,6 +2074,17 @@ config RTC_DRV_WILCO_EC
 	  This can also be built as a module. If so, the module will
 	  be named "rtc_wilco_ec".
 
+config RTC_DRV_MACSMC
+	tristate "Apple Mac System Management Controller RTC"
+	depends on MFD_MACSMC
+	help
+	  If you say yes here you get support for RTC functions
+	  inside Apple SPMI PMUs accessed through the SoC's
+	  System Management Controller
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rtc-macsmc.
+
 config RTC_DRV_MSC313
 	tristate "MStar MSC313 RTC"
         depends on ARCH_MSTARV7 || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 8221bda6e6dc..0485b09caceb 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_RTC_DRV_M48T35)	+= rtc-m48t35.o
 obj-$(CONFIG_RTC_DRV_M48T59)	+= rtc-m48t59.o
 obj-$(CONFIG_RTC_DRV_M48T86)	+= rtc-m48t86.o
 obj-$(CONFIG_RTC_DRV_MA35D1)	+= rtc-ma35d1.o
+obj-$(CONFIG_RTC_DRV_MACSMC)	+= rtc-macsmc.o
 obj-$(CONFIG_RTC_DRV_MAX31335)	+= rtc-max31335.o
 obj-$(CONFIG_RTC_DRV_MAX6900)	+= rtc-max6900.o
 obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
diff --git a/drivers/rtc/rtc-macsmc.c b/drivers/rtc/rtc-macsmc.c
new file mode 100644
index 000000000000..8fe883066956
--- /dev/null
+++ b/drivers/rtc/rtc-macsmc.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Apple SMC RTC driver
+ * Copyright The Asahi Linux Contributors
+ */
+
+#include <linux/bitops.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+/* 48-bit RTC */
+#define RTC_BYTES 6
+#define RTC_BITS (8 * RTC_BYTES)
+
+/* 32768 Hz clock */
+#define RTC_SEC_SHIFT 15
+
+struct macsmc_rtc {
+	struct device *dev;
+	struct apple_smc *smc;
+	struct rtc_device *rtc_dev;
+	struct nvmem_cell *rtc_offset;
+};
+
+static int macsmc_rtc_get_time(struct device *dev, struct rtc_time *tm)
+{
+	struct macsmc_rtc *rtc = dev_get_drvdata(dev);
+	u64 ctr = 0, off = 0;
+	time64_t now;
+	void *p_off;
+	size_t len;
+	int ret;
+
+	ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES);
+	if (ret < 0)
+		return ret;
+	if (ret != RTC_BYTES)
+		return -EIO;
+
+	p_off = nvmem_cell_read(rtc->rtc_offset, &len);
+	if (IS_ERR(p_off))
+		return PTR_ERR(p_off);
+	if (len < RTC_BYTES) {
+		kfree(p_off);
+		return -EIO;
+	}
+
+	memcpy(&off, p_off, RTC_BYTES);
+	kfree(p_off);
+
+	/* Sign extend from 48 to 64 bits, then arithmetic shift right 15 bits to get seconds */
+	now = sign_extend64(ctr + off, RTC_BITS - 1) >> RTC_SEC_SHIFT;
+	rtc_time64_to_tm(now, tm);
+
+	return ret;
+}
+
+static int macsmc_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct macsmc_rtc *rtc = dev_get_drvdata(dev);
+	u64 ctr = 0, off = 0;
+	int ret;
+
+	ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES);
+	if (ret < 0)
+		return ret;
+	if (ret != RTC_BYTES)
+		return -EIO;
+
+	/* This sets the offset such that the set second begins now */
+	off = (rtc_tm_to_time64(tm) << RTC_SEC_SHIFT) - ctr;
+	return nvmem_cell_write(rtc->rtc_offset, &off, RTC_BYTES);
+}
+
+static const struct rtc_class_ops macsmc_rtc_ops = {
+	.read_time = macsmc_rtc_get_time,
+	.set_time = macsmc_rtc_set_time,
+};
+
+static int macsmc_rtc_probe(struct platform_device *pdev)
+{
+	struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
+	struct macsmc_rtc *rtc;
+
+	/*
+	 * MFD will probe this device even without a node in the device tree,
+	 * thus bail out early if the SMC on the current machines does not
+	 * support RTC and has no node in the device tree.
+	 */
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	rtc->dev = &pdev->dev;
+	rtc->smc = smc;
+
+	rtc->rtc_offset = devm_nvmem_cell_get(&pdev->dev, "rtc_offset");
+	if (IS_ERR(rtc->rtc_offset))
+		return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_offset),
+				     "Failed to get rtc_offset NVMEM cell\n");
+
+	rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+	if (IS_ERR(rtc->rtc_dev))
+		return PTR_ERR(rtc->rtc_dev);
+
+	rtc->rtc_dev->ops = &macsmc_rtc_ops;
+	rtc->rtc_dev->range_min = S64_MIN >> (RTC_SEC_SHIFT + (64 - RTC_BITS));
+	rtc->rtc_dev->range_max = S64_MAX >> (RTC_SEC_SHIFT + (64 - RTC_BITS));
+
+	platform_set_drvdata(pdev, rtc);
+
+	return devm_rtc_register_device(rtc->rtc_dev);
+}
+
+static const struct of_device_id macsmc_rtc_of_table[] = {
+	{ .compatible = "apple,smc-rtc", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, macsmc_rtc_of_table);
+
+static struct platform_driver macsmc_rtc_driver = {
+	.driver = {
+		.name = "macsmc-rtc",
+		.of_match_table = macsmc_rtc_of_table,
+	},
+	.probe = macsmc_rtc_probe,
+};
+module_platform_driver(macsmc_rtc_driver);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("Apple SMC RTC driver");
+MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 04/11] mfd: macsmc: Wire up Apple SMC RTC subdevice
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

Add the new SMC RTC function to the mfd device

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 drivers/mfd/macsmc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c
index e6cdae221f1d..500395bb48da 100644
--- a/drivers/mfd/macsmc.c
+++ b/drivers/mfd/macsmc.c
@@ -47,6 +47,7 @@
 static const struct mfd_cell apple_smc_devs[] = {
 	MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"),
 	MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"),
+	MFD_CELL_OF("macsmc-rtc", NULL, NULL, 0, 0, "apple,smc-rtc"),
 };
 
 static int apple_smc_cmd_locked(struct apple_smc *smc, u64 cmd, u64 arg,

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 05/11] mfd: macsmc: Add new __SMC_KEY macro
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

When using the _SMC_KEY macro in switch/case statements, GCC 15.2.1 errors
out with 'case label does not reduce to an integer constant'. Introduce
a new __SMC_KEY macro that can be used instead.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 include/linux/mfd/macsmc.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/mfd/macsmc.h b/include/linux/mfd/macsmc.h
index 6b13f01a8592..f6f80c33b5cf 100644
--- a/include/linux/mfd/macsmc.h
+++ b/include/linux/mfd/macsmc.h
@@ -41,6 +41,7 @@ typedef u32 smc_key;
  */
 #define SMC_KEY(s) (smc_key)(_SMC_KEY(#s))
 #define _SMC_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3])
+#define __SMC_KEY(a, b, c, d) (((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d)))
 
 #define APPLE_SMC_READABLE BIT(7)
 #define APPLE_SMC_WRITABLE BIT(6)

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 06/11] hwmon: Add Apple Silicon SMC hwmon driver
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

The System Management Controller on Apple Silicon devices is responsible
for integrating and exposing the data reported by the vast array of
hardware monitoring sensors present on these devices. It is also
responsible for fan control, and allows users to manually set fan
speeds if they so desire. Add a hwmon driver to expose current,
power, temperature, and voltage monitoring sensors, as well as
fan speed monitoring and control via the SMC on Apple Silicon devices.

The SMC firmware has no consistency between devices, even when they
share an SoC. The FourCC keys used to access sensors are almost
random. An M1 Mac mini will have different FourCCs for its CPU core
temperature sensors to an M1 MacBook Pro, for example. For this
reason, the valid sensors for a given device are specified in a
child of the SMC Devicetree node. The driver uses this information
to determine which sensors to make available at runtime.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Co-developed-by: Janne Grunau <j@jannau.net>
Signed-off-by: Janne Grunau <j@jannau.net>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 Documentation/hwmon/macsmc-hwmon.rst |  71 +++
 MAINTAINERS                          |   2 +
 drivers/hwmon/Kconfig                |  12 +
 drivers/hwmon/Makefile               |   1 +
 drivers/hwmon/macsmc-hwmon.c         | 851 +++++++++++++++++++++++++
 5 files changed, 937 insertions(+)

diff --git a/Documentation/hwmon/macsmc-hwmon.rst b/Documentation/hwmon/macsmc-hwmon.rst
new file mode 100644
index 000000000000..6903f76df62b
--- /dev/null
+++ b/Documentation/hwmon/macsmc-hwmon.rst
@@ -0,0 +1,71 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver macsmc-hwmon
+==========================
+
+Supported hardware
+
+    * Apple Silicon Macs (M1 and up)
+
+Author: James Calligeros <jcalligeros99@gmail.com>
+
+Description
+-----------
+
+macsmc-hwmon exposes the Apple System Management controller's
+temperature, voltage, current and power sensors, as well as
+fan speed and control capabilities, via hwmon.
+
+Because each Apple Silicon Mac exposes a different set of sensors
+(e.g. the MacBooks expose battery telemetry that is not present on
+the desktop Macs), sensors present on any given machine are described
+via Devicetree. The driver picks these up and registers them with
+hwmon when probed.
+
+Manual fan speed is supported via the fan_control module parameter. This
+is disabled by default and marked as unsafe, as it cannot be proven that
+the system will fail safe if overheating due to manual fan control being
+used.
+
+sysfs interface
+---------------
+
+currX_input
+    Ammeter value
+
+currX_label
+    Ammeter label
+
+fanX_input
+    Current fan speed
+
+fanX_label
+    Fan label
+
+fanX_min
+    Minimum possible fan speed
+
+fanX_max
+    Maximum possible fan speed
+
+fanX_target
+    Current fan setpoint
+
+inX_input
+    Voltmeter value
+
+inX_label
+    Voltmeter label
+
+powerX_input
+    Power meter value
+
+powerX_label
+    Power meter label
+
+tempX_input
+    Temperature sensor value
+
+tempX_label
+    Temperature sensor label
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 5cf253a25831..fb55a167699e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2444,12 +2444,14 @@ F:	Documentation/devicetree/bindings/rtc/apple,smc-rtc.yaml
 F:	Documentation/devicetree/bindings/spi/apple,spi.yaml
 F:	Documentation/devicetree/bindings/spmi/apple,spmi.yaml
 F:	Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
+F:	Documentation/hwmon/macsmc-hwmon.rst
 F:	arch/arm64/boot/dts/apple/
 F:	drivers/bluetooth/hci_bcm4377.c
 F:	drivers/clk/clk-apple-nco.c
 F:	drivers/cpufreq/apple-soc-cpufreq.c
 F:	drivers/dma/apple-admac.c
 F:	drivers/gpio/gpio-macsmc.c
+F:	drivers/hwmon/macsmc-hwmon.c
 F:	drivers/pmdomain/apple/
 F:	drivers/i2c/busses/i2c-pasemi-core.c
 F:	drivers/i2c/busses/i2c-pasemi-platform.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2760feb9f83b..d6e9e39d2762 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1174,6 +1174,18 @@ config SENSORS_LTQ_CPUTEMP
 	  If you say yes here you get support for the temperature
 	  sensor inside your CPU.
 
+config SENSORS_MACSMC_HWMON
+	tristate "Apple SMC (Apple Silicon)"
+	depends on MFD_MACSMC && OF
+	help
+	  This driver enables hwmon support for current, power, temperature,
+	  and voltage sensors, as well as fan speed reporting and control
+	  on Apple Silicon devices. Say Y here if you have an Apple Silicon
+	  device.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called macsmc-hwmon.
+
 config SENSORS_MAX1111
 	tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles"
 	depends on SPI_MASTER
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 73b2abdcc6dd..f9c049ce9124 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_LTC4260)	+= ltc4260.o
 obj-$(CONFIG_SENSORS_LTC4261)	+= ltc4261.o
 obj-$(CONFIG_SENSORS_LTC4282)	+= ltc4282.o
 obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o
+obj-$(CONFIG_SENSORS_MACSMC_HWMON)	+= macsmc-hwmon.o
 obj-$(CONFIG_SENSORS_MAX1111)	+= max1111.o
 obj-$(CONFIG_SENSORS_MAX127)	+= max127.o
 obj-$(CONFIG_SENSORS_MAX16065)	+= max16065.o
diff --git a/drivers/hwmon/macsmc-hwmon.c b/drivers/hwmon/macsmc-hwmon.c
new file mode 100644
index 000000000000..1c0bbec7e8eb
--- /dev/null
+++ b/drivers/hwmon/macsmc-hwmon.c
@@ -0,0 +1,851 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Apple SMC hwmon driver for Apple Silicon platforms
+ *
+ * The System Management Controller on Apple Silicon devices is responsible for
+ * measuring data from sensors across the SoC and machine. These include power,
+ * temperature, voltage and current sensors. Some "sensors" actually expose
+ * derived values. An example of this is the key PHPC, which is an estimate
+ * of the heat energy being dissipated by the SoC.
+ *
+ * While each SoC only has one SMC variant, each platform exposes a different
+ * set of sensors. For example, M1 MacBooks expose battery telemetry sensors
+ * which are not present on the M1 Mac mini. For this reason, the available
+ * sensors for a given platform are described in the device tree in a child
+ * node of the SMC device. We must walk this list of available sensors and
+ * populate the required hwmon data structures at runtime.
+ *
+ * Originally based on a concept by Jean-Francois Bortolotti <jeff@borto.fr>
+ *
+ * Copyright The Asahi Linux Contributors
+ */
+
+#include <linux/bitfield.h>
+#include <linux/hwmon.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define MAX_LABEL_LENGTH	32
+
+/* Temperature, voltage, current, power, fan(s) */
+#define NUM_SENSOR_TYPES	5
+
+#define FLT_EXP_BIAS	127
+#define FLT_EXP_MASK	GENMASK(30, 23)
+#define FLT_MANT_BIAS	23
+#define FLT_MANT_MASK	GENMASK(22, 0)
+#define FLT_SIGN_MASK	BIT(31)
+
+static bool fan_control;
+module_param_unsafe(fan_control, bool, 0644);
+MODULE_PARM_DESC(fan_control,
+		 "Override the SMC to set your own fan speeds on supported machines");
+
+struct macsmc_hwmon_sensor {
+	struct apple_smc_key_info info;
+	smc_key macsmc_key;
+	char label[MAX_LABEL_LENGTH];
+	u32 attrs;
+};
+
+struct macsmc_hwmon_fan {
+	struct macsmc_hwmon_sensor now;
+	struct macsmc_hwmon_sensor min;
+	struct macsmc_hwmon_sensor max;
+	struct macsmc_hwmon_sensor set;
+	struct macsmc_hwmon_sensor mode;
+	char label[MAX_LABEL_LENGTH];
+	u32 attrs;
+	bool manual;
+};
+
+struct macsmc_hwmon_sensors {
+	struct hwmon_channel_info channel_info;
+	struct macsmc_hwmon_sensor *sensors;
+	u32 count;
+};
+
+struct macsmc_hwmon_fans {
+	struct hwmon_channel_info channel_info;
+	struct macsmc_hwmon_fan *fans;
+	u32 count;
+};
+
+struct macsmc_hwmon {
+	struct device *dev;
+	struct apple_smc *smc;
+	struct device *hwmon_dev;
+	struct hwmon_chip_info chip_info;
+	/* Chip + sensor types + NULL */
+	const struct hwmon_channel_info *channel_infos[1 + NUM_SENSOR_TYPES + 1];
+	struct macsmc_hwmon_sensors temp;
+	struct macsmc_hwmon_sensors volt;
+	struct macsmc_hwmon_sensors curr;
+	struct macsmc_hwmon_sensors power;
+	struct macsmc_hwmon_fans fan;
+};
+
+static int macsmc_hwmon_read_label(struct device *dev,
+				   enum hwmon_sensor_types type, u32 attr,
+				   int channel, const char **str)
+{
+	struct macsmc_hwmon *hwmon = dev_get_drvdata(dev);
+
+	switch (type) {
+	case hwmon_temp:
+		*str = hwmon->temp.sensors[channel].label;
+		break;
+	case hwmon_in:
+		*str = hwmon->volt.sensors[channel].label;
+		break;
+	case hwmon_curr:
+		*str = hwmon->curr.sensors[channel].label;
+		break;
+	case hwmon_power:
+		*str = hwmon->power.sensors[channel].label;
+		break;
+	case hwmon_fan:
+		*str = hwmon->fan.fans[channel].label;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+/*
+ * A number of sensors report data in a 48.16 fixed-point decimal format that is
+ * not used by any other function of the SMC.
+ */
+static int macsmc_hwmon_read_ioft_scaled(struct apple_smc *smc, smc_key key,
+					 u64 *p, int scale)
+{
+	u64 val;
+	int ret;
+
+	ret = apple_smc_read_u64(smc, key, &val);
+	if (ret < 0)
+		return ret;
+
+	*p = mult_frac(val, scale, 65536);
+
+	return 0;
+}
+
+/*
+ * Many sensors report their data as IEEE-754 floats. No other SMC function uses
+ * them.
+ */
+static int macsmc_hwmon_read_f32_scaled(struct apple_smc *smc, smc_key key,
+					int *p, int scale)
+{
+	u32 fval;
+	u64 val;
+	int ret, exp;
+
+	ret = apple_smc_read_u32(smc, key, &fval);
+	if (ret < 0)
+		return ret;
+
+	val = ((u64)((fval & FLT_MANT_MASK) | BIT(23)));
+	exp = ((fval >> 23) & 0xff) - FLT_EXP_BIAS - FLT_MANT_BIAS;
+
+	/* We never have negatively scaled SMC floats */
+	val *= scale;
+
+	if (exp > 63)
+		val = U64_MAX;
+	else if (exp < -63)
+		val = 0;
+	else if (exp < 0)
+		val >>= -exp;
+	else if (exp != 0 && (val & ~((1UL << (64 - exp)) - 1))) /* overflow */
+		val = U64_MAX;
+	else
+		val <<= exp;
+
+	if (fval & FLT_SIGN_MASK) {
+		if (val > (-(s64)INT_MIN))
+			*p = INT_MIN;
+		else
+			*p = -val;
+	} else {
+		if (val > INT_MAX)
+			*p = INT_MAX;
+		else
+			*p = val;
+	}
+
+	return 0;
+}
+
+/*
+ * The SMC has keys of multiple types, denoted by a FourCC of the same format
+ * as the key ID. We don't know what data type a key encodes until we poke at it.
+ */
+static int macsmc_hwmon_read_key(struct apple_smc *smc,
+				 struct macsmc_hwmon_sensor *sensor, int scale,
+				 long *val)
+{
+	int ret;
+
+	switch (sensor->info.type_code) {
+	/* 32-bit IEEE 754 float */
+	case __SMC_KEY('f', 'l', 't', ' '): {
+		u32 flt_ = 0;
+
+		ret = macsmc_hwmon_read_f32_scaled(smc, sensor->macsmc_key,
+						   &flt_, scale);
+		if (ret)
+			return ret;
+
+		*val = flt_;
+		break;
+	}
+	/* 48.16 fixed point decimal */
+	case __SMC_KEY('i', 'o', 'f', 't'): {
+		u64 ioft = 0;
+
+		ret = macsmc_hwmon_read_ioft_scaled(smc, sensor->macsmc_key,
+						    &ioft, scale);
+		if (ret)
+			return ret;
+
+		*val = (long)ioft;
+		break;
+	}
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int macsmc_hwmon_write_f32(struct apple_smc *smc, smc_key key, int value)
+{
+	u64 val;
+	u32 fval = 0;
+	int exp = 0, neg;
+
+	val = abs(value);
+	neg = val != value;
+
+	if (val) {
+		int msb = __fls(val) - exp;
+
+		if (msb > 23) {
+			val >>= msb - FLT_MANT_BIAS;
+			exp -= msb - FLT_MANT_BIAS;
+		} else if (msb < 23) {
+			val <<= FLT_MANT_BIAS - msb;
+			exp += msb;
+		}
+
+		fval = FIELD_PREP(FLT_SIGN_MASK, neg) |
+		       FIELD_PREP(FLT_EXP_MASK, exp + FLT_EXP_BIAS) |
+		       FIELD_PREP(FLT_MANT_MASK, val);
+	}
+
+	return apple_smc_write_u32(smc, key, fval);
+}
+
+static int macsmc_hwmon_write_key(struct apple_smc *smc,
+				  struct macsmc_hwmon_sensor *sensor, long val)
+{
+	switch (sensor->info.type_code) {
+	/* 32-bit IEEE 754 float */
+	case __SMC_KEY('f', 'l', 't', ' '):
+		return macsmc_hwmon_write_f32(smc, sensor->macsmc_key, val);
+	/* unsigned 8-bit integer */
+	case __SMC_KEY('u', 'i', '8', ' '):
+		return apple_smc_write_u8(smc, sensor->macsmc_key, val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int macsmc_hwmon_read_fan(struct macsmc_hwmon *hwmon, u32 attr, int chan,
+				 long *val)
+{
+	switch (attr) {
+	case hwmon_fan_input:
+		return macsmc_hwmon_read_key(hwmon->smc,
+					     &hwmon->fan.fans[chan].now, 1, val);
+	case hwmon_fan_min:
+		return macsmc_hwmon_read_key(hwmon->smc,
+					     &hwmon->fan.fans[chan].min, 1, val);
+	case hwmon_fan_max:
+		return macsmc_hwmon_read_key(hwmon->smc,
+					     &hwmon->fan.fans[chan].max, 1, val);
+	case hwmon_fan_target:
+		return macsmc_hwmon_read_key(hwmon->smc,
+					     &hwmon->fan.fans[chan].set, 1, val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel,
+				  long val)
+{
+	struct macsmc_hwmon *hwmon = dev_get_drvdata(dev);
+	long min, max;
+	int ret;
+
+	if (!fan_control || hwmon->fan.fans[channel].mode.macsmc_key == 0)
+		return -EOPNOTSUPP;
+
+	/*
+	 * The SMC does no sanity checks on requested fan speeds, so we need to.
+	 */
+	ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].min,
+				    1, &min);
+	if (ret)
+		return ret;
+
+	ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].max,
+				    1, &max);
+	if (ret)
+		return ret;
+
+	if (val >= min && val <= max) {
+		if (!hwmon->fan.fans[channel].manual) {
+			/* Write 1 to mode key for manual control */
+			ret = macsmc_hwmon_write_key(hwmon->smc,
+						     &hwmon->fan.fans[channel].mode, 1);
+			if (ret < 0)
+				return ret;
+
+			hwmon->fan.fans[channel].manual = true;
+		}
+		return macsmc_hwmon_write_key(hwmon->smc,
+					      &hwmon->fan.fans[channel].set, val);
+	} else if (!val) {
+		if (hwmon->fan.fans[channel].manual) {
+			ret = macsmc_hwmon_write_key(hwmon->smc,
+						     &hwmon->fan.fans[channel].mode, 0);
+			if (ret < 0)
+				return ret;
+
+			hwmon->fan.fans[channel].manual = false;
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int macsmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+			     u32 attr, int channel, long *val)
+{
+	struct macsmc_hwmon *hwmon = dev_get_drvdata(dev);
+	int ret = 0;
+
+	switch (type) {
+	case hwmon_temp:
+		ret = macsmc_hwmon_read_key(hwmon->smc,
+					    &hwmon->temp.sensors[channel], 1000, val);
+		break;
+	case hwmon_in:
+		ret = macsmc_hwmon_read_key(hwmon->smc,
+					    &hwmon->volt.sensors[channel], 1000, val);
+		break;
+	case hwmon_curr:
+		ret = macsmc_hwmon_read_key(hwmon->smc,
+					    &hwmon->curr.sensors[channel], 1000, val);
+		break;
+	case hwmon_power:
+		/* SMC returns power in Watts with acceptable precision to scale to uW */
+		ret = macsmc_hwmon_read_key(hwmon->smc,
+					    &hwmon->power.sensors[channel],
+					    1000000, val);
+		break;
+	case hwmon_fan:
+		ret = macsmc_hwmon_read_fan(hwmon, attr, channel, val);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+
+static int macsmc_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+			      u32 attr, int channel, long val)
+{
+	switch (type) {
+	case hwmon_fan:
+		return macsmc_hwmon_write_fan(dev, attr, channel, val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static umode_t macsmc_hwmon_fan_is_visible(const struct macsmc_hwmon_fan *fan,
+					   u32 attr)
+{
+	if (fan->attrs & BIT(attr)) {
+		if (attr == hwmon_fan_target && fan_control && fan->mode.macsmc_key)
+			return 0644;
+
+		return 0444;
+	}
+
+	return 0;
+}
+
+static umode_t macsmc_hwmon_is_visible(const void *data,
+				       enum hwmon_sensor_types type, u32 attr,
+				       int channel)
+{
+	const struct macsmc_hwmon *hwmon = data;
+	struct macsmc_hwmon_sensor *sensor;
+
+	switch (type) {
+	case hwmon_in:
+		sensor = &hwmon->volt.sensors[channel];
+		break;
+	case hwmon_curr:
+		sensor = &hwmon->curr.sensors[channel];
+		break;
+	case hwmon_power:
+		sensor = &hwmon->power.sensors[channel];
+		break;
+	case hwmon_temp:
+		sensor = &hwmon->temp.sensors[channel];
+		break;
+	case hwmon_fan:
+		return macsmc_hwmon_fan_is_visible(&hwmon->fan.fans[channel], attr);
+	default:
+		return 0;
+	}
+
+	/* Sensors only register ro attributes */
+	if (sensor->attrs & BIT(attr))
+		return 0444;
+
+	return 0;
+}
+
+static const struct hwmon_ops macsmc_hwmon_ops = {
+	.is_visible = macsmc_hwmon_is_visible,
+	.read = macsmc_hwmon_read,
+	.read_string = macsmc_hwmon_read_label,
+	.write = macsmc_hwmon_write,
+};
+
+/*
+ * Get the key metadata, including key data type, from the SMC.
+ */
+static int macsmc_hwmon_parse_key(struct device *dev, struct apple_smc *smc,
+				  struct macsmc_hwmon_sensor *sensor,
+				  const char *key)
+{
+	int ret;
+
+	ret = apple_smc_get_key_info(smc, _SMC_KEY(key), &sensor->info);
+	if (ret) {
+		dev_dbg(dev, "Failed to retrieve key info for %s\n", key);
+		return ret;
+	}
+
+	sensor->macsmc_key = _SMC_KEY(key);
+
+	return 0;
+}
+
+/*
+ * A sensor is a single key-value pair as made available by the SMC.
+ * The devicetree gives us the SMC key ID and a friendly name where the
+ * purpose of the sensor is known.
+ */
+static int macsmc_hwmon_create_sensor(struct device *dev, struct apple_smc *smc,
+				      struct device_node *sensor_node,
+				      struct macsmc_hwmon_sensor *sensor)
+{
+	const char *key, *label;
+	int ret;
+
+	ret = of_property_read_string(sensor_node, "apple,key-id", &key);
+	if (ret) {
+		dev_dbg(dev, "Could not find apple,key-id in sensor node\n");
+		return ret;
+	}
+
+	ret = macsmc_hwmon_parse_key(dev, smc, sensor, key);
+	if (ret)
+		return ret;
+
+	ret = of_property_read_string(sensor_node, "label", &label);
+	if (ret)
+		dev_dbg(dev, "No label found for sensor %s\n", key);
+	else
+		strscpy_pad(sensor->label, label, sizeof(sensor->label));
+
+	return 0;
+}
+
+/*
+ * Fan data is exposed by the SMC as multiple sensors.
+ *
+ * The devicetree schema reuses apple,key-id for the actual fan speed sensor.
+ * Min, max and target keys do not need labels, so we can reuse label
+ * for naming the entire fan.
+ */
+static int macsmc_hwmon_create_fan(struct device *dev, struct apple_smc *smc,
+				   struct device_node *fan_node,
+				   struct macsmc_hwmon_fan *fan)
+{
+	const char *label, *now, *min, *max, *set, *mode;
+	int ret;
+
+	ret = of_property_read_string(fan_node, "apple,key-id", &now);
+	if (ret) {
+		dev_err(dev, "apple,key-id not found in fan node!\n");
+		return ret;
+	}
+
+	ret = macsmc_hwmon_parse_key(dev, smc, &fan->now, now);
+	if (ret)
+		return ret;
+
+	fan->attrs = HWMON_F_INPUT;
+
+	ret = of_property_read_string(fan_node, "label", &label);
+	if (ret) {
+		dev_dbg(dev, "No label found for fan %s\n", now);
+	} else {
+		strscpy_pad(fan->label, label, sizeof(fan->label));
+		fan->attrs |= HWMON_F_LABEL;
+	}
+
+	/* The following keys are not required to simply monitor fan speed */
+	if (!of_property_read_string(fan_node, "apple,fan-minimum", &min)) {
+		ret = macsmc_hwmon_parse_key(dev, smc, &fan->min, min);
+		if (ret)
+			return ret;
+
+		fan->attrs |= HWMON_F_MIN;
+	}
+
+	if (!of_property_read_string(fan_node, "apple,fan-maximum", &max)) {
+		ret = macsmc_hwmon_parse_key(dev, smc, &fan->max, max);
+		if (ret)
+			return ret;
+
+		fan->attrs |= HWMON_F_MAX;
+	}
+
+	if (!of_property_read_string(fan_node, "apple,fan-target", &set)) {
+		ret = macsmc_hwmon_parse_key(dev, smc, &fan->set, set);
+		if (ret)
+			return ret;
+
+		fan->attrs |= HWMON_F_TARGET;
+	}
+
+	if (!of_property_read_string(fan_node, "apple,fan-mode", &mode)) {
+		ret = macsmc_hwmon_parse_key(dev, smc, &fan->mode, mode);
+		if (ret)
+			return ret;
+	}
+
+	/* Initialise fan control mode to automatic */
+	fan->manual = false;
+
+	return 0;
+}
+
+static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon,
+					 struct device_node *hwmon_node)
+{
+	struct device_node *key_node __maybe_unused;
+	struct macsmc_hwmon_sensor *sensor;
+	u32 n_current = 0, n_fan = 0, n_power = 0, n_temperature = 0, n_voltage = 0;
+
+	for_each_child_of_node_with_prefix(hwmon_node, key_node, "current-") {
+		n_current++;
+	}
+
+	if (n_current) {
+		hwmon->curr.sensors = devm_kcalloc(hwmon->dev, n_current,
+						   sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL);
+		if (!hwmon->curr.sensors)
+			return -ENOMEM;
+
+		for_each_child_of_node_with_prefix(hwmon_node, key_node, "current-") {
+			sensor = &hwmon->curr.sensors[hwmon->curr.count];
+			if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) {
+				sensor->attrs = HWMON_C_INPUT;
+
+				if (*sensor->label)
+					sensor->attrs |= HWMON_C_LABEL;
+
+				hwmon->curr.count++;
+			}
+		}
+	}
+
+	for_each_child_of_node_with_prefix(hwmon_node, key_node, "fan-") {
+		n_fan++;
+	}
+
+	if (n_fan) {
+		hwmon->fan.fans = devm_kcalloc(hwmon->dev, n_fan,
+					       sizeof(struct macsmc_hwmon_fan), GFP_KERNEL);
+		if (!hwmon->fan.fans)
+			return -ENOMEM;
+
+		for_each_child_of_node_with_prefix(hwmon_node, key_node, "fan-") {
+			if (!macsmc_hwmon_create_fan(hwmon->dev, hwmon->smc, key_node,
+						     &hwmon->fan.fans[hwmon->fan.count]))
+				hwmon->fan.count++;
+		}
+	}
+
+	for_each_child_of_node_with_prefix(hwmon_node, key_node, "power-") {
+		n_power++;
+	}
+
+	if (n_power) {
+		hwmon->power.sensors = devm_kcalloc(hwmon->dev, n_power,
+						    sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL);
+		if (!hwmon->power.sensors)
+			return -ENOMEM;
+
+		for_each_child_of_node_with_prefix(hwmon_node, key_node, "power-") {
+			sensor = &hwmon->power.sensors[hwmon->power.count];
+			if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) {
+				sensor->attrs = HWMON_P_INPUT;
+
+				if (*sensor->label)
+					sensor->attrs |= HWMON_P_LABEL;
+
+				hwmon->power.count++;
+			}
+		}
+	}
+
+	for_each_child_of_node_with_prefix(hwmon_node, key_node, "temperature-") {
+		n_temperature++;
+	}
+
+	if (n_temperature) {
+		hwmon->temp.sensors = devm_kcalloc(hwmon->dev, n_temperature,
+						   sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL);
+		if (!hwmon->temp.sensors)
+			return -ENOMEM;
+
+		for_each_child_of_node_with_prefix(hwmon_node, key_node, "temperature-") {
+			sensor = &hwmon->temp.sensors[hwmon->temp.count];
+			if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) {
+				sensor->attrs = HWMON_T_INPUT;
+
+				if (*sensor->label)
+					sensor->attrs |= HWMON_T_LABEL;
+
+				hwmon->temp.count++;
+			}
+		}
+	}
+
+	for_each_child_of_node_with_prefix(hwmon_node, key_node, "voltage-") {
+		n_voltage++;
+	}
+
+	if (n_voltage) {
+		hwmon->volt.sensors = devm_kcalloc(hwmon->dev, n_voltage,
+						   sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL);
+		if (!hwmon->volt.sensors)
+			return -ENOMEM;
+
+		for_each_child_of_node_with_prefix(hwmon_node, key_node, "volt-") {
+			sensor = &hwmon->temp.sensors[hwmon->temp.count];
+			if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) {
+				sensor->attrs = HWMON_I_INPUT;
+
+				if (*sensor->label)
+					sensor->attrs |= HWMON_I_LABEL;
+
+				hwmon->volt.count++;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* Create NULL-terminated config arrays */
+static void macsmc_hwmon_populate_configs(u32 *configs, const struct macsmc_hwmon_sensors *sensors)
+{
+	int idx;
+
+	for (idx = 0; idx < sensors->count; idx++)
+		configs[idx] = sensors->sensors[idx].attrs;
+}
+
+static void macsmc_hwmon_populate_fan_configs(u32 *configs, const struct macsmc_hwmon_fans *fans)
+{
+	int idx;
+
+	for (idx = 0; idx < fans->count; idx++)
+		configs[idx] = fans->fans[idx].attrs;
+}
+
+static const struct hwmon_channel_info *const macsmc_chip_channel_info =
+	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ);
+
+static int macsmc_hwmon_create_infos(struct macsmc_hwmon *hwmon)
+{
+	struct hwmon_channel_info *channel_info;
+	int i = 0;
+
+	/* chip */
+	hwmon->channel_infos[i++] = macsmc_chip_channel_info;
+
+	if (hwmon->curr.count) {
+		channel_info = &hwmon->curr.channel_info;
+		channel_info->type = hwmon_curr;
+		channel_info->config = devm_kcalloc(hwmon->dev, hwmon->curr.count + 1,
+						    sizeof(u32), GFP_KERNEL);
+		if (!channel_info->config)
+			return -ENOMEM;
+
+		macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->curr);
+		hwmon->channel_infos[i++] = channel_info;
+	}
+
+	if (hwmon->fan.count) {
+		channel_info = &hwmon->fan.channel_info;
+		channel_info->type = hwmon_fan;
+		channel_info->config = devm_kcalloc(hwmon->dev, hwmon->fan.count + 1,
+						    sizeof(u32), GFP_KERNEL);
+		if (!channel_info->config)
+			return -ENOMEM;
+
+		macsmc_hwmon_populate_fan_configs((u32 *)channel_info->config, &hwmon->fan);
+		hwmon->channel_infos[i++] = channel_info;
+	}
+
+	if (hwmon->power.count) {
+		channel_info = &hwmon->power.channel_info;
+		channel_info->type = hwmon_power;
+		channel_info->config = devm_kcalloc(hwmon->dev, hwmon->power.count + 1,
+						    sizeof(u32), GFP_KERNEL);
+		if (!channel_info->config)
+			return -ENOMEM;
+
+		macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->power);
+		hwmon->channel_infos[i++] = channel_info;
+	}
+
+	if (hwmon->temp.count) {
+		channel_info = &hwmon->temp.channel_info;
+		channel_info->type = hwmon_temp;
+		channel_info->config = devm_kcalloc(hwmon->dev, hwmon->temp.count + 1,
+						    sizeof(u32), GFP_KERNEL);
+		if (!channel_info->config)
+			return -ENOMEM;
+
+		macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->temp);
+		hwmon->channel_infos[i++] = channel_info;
+	}
+
+	if (hwmon->volt.count) {
+		channel_info = &hwmon->volt.channel_info;
+		channel_info->type = hwmon_in;
+		channel_info->config = devm_kcalloc(hwmon->dev, hwmon->volt.count + 1,
+						    sizeof(u32), GFP_KERNEL);
+		if (!channel_info->config)
+			return -ENOMEM;
+
+		macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->volt);
+		hwmon->channel_infos[i++] = channel_info;
+	}
+
+	return 0;
+}
+
+static int macsmc_hwmon_probe(struct platform_device *pdev)
+{
+	struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
+	struct macsmc_hwmon *hwmon;
+	int ret;
+
+	/*
+	 * The MFD driver will try to probe us unconditionally. Some devices
+	 * with the SMC do not have hwmon capabilities. Only probe if we have
+	 * a hwmon node.
+	 */
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon),
+			     GFP_KERNEL);
+	if (!hwmon)
+		return -ENOMEM;
+
+	hwmon->dev = &pdev->dev;
+	hwmon->smc = smc;
+
+	ret = macsmc_hwmon_populate_sensors(hwmon, hwmon->dev->of_node);
+	if (ret) {
+		dev_err(hwmon->dev, "Could not parse sensors\n");
+		return ret;
+	}
+
+	if (!hwmon->curr.count && !hwmon->fan.count &&
+	    !hwmon->power.count && !hwmon->temp.count &&
+	    !hwmon->volt.count) {
+		dev_err(hwmon->dev,
+			"No valid sensors found of any supported type\n");
+		return -ENODEV;
+	}
+
+	ret = macsmc_hwmon_create_infos(hwmon);
+	if (ret)
+		return ret;
+
+	hwmon->chip_info.ops = &macsmc_hwmon_ops;
+	hwmon->chip_info.info =
+		(const struct hwmon_channel_info *const *)&hwmon->channel_infos;
+
+	hwmon->hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
+								"macsmc_hwmon", hwmon,
+								&hwmon->chip_info, NULL);
+	if (IS_ERR(hwmon->hwmon_dev))
+		return dev_err_probe(hwmon->dev, PTR_ERR(hwmon->hwmon_dev),
+				     "Probing SMC hwmon device failed\n");
+
+	dev_dbg(hwmon->dev, "Registered SMC hwmon device. Sensors:\n");
+	dev_dbg(hwmon->dev,
+		"Current: %d, Fans: %d, Power: %d, Temperature: %d, Voltage: %d",
+		hwmon->curr.count, hwmon->fan.count,
+		hwmon->power.count, hwmon->temp.count,
+		hwmon->volt.count);
+
+	return 0;
+}
+
+static const struct of_device_id macsmc_hwmon_of_table[] = {
+	{ .compatible = "apple,smc-hwmon" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, macsmc_hwmon_of_table);
+
+static struct platform_driver macsmc_hwmon_driver = {
+	.probe = macsmc_hwmon_probe,
+	.driver = {
+		.name = "macsmc-hwmon",
+		.of_match_table = macsmc_hwmon_of_table,
+	},
+};
+module_platform_driver(macsmc_hwmon_driver);
+
+MODULE_DESCRIPTION("Apple Silicon SMC hwmon driver");
+MODULE_AUTHOR("James Calligeros <jcalligeros99@gmail.com>");
+MODULE_LICENSE("Dual MIT/GPL");

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 07/11] mfd: macsmc: Wire up Apple SMC hwmon subdevice
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

Add the SMC hwmon functionality to the mfd device

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 drivers/mfd/macsmc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c
index 500395bb48da..51dd667d3b5f 100644
--- a/drivers/mfd/macsmc.c
+++ b/drivers/mfd/macsmc.c
@@ -46,6 +46,7 @@
 
 static const struct mfd_cell apple_smc_devs[] = {
 	MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"),
+	MFD_CELL_OF("macsmc-hwmon", NULL, NULL, 0, 0, "apple,smc-hwmon"),
 	MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"),
 	MFD_CELL_OF("macsmc-rtc", NULL, NULL, 0, 0, "apple,smc-rtc"),
 };

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 08/11] input: macsmc-input: New driver to handle the Apple Mac SMC buttons/lid
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc, Hector Martin
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

From: Hector Martin <marcan@marcan.st>

This driver implements power button and lid switch support for Apple Mac
devices using SMC controllers driven by the macsmc driver.

In addition to basic input support, this also responds to the final
shutdown warning (when the power button is held down long enough) by
doing an emergency kernel poweroff. This allows the NVMe controller to
be cleanly shut down, which prevents data loss for in-cache data.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Hector Martin <marcan@marcan.st>
Co-developed-by: Sven Peter <sven@kernel.org>
Signed-off-by: Sven Peter <sven@kernel.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 MAINTAINERS                       |   1 +
 drivers/input/misc/Kconfig        |  11 ++
 drivers/input/misc/Makefile       |   1 +
 drivers/input/misc/macsmc-input.c | 207 +++++++++++++++++++++++++
 4 files changed, 220 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fb55a167699e..48bdca6943f3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2455,6 +2455,7 @@ F:	drivers/hwmon/macsmc-hwmon.c
 F:	drivers/pmdomain/apple/
 F:	drivers/i2c/busses/i2c-pasemi-core.c
 F:	drivers/i2c/busses/i2c-pasemi-platform.c
+F:	drivers/input/misc/macsmc-input.c
 F:	drivers/input/touchscreen/apple_z2.c
 F:	drivers/iommu/apple-dart.c
 F:	drivers/iommu/io-pgtable-dart.c
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index cc2558630797..1144ba151dbf 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -992,4 +992,15 @@ config INPUT_STPMIC1_ONKEY
 	  To compile this driver as a module, choose M here: the
 	  module will be called stpmic1_onkey.
 
+config INPUT_MACSMC
+	tristate "Apple Mac SMC lid/buttons"
+	depends on MFD_MACSMC
+	help
+	  Say Y here if you want to use the input events delivered via the
+	  SMC controller on Apple Mac machines using the macsmc driver.
+	  This includes lid open/close and the power button.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called macsmc-input.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index f5ebfa9d9983..c6394105252f 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_INPUT_IQS7222)		+= iqs7222.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
 obj-$(CONFIG_INPUT_KXTJ9)		+= kxtj9.o
 obj-$(CONFIG_INPUT_M68K_BEEP)		+= m68kspkr.o
+obj-$(CONFIG_INPUT_MACSMC_INPUT)	+= macsmc-input.o
 obj-$(CONFIG_INPUT_MAX7360_ROTARY)	+= max7360-rotary.o
 obj-$(CONFIG_INPUT_MAX77650_ONKEY)	+= max77650-onkey.o
 obj-$(CONFIG_INPUT_MAX77693_HAPTIC)	+= max77693-haptic.o
diff --git a/drivers/input/misc/macsmc-input.c b/drivers/input/misc/macsmc-input.c
new file mode 100644
index 000000000000..f4598e65fc80
--- /dev/null
+++ b/drivers/input/misc/macsmc-input.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Apple SMC input event driver
+ * Copyright The Asahi Linux Contributors
+ *
+ * This driver exposes certain events from the SMC as an input device.
+ * This includes the lid open/close and power button notifications.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+
+/**
+ * struct macsmc_input
+ * @dev: Underlying struct device for the input sub-device
+ * @smc: Pointer to apple_smc struct of the mfd parent
+ * @input: Allocated input_dev; devres managed
+ * @nb: Notifier block used for incoming events from SMC (e.g. button pressed down)
+ * @wakeup_mode: Set to true when system is suspended and power button events should wake it
+ */
+struct macsmc_input {
+	struct device *dev;
+	struct apple_smc *smc;
+	struct input_dev *input;
+	struct notifier_block nb;
+	bool wakeup_mode;
+};
+
+#define SMC_EV_BTN 0x7201
+#define SMC_EV_LID 0x7203
+
+#define BTN_POWER		0x01 /* power button on e.g. Mac Mini chasis pressed */
+#define BTN_TOUCHID		0x06 /* combined TouchID / power button on MacBooks pressed */
+#define BTN_POWER_HELD_SHORT	0xfe /* power button briefly held down */
+#define BTN_POWER_HELD_LONG	0x00 /* power button held down; sent just before forced poweroff */
+
+static void macsmc_input_event_button(struct macsmc_input *smcin, unsigned long event)
+{
+	u8 button = (event >> 8) & 0xff;
+	u8 state = !!(event & 0xff);
+
+	switch (button) {
+	case BTN_POWER:
+	case BTN_TOUCHID:
+		pm_wakeup_dev_event(smcin->dev, 0, (smcin->wakeup_mode && state));
+
+		/* Suppress KEY_POWER event to prevent immediate shutdown on wake */
+		if (smcin->wakeup_mode)
+			return;
+
+		input_report_key(smcin->input, KEY_POWER, state);
+		input_sync(smcin->input);
+		break;
+	case BTN_POWER_HELD_SHORT: /* power button held down; ignore */
+		break;
+	case BTN_POWER_HELD_LONG:
+		/*
+		 * If we get here the power button has been held down for a while and
+		 * we have about 4 seconds before forced power-off is triggered by SMC.
+		 * Try to do an emergency shutdown to make sure the NVMe cache is
+		 * flushed. macOS actually does this by panicing (!)...
+		 */
+		if (state) {
+			dev_crit(smcin->dev, "Triggering forced shutdown!\n");
+			if (kernel_can_power_off())
+				kernel_power_off();
+			else /* Missing macsmc-reboot driver? */
+				kernel_restart("SMC power button triggered restart");
+		}
+		break;
+	default:
+		dev_warn(smcin->dev, "Unknown SMC button event: %04lx\n", event & 0xffff);
+	}
+}
+
+static void macsmc_input_event_lid(struct macsmc_input *smcin, unsigned long event)
+{
+	u8 lid_state = !!((event >> 8) & 0xff);
+
+	pm_wakeup_dev_event(smcin->dev, 0, (smcin->wakeup_mode && !lid_state));
+	input_report_switch(smcin->input, SW_LID, lid_state);
+	input_sync(smcin->input);
+}
+
+static int macsmc_input_event(struct notifier_block *nb, unsigned long event, void *data)
+{
+	struct macsmc_input *smcin = container_of(nb, struct macsmc_input, nb);
+	u16 type = event >> 16;
+
+	switch (type) {
+	case SMC_EV_BTN:
+		macsmc_input_event_button(smcin, event);
+		return NOTIFY_OK;
+	case SMC_EV_LID:
+		macsmc_input_event_lid(smcin, event);
+		return NOTIFY_OK;
+	default:
+		/* SMC event meant for another driver */
+		return NOTIFY_DONE;
+	}
+}
+
+static int macsmc_input_probe(struct platform_device *pdev)
+{
+	struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
+	struct macsmc_input *smcin;
+	bool have_lid, have_power;
+	int error;
+
+	/* Bail early if this SMC neither supports power button nor lid events */
+	have_lid = apple_smc_key_exists(smc, SMC_KEY(MSLD));
+	have_power = apple_smc_key_exists(smc, SMC_KEY(bHLD));
+	if (!have_lid && !have_power)
+		return -ENODEV;
+
+	smcin = devm_kzalloc(&pdev->dev, sizeof(*smcin), GFP_KERNEL);
+	if (!smcin)
+		return -ENOMEM;
+
+	smcin->dev = &pdev->dev;
+	smcin->smc = smc;
+	platform_set_drvdata(pdev, smcin);
+
+	smcin->input = devm_input_allocate_device(&pdev->dev);
+	if (!smcin->input)
+		return -ENOMEM;
+
+	smcin->input->phys = "macsmc-input (0)";
+	smcin->input->name = "Apple SMC power/lid events";
+
+	if (have_lid)
+		input_set_capability(smcin->input, EV_SW, SW_LID);
+	if (have_power)
+		input_set_capability(smcin->input, EV_KEY, KEY_POWER);
+
+	if (have_lid) {
+		u8 val;
+
+		error = apple_smc_read_u8(smc, SMC_KEY(MSLD), &val);
+		if (error < 0)
+			dev_warn(&pdev->dev, "Failed to read initial lid state\n");
+		else
+			input_report_switch(smcin->input, SW_LID, val);
+	}
+
+	if (have_power) {
+		u32 val;
+
+		error = apple_smc_read_u32(smc, SMC_KEY(bHLD), &val);
+		if (error < 0)
+			dev_warn(&pdev->dev, "Failed to read initial power button state\n");
+		else
+			input_report_key(smcin->input, KEY_POWER, val & 1);
+	}
+
+	error = input_register_device(smcin->input);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
+		return error;
+	}
+
+	input_sync(smcin->input);
+
+	smcin->nb.notifier_call = macsmc_input_event;
+	blocking_notifier_chain_register(&smc->event_handlers, &smcin->nb);
+
+	device_init_wakeup(&pdev->dev, true);
+
+	return 0;
+}
+
+static int macsmc_input_pm_prepare(struct device *dev)
+{
+	struct macsmc_input *smcin = dev_get_drvdata(dev);
+
+	smcin->wakeup_mode = true;
+	return 0;
+}
+
+static void macsmc_input_pm_complete(struct device *dev)
+{
+	struct macsmc_input *smcin = dev_get_drvdata(dev);
+
+	smcin->wakeup_mode = false;
+}
+
+static const struct dev_pm_ops macsmc_input_pm_ops = {
+	.prepare = macsmc_input_pm_prepare,
+	.complete = macsmc_input_pm_complete,
+};
+
+static struct platform_driver macsmc_input_driver = {
+	.driver = {
+		.name = "macsmc-input",
+		.pm = &macsmc_input_pm_ops,
+	},
+	.probe = macsmc_input_probe,
+};
+module_platform_driver(macsmc_input_driver);
+
+MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("Apple SMC input driver");
+MODULE_ALIAS("platform:macsmc-input");

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 09/11] mfd: macsmc: Wire up Apple SMC input subdevice
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

Add the new SMC input function to the mfd device

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 drivers/mfd/macsmc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c
index 51dd667d3b5f..3b69eb6d032a 100644
--- a/drivers/mfd/macsmc.c
+++ b/drivers/mfd/macsmc.c
@@ -45,6 +45,7 @@
 #define SMC_TIMEOUT_MS		500
 
 static const struct mfd_cell apple_smc_devs[] = {
+	MFD_CELL_NAME("macsmc-input"),
 	MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"),
 	MFD_CELL_OF("macsmc-hwmon", NULL, NULL, 0, 0, "apple,smc-hwmon"),
 	MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"),

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 10/11] arm64: dts: apple: t8103,t60xx,t8112: Add SMC RTC node
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

From: Sven Peter <sven@kernel.org>

The System Manager Controller of all M1/M2 SoCs supports the RTC
sub-device.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 .../arm64/boot/dts/apple/t600x-die0.dtsi | 6 ++++++
 .../arm64/boot/dts/apple/t602x-die0.dtsi | 6 ++++++
 arch/arm64/boot/dts/apple/t8103.dtsi     | 6 ++++++
 arch/arm64/boot/dts/apple/t8112.dtsi     | 6 ++++++
 4 files changed, 24 insertions(+)

diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi
index 3603b276a2ab..f715b19efd16 100644
--- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi
+++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi
@@ -44,6 +44,12 @@ smc_reboot: reboot {
 			nvmem-cell-names = "shutdown_flag", "boot_stage",
 				"boot_error_count", "panic_count";
 		};
+
+		rtc {
+			compatible = "apple,smc-rtc";
+			nvmem-cells = <&rtc_offset>;
+			nvmem-cell-names = "rtc_offset";
+		};
 	};
 
 	smc_mbox: mbox@290408000 {
diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi
index 2e7d2bf08ddc..8622ddea7b44 100644
--- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi
+++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi
@@ -121,6 +121,12 @@ smc_reboot: reboot {
 			nvmem-cell-names = "shutdown_flag", "boot_stage",
 				"boot_error_count", "panic_count";
 		};
+
+		rtc {
+			compatible = "apple,smc-rtc";
+			nvmem-cells = <&rtc_offset>;
+			nvmem-cell-names = "rtc_offset";
+		};
 	};
 
 	pinctrl_smc: pinctrl@2a2820000 {
diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi
index 8b7b27887968..59f2678639cf 100644
--- a/arch/arm64/boot/dts/apple/t8103.dtsi
+++ b/arch/arm64/boot/dts/apple/t8103.dtsi
@@ -916,6 +916,12 @@ smc_reboot: reboot {
 				nvmem-cell-names = "shutdown_flag", "boot_stage",
 					"boot_error_count", "panic_count";
 			};
+
+			rtc {
+				compatible = "apple,smc-rtc";
+				nvmem-cells = <&rtc_offset>;
+				nvmem-cell-names = "rtc_offset";
+			};
 		};
 
 		smc_mbox: mbox@23e408000 {
diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi
index 3f79878b25af..6bc3f58b06f7 100644
--- a/arch/arm64/boot/dts/apple/t8112.dtsi
+++ b/arch/arm64/boot/dts/apple/t8112.dtsi
@@ -919,6 +919,12 @@ smc_reboot: reboot {
 				nvmem-cell-names = "shutdown_flag", "boot_stage",
 					"boot_error_count", "panic_count";
 			};
+
+			rtc {
+				compatible = "apple,smc-rtc";
+				nvmem-cells = <&rtc_offset>;
+				nvmem-cell-names = "rtc_offset";
+			};
 		};
 
 		smc_mbox: mbox@23e408000 {

-- 
2.51.2


^ permalink raw reply related

* [PATCH v5 11/11] arm64: dts: apple: t8103, t8112, t60xx: Add hwmon SMC subdevice
From: James Calligeros @ 2025-11-12 11:16 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Alyssa Rosenzweig, Neal Gompa,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Alexandre Belloni, Jean Delvare, Guenter Roeck, Dmitry Torokhov,
	Jonathan Corbet, James Calligeros
  Cc: asahi, linux-arm-kernel, devicetree, linux-kernel, linux-rtc,
	linux-hwmon, linux-input, linux-doc
In-Reply-To: <20251112-macsmc-subdevs-v5-0-728e4b91fe81@gmail.com>

Apple's System Management Controller integrates numerous sensors
that can be exposed via hwmon. Add the subdevice, compatible,
and some common sensors that are exposed on every currently
supported device as a starting point.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Co-developed-by: Janne Grunau <j@jannau.net>
Signed-off-by: Janne Grunau <j@jannau.net>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 .../boot/dts/apple/hwmon-common.dtsi     | 33 +++++++++++++++++++++++++
 .../boot/dts/apple/hwmon-fan-dual.dtsi   | 22 +++++++++++++++++
 arch/arm64/boot/dts/apple/hwmon-fan.dtsi | 17 +++++++++++++
 .../boot/dts/apple/hwmon-laptop.dtsi     | 33 +++++++++++++++++++++++++
 .../boot/dts/apple/hwmon-mac-mini.dtsi   | 15 +++++++++++
 .../arm64/boot/dts/apple/t6001-j375c.dts |  2 ++
 arch/arm64/boot/dts/apple/t6001.dtsi     |  2 ++
 .../arm64/boot/dts/apple/t6002-j375d.dts |  2 ++
 .../arm64/boot/dts/apple/t600x-die0.dtsi |  4 +++
 .../boot/dts/apple/t600x-j314-j316.dtsi  |  3 +++
 .../arm64/boot/dts/apple/t602x-die0.dtsi |  4 +++
 arch/arm64/boot/dts/apple/t8103-j274.dts |  2 ++
 arch/arm64/boot/dts/apple/t8103-j293.dts |  3 +++
 arch/arm64/boot/dts/apple/t8103-j313.dts |  2 ++
 arch/arm64/boot/dts/apple/t8103-j456.dts |  2 ++
 arch/arm64/boot/dts/apple/t8103-j457.dts |  2 ++
 arch/arm64/boot/dts/apple/t8103.dtsi     |  5 ++++
 arch/arm64/boot/dts/apple/t8112-j413.dts |  2 ++
 arch/arm64/boot/dts/apple/t8112-j473.dts |  2 ++
 arch/arm64/boot/dts/apple/t8112-j493.dts |  3 +++
 arch/arm64/boot/dts/apple/t8112.dtsi     |  5 ++++
 21 files changed, 165 insertions(+)

diff --git a/arch/arm64/boot/dts/apple/hwmon-common.dtsi b/arch/arm64/boot/dts/apple/hwmon-common.dtsi
new file mode 100644
index 000000000000..b87021855fdf
--- /dev/null
+++ b/arch/arm64/boot/dts/apple/hwmon-common.dtsi
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/*
+ * Hardware monitoring sensors expected to be found on all Apple Silicon devices
+ *
+ * Copyright The Asahi Linux Contributors
+ */
+
+&smc_hwmon {
+	current-ID0R {
+		apple,key-id = "ID0R";
+		label = "AC Input Current";
+	};
+	power-PSTR {
+		apple,key-id = "PSTR";
+		label = "Total System Power";
+	};
+	power-PDTR {
+		apple,key-id = "PDTR";
+		label = "AC Input Power";
+	};
+	power-PMVR {
+		apple,key-id = "PMVR";
+		label = "3.8 V Rail Power";
+	};
+	temperature-TH0x {
+		apple,key-id = "TH0x";
+		label = "NAND Flash Temperature";
+	};
+	voltage-VD0R {
+		apple,key-id = "VD0R";
+		label = "AC Input Voltage";
+	};
+};
diff --git a/arch/arm64/boot/dts/apple/hwmon-fan-dual.dtsi b/arch/arm64/boot/dts/apple/hwmon-fan-dual.dtsi
new file mode 100644
index 000000000000..3eef0721bcca
--- /dev/null
+++ b/arch/arm64/boot/dts/apple/hwmon-fan-dual.dtsi
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/*
+ * SMC hwmon fan keys for Apple Silicon desktops/laptops with two fans
+ *
+ * Copyright The Asahi Linux Contributors
+ */
+
+#include "hwmon-fan.dtsi"
+
+&smc_hwmon {
+	fan-F0Ac {
+		label = "Fan 1";
+	};
+	fan-F1Ac {
+		apple,key-id = "F1Ac";
+		label = "Fan 2";
+		apple,fan-minimum = "F1Mn";
+		apple,fan-maximum = "F1Mx";
+		apple,fan-target = "F1Tg";
+		apple,fan-mode = "F1Md";
+	};
+};
diff --git a/arch/arm64/boot/dts/apple/hwmon-fan.dtsi b/arch/arm64/boot/dts/apple/hwmon-fan.dtsi
new file mode 100644
index 000000000000..fba9faf38f4b
--- /dev/null
+++ b/arch/arm64/boot/dts/apple/hwmon-fan.dtsi
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/*
+ * hwmon fan keys for Apple Silicon desktops/laptops with a single fan.
+ *
+ *  Copyright The Asahi Linux Contributors
+ */
+
+&smc_hwmon {
+	fan-F0Ac {
+		apple,key-id = "F0Ac";
+		label = "Fan";
+		apple,fan-minimum = "F0Mn";
+		apple,fan-maximum = "F0Mx";
+		apple,fan-target = "F0Tg";
+		apple,fan-mode = "F0Md";
+	};
+};
diff --git a/arch/arm64/boot/dts/apple/hwmon-laptop.dtsi b/arch/arm64/boot/dts/apple/hwmon-laptop.dtsi
new file mode 100644
index 000000000000..0c4666282a5c
--- /dev/null
+++ b/arch/arm64/boot/dts/apple/hwmon-laptop.dtsi
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/*
+ * Hardware monitoring sensors expected on all Apple Silicon laptops
+ *
+ * Copyright The Asahi Linux Contributors
+ */
+
+&smc_hwmon {
+	power-PHPC {
+		apple,key-id = "PHPC";
+		label = "Heatpipe Power";
+	};
+	temperature-TB0T {
+		apple,key-id = "TB0T";
+		label = "Battery Hotspot Temperature";
+	};
+	temperature-TCHP {
+		apple,key-id = "TCHP";
+		label = "Charge Regulator Temperature";
+	};
+	temperature-TW0P {
+		apple,key-id = "TW0P";
+		label = "WiFi/BT Module Temperature";
+	};
+	voltage-SBAV {
+		apple,key-id = "SBAV";
+		label = "Battery Voltage";
+	};
+	voltage-VD0R {
+		apple,key-id = "VD0R";
+		label = "Charger Input Voltage";
+	};
+};
diff --git a/arch/arm64/boot/dts/apple/hwmon-mac-mini.dtsi b/arch/arm64/boot/dts/apple/hwmon-mac-mini.dtsi
new file mode 100644
index 000000000000..f32627336ae7
--- /dev/null
+++ b/arch/arm64/boot/dts/apple/hwmon-mac-mini.dtsi
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/*
+ * hwmon sensors expected on all Mac mini models
+ *
+ * Copyright The Asahi Linux Contributors
+ */
+
+#include "hwmon-fan.dtsi"
+
+&smc_hwmon {
+	temperature-TW0P {
+		apple,key-id = "TW0P";
+		label = "WiFi/BT Module Temperature";
+	};
+};
diff --git a/arch/arm64/boot/dts/apple/t6001-j375c.dts b/arch/arm64/boot/dts/apple/t6001-j375c.dts
index 2e7c23714d4d..08276114c1d8 100644
--- a/arch/arm64/boot/dts/apple/t6001-j375c.dts
+++ b/arch/arm64/boot/dts/apple/t6001-j375c.dts
@@ -24,3 +24,5 @@ &wifi0 {
 &bluetooth0 {
 	brcm,board-type = "apple,okinawa";
 };
+
+#include "hwmon-fan-dual.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t6001.dtsi b/arch/arm64/boot/dts/apple/t6001.dtsi
index ffbe823b71bc..264df90f07d8 100644
--- a/arch/arm64/boot/dts/apple/t6001.dtsi
+++ b/arch/arm64/boot/dts/apple/t6001.dtsi
@@ -66,3 +66,5 @@ p-core-pmu-affinity {
 &gpu {
 	compatible = "apple,agx-g13c", "apple,agx-g13s";
 };
+
+#include "hwmon-common.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t6002-j375d.dts b/arch/arm64/boot/dts/apple/t6002-j375d.dts
index 2b7f80119618..d12c0ae418f7 100644
--- a/arch/arm64/boot/dts/apple/t6002-j375d.dts
+++ b/arch/arm64/boot/dts/apple/t6002-j375d.dts
@@ -56,3 +56,5 @@ &bluetooth0 {
 
 /delete-node/ &ps_disp0_cpu0_die1;
 /delete-node/ &ps_disp0_fe_die1;
+
+#include "hwmon-fan-dual.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi
index f715b19efd16..e6647c1a9173 100644
--- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi
+++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi
@@ -37,6 +37,10 @@ smc_gpio: gpio {
 			#gpio-cells = <2>;
 		};
 
+		smc_hwmon: hwmon {
+			compatible = "apple,smc-hwmon";
+		};
+
 		smc_reboot: reboot {
 			compatible = "apple,smc-reboot";
 			nvmem-cells = <&shutdown_flag>, <&boot_stage>,
diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi
index c0aac59a6fae..127814a9dfa4 100644
--- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi
+++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi
@@ -131,3 +131,6 @@ &fpwm0 {
 };
 
 #include "spi1-nvram.dtsi"
+
+#include "hwmon-laptop.dtsi"
+#include "hwmon-fan-dual.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi
index 8622ddea7b44..680c103c1c0f 100644
--- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi
+++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi
@@ -114,6 +114,10 @@ smc_gpio: gpio {
 			#gpio-cells = <2>;
 		};
 
+		smc_hwmon: hwmon {
+			compatible = "apple,smc-hwmon";
+		};
+
 		smc_reboot: reboot {
 			compatible = "apple,smc-reboot";
 			nvmem-cells = <&shutdown_flag>, <&boot_stage>,
diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts
index 1c3e37f86d46..f5b8cc087882 100644
--- a/arch/arm64/boot/dts/apple/t8103-j274.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j274.dts
@@ -61,3 +61,5 @@ &pcie0_dart_2 {
 &i2c2 {
 	status = "okay";
 };
+
+#include "hwmon-mac-mini.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts
index 5b3c42e9f0e6..abb88391635f 100644
--- a/arch/arm64/boot/dts/apple/t8103-j293.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j293.dts
@@ -119,3 +119,6 @@ dfr_panel_in: endpoint {
 &displaydfr_dart {
 	status = "okay";
 };
+
+#include "hwmon-laptop.dtsi"
+#include "hwmon-fan.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts
index 97a4344d8dca..491ead016b21 100644
--- a/arch/arm64/boot/dts/apple/t8103-j313.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j313.dts
@@ -41,3 +41,5 @@ &wifi0 {
 &fpwm1 {
 	status = "okay";
 };
+
+#include "hwmon-laptop.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts
index 58c8e43789b4..c2ec6fbb633c 100644
--- a/arch/arm64/boot/dts/apple/t8103-j456.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j456.dts
@@ -75,3 +75,5 @@ &pcie0_dart_1 {
 &pcie0_dart_2 {
 	status = "okay";
 };
+
+#include "hwmon-fan-dual.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts
index 7089ccf3ce55..aeaab2482d54 100644
--- a/arch/arm64/boot/dts/apple/t8103-j457.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j457.dts
@@ -56,3 +56,5 @@ ethernet0: ethernet@0,0 {
 &pcie0_dart_2 {
 	status = "okay";
 };
+
+#include "hwmon-fan.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi
index 59f2678639cf..f1820bdc0910 100644
--- a/arch/arm64/boot/dts/apple/t8103.dtsi
+++ b/arch/arm64/boot/dts/apple/t8103.dtsi
@@ -909,6 +909,10 @@ smc_gpio: gpio {
 				#gpio-cells = <2>;
 			};
 
+			smc_hwmon: hwmon {
+				compatible = "apple,smc-hwmon";
+			};
+
 			smc_reboot: reboot {
 				compatible = "apple,smc-reboot";
 				nvmem-cells = <&shutdown_flag>, <&boot_stage>,
@@ -1141,3 +1145,4 @@ port02: pci@2,0 {
 };
 
 #include "t8103-pmgr.dtsi"
+#include "hwmon-common.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts
index 6f69658623bf..500dcdf2d4b5 100644
--- a/arch/arm64/boot/dts/apple/t8112-j413.dts
+++ b/arch/arm64/boot/dts/apple/t8112-j413.dts
@@ -78,3 +78,5 @@ &i2c4 {
 &fpwm1 {
 	status = "okay";
 };
+
+#include "hwmon-laptop.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts
index 06fe257f08be..11db6a92493f 100644
--- a/arch/arm64/boot/dts/apple/t8112-j473.dts
+++ b/arch/arm64/boot/dts/apple/t8112-j473.dts
@@ -52,3 +52,5 @@ &pcie1_dart {
 &pcie2_dart {
 	status = "okay";
 };
+
+#include "hwmon-mac-mini.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts
index fb8ad7d4c65a..a0da02c00f15 100644
--- a/arch/arm64/boot/dts/apple/t8112-j493.dts
+++ b/arch/arm64/boot/dts/apple/t8112-j493.dts
@@ -133,3 +133,6 @@ touchbar0: touchbar@0 {
 		touchscreen-inverted-y;
 	};
 };
+
+#include "hwmon-laptop.dtsi"
+#include "hwmon-fan.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi
index 6bc3f58b06f7..c4d1e5ffaee9 100644
--- a/arch/arm64/boot/dts/apple/t8112.dtsi
+++ b/arch/arm64/boot/dts/apple/t8112.dtsi
@@ -912,6 +912,10 @@ smc_gpio: gpio {
 				#gpio-cells = <2>;
 			};
 
+			smc_hwmon: hwmon {
+				compatible = "apple,smc-hwmon";
+			};
+
 			smc_reboot: reboot {
 				compatible = "apple,smc-reboot";
 				nvmem-cells = <&shutdown_flag>, <&boot_stage>,
@@ -1180,3 +1184,4 @@ port03: pci@3,0 {
 };
 
 #include "t8112-pmgr.dtsi"
+#include "hwmon-common.dtsi"

-- 
2.51.2


^ permalink raw reply related

* Re: [PATCH v2 02/11] dt-bindings: display: panel: ronbo,rb070d30: panel-common ref
From: Josua Mayer @ 2025-11-12 11:43 UTC (permalink / raw)
  To: Frank Li
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
	Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	Dmitry Torokhov, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Laurent Pinchart, Lad Prabhakar, Thierry Reding,
	Jon Nettleton, Mikhail Anikin, Yazan Shhady,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-input@vger.kernel.org,
	imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org
In-Reply-To: <aQ4V+QW8l4X5yEV2@lizhi-Precision-Tower-5810>

Am 07.11.25 um 16:53 schrieb Frank Li:
> On Fri, Nov 07, 2025 at 12:46:09PM +0100, Josua Mayer wrote:
>> Add missing ref on panel-common.yaml for this dsi panel so that common
>> properties can be shared.
>>
>> Drop reset-gpios and backlight as they are already in panel-common.
>>
>> Switch from additionalProperties to unevaluatedProperties so that common
>> panel properties are available without repeating them in this binding.
>>
>> Notably panel-common defines the "port" property for linking panels to a
>> source - which was missing from this panel. Mark it as required.
>>
>> Signed-off-by: Josua Mayer <josua@solid-run.com>
>> ---
>>  .../devicetree/bindings/display/panel/ronbo,rb070d30.yaml  | 14 +++++---------
>>  1 file changed, 5 insertions(+), 9 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml b/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml
>> index 04f86e0cbac91..6940373015833 100644
>> --- a/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml
>> +++ b/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml
>> @@ -9,6 +9,9 @@ title: Ronbo RB070D30 DSI Display Panel
>>  maintainers:
>>    - Maxime Ripard <mripard@kernel.org>
>>
>> +allOf:
>> +  - $ref: panel-common.yaml#
>> +
> Can you move allof after required incase add if-else branch later.

Do you mean at the very end just before unevaluatedProperties?

I have no strong opinion there but most panel bindings reference panel-common
at the very beginning, after maintainers or description.

>
> Frank
>>  properties:
>>    compatible:
>>      const: ronbo,rb070d30
>> @@ -20,10 +23,6 @@ properties:
>>      description: GPIO used for the power pin
>>      maxItems: 1
>>
>> -  reset-gpios:
>> -    description: GPIO used for the reset pin
>> -    maxItems: 1
>> -
>>    shlr-gpios:
>>      description: GPIO used for the shlr pin (horizontal flip)
>>      maxItems: 1
>> @@ -35,10 +34,6 @@ properties:
>>    vcc-lcd-supply:
>>      description: Power regulator
>>
>> -  backlight:
>> -    description: Backlight used by the panel
>> -    $ref: /schemas/types.yaml#/definitions/phandle
>> -
>>  required:
>>    - compatible
>>    - power-gpios
>> @@ -47,5 +42,6 @@ required:
>>    - shlr-gpios
>>    - updn-gpios
>>    - vcc-lcd-supply
>> +  - port
>>
>> -additionalProperties: false
>> +unevaluatedProperties: false
>>
>> --
>> 2.51.0
>>

^ permalink raw reply

* Re: [PATCH v2 02/11] dt-bindings: display: panel: ronbo,rb070d30: panel-common ref
From: Josua Mayer @ 2025-11-12 11:51 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
	Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	Dmitry Torokhov, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Laurent Pinchart, Lad Prabhakar, Thierry Reding,
	Jon Nettleton, Mikhail Anikin, Yazan Shhady,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-input@vger.kernel.org,
	imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20251107-cornbread-juggling-2e7e69bcac10@spud>

Hi Conor,

Am 07.11.25 um 18:48 schrieb Conor Dooley:
> On Fri, Nov 07, 2025 at 12:46:09PM +0100, Josua Mayer wrote:
>> Add missing ref on panel-common.yaml for this dsi panel so that common
>> properties can be shared.
>>
>> Drop reset-gpios and backlight as they are already in panel-common.
>>
>> Switch from additionalProperties to unevaluatedProperties so that common
>> panel properties are available without repeating them in this binding.
>>
>> Notably panel-common defines the "port" property for linking panels to a
>> source - which was missing from this panel. Mark it as required.
>>
>> Signed-off-by: Josua Mayer <josua@solid-run.com>
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
Is your ack compatible with Frank Li requesting to move the
ref on panel-common further towards the end of the file?
> pw-bot: not-applicable

^ permalink raw reply

* [PATCH v3 1/2] dt-bindings: input: i2c-hid: Introduce FocalTech FT8112
From: daniel_peng @ 2025-11-12 11:59 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input
  Cc: LKML, Daniel Peng, Conor Dooley, Krzysztof Kozlowski, Rob Herring,
	devicetree

From: Daniel Peng <Daniel_Peng@pegatron.corp-partner.google.com>

The FocalTech FT8112 touch screen chip same as Ilitek ili2901 controller
has a reset gpio. The difference is that they have different
post_gpio_reset_on_delay_ms.
FocalTech FT8112 also uses 3.3V power supply.

Signed-off-by: Daniel Peng <Daniel_Peng@pegatron.corp-partner.google.com>
- Modified the subject description.
- Modified maintainers to myself of this binding file.
- Fixed the extra '>' on section of "interrupts = <15 IRQ_TYPE_LEVEL_LOW>;" and confirm command 'make dt_binding_check' correctly.
- Restored MAINTAINERS file.

---

(no changes since v1)

 .../bindings/input/focaltech,ft8112.yaml      | 66 +++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/focaltech,ft8112.yaml

diff --git a/Documentation/devicetree/bindings/input/focaltech,ft8112.yaml b/Documentation/devicetree/bindings/input/focaltech,ft8112.yaml
new file mode 100644
index 000000000000..197f30b14d45
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/focaltech,ft8112.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/focaltech,ft8112.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: FocalTech FT8112 touchscreen controller
+
+maintainers:
+  - Daniel Peng <Daniel_Peng@pegatron.corp-partner.google.com>
+
+description:
+  Supports the FocalTech FT8112 touchscreen controller.
+  This touchscreen controller uses the i2c-hid protocol with a reset GPIO.
+
+allOf:
+  - $ref: /schemas/input/touchscreen/touchscreen.yaml#
+
+properties:
+  compatible:
+    enum:
+      - focaltech,ft8112
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  panel: true
+
+  reset-gpios:
+    maxItems: 1
+
+  vcc33-supply: true
+
+  vccio-supply: true
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - vcc33-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      touchscreen@38 {
+        compatible = "focaltech,ft8112";
+        reg = <0x38>;
+
+        interrupt-parent = <&pio>;
+        interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
+
+        reset-gpios = <&pio 126 GPIO_ACTIVE_LOW>;
+        vcc33-supply = <&pp3300_tchscr_x>;
+      };
+    };
-- 
2.34.1


^ permalink raw reply related

* [PATCH v3 2/2] HID: i2c-hid: Add FocalTech FT8112
From: daniel_peng @ 2025-11-12 11:59 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input
  Cc: LKML, Daniel Peng, Benjamin Tissoires, Douglas Anderson,
	Jiri Kosina, Pin-yen Lin
In-Reply-To: <20251112195751.v3.1.I894dde5015f4acad94cb5bada61e5811c5142395@changeid>

From: Daniel Peng <Daniel_Peng@pegatron.corp-partner.google.com>

Information for touchscreen model HKO/RB116AS01-2 as below:
- HID :FTSC1000
- slave address:0X38
- Interface:HID over I2C
- Touch control lC:FT8112
- I2C ID: PNP0C50

Signed-off-by: Daniel Peng <Daniel_Peng@pegatron.corp-partner.google.com>

- Modified the subject to include the ID of the chip as suggestion only.

---

(no changes since v1)

 drivers/hid/i2c-hid/i2c-hid-of-elan.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c
index 0215f217f6d8..b81fcc6ff49e 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c
@@ -168,6 +168,13 @@ static const struct elan_i2c_hid_chip_data elan_ekth6a12nay_chip_data = {
 	.power_after_backlight = true,
 };
 
+static const struct elan_i2c_hid_chip_data focaltech_ft8112_chip_data = {
+	.post_power_delay_ms = 10,
+	.post_gpio_reset_on_delay_ms = 150,
+	.hid_descriptor_address = 0x0001,
+	.main_supply_name = "vcc33",
+};
+
 static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = {
 	.post_power_delay_ms = 1,
 	.post_gpio_reset_on_delay_ms = 200,
@@ -191,6 +198,7 @@ static const struct elan_i2c_hid_chip_data ilitek_ili2901_chip_data = {
 static const struct of_device_id elan_i2c_hid_of_match[] = {
 	{ .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data },
 	{ .compatible = "elan,ekth6a12nay", .data = &elan_ekth6a12nay_chip_data },
+	{ .compatible = "focaltech,ft8112", .data = &focaltech_ft8112_chip_data },
 	{ .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data },
 	{ .compatible = "ilitek,ili2901", .data = &ilitek_ili2901_chip_data },
 	{ }
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH v2 00/11] arm64: dts: add description for solidrun imx8mp hummingboard-iiot
From: Josua Mayer @ 2025-11-12 12:00 UTC (permalink / raw)
  To: Rob Herring (Arm)
  Cc: Sascha Hauer, Lad Prabhakar, Thierry Reding,
	dri-devel@lists.freedesktop.org,
	linux-arm-kernel@lists.infradead.org, linux-input@vger.kernel.org,
	Mikhail Anikin, Fabio Estevam, imx@lists.linux.dev, Conor Dooley,
	David Airlie, Yazan Shhady, Shawn Guo, Jessica Zhang,
	Maarten Lankhorst, Simona Vetter, Neil Armstrong, Jon Nettleton,
	devicetree@vger.kernel.org, Krzysztof Kozlowski, Laurent Pinchart,
	Maxime Ripard, Dmitry Torokhov, Thomas Zimmermann,
	Pengutronix Kernel Team, linux-kernel@vger.kernel.org,
	Krzysztof Kozlowski
In-Reply-To: <176278493126.154536.17870405687578843628.robh@kernel.org>


Am 10.11.25 um 15:34 schrieb Rob Herring (Arm):
> On Fri, 07 Nov 2025 12:46:07 +0100, Josua Mayer wrote:
>> This patchset mainl adds description for 3 SolidRun boards:
>> - i.MX8MP Hummingboard IIoT
>> - SolidSense N8 Compact
>> - i.MX8MM Hummingboard Ripple
>>
>> This includes dt bindings and a range of bug-fixes:
>>
>> - dt bindings for the boards
>> - fix to dsi panel bindings referncing panel-common and adding port
>>   property
>> - cosmetic fix to some  solidrun imx8mp boards for regulator gpio
>>    polarity
>> - fix dsi hdmi bridge on hummingboard pulse
>> - compile dtbs with symbols to support overlays
>> - gpiod_set_value _can_sleep conversion for panel and touchscreen
>>    drivers
>>
>> Open Questions:
>>
>> - How to describe HX3 USB-2.0/3.0 Hub placed on a USB-2.0-only Bus
>>   (affects imx8mm-hummingboard-ripple.dts)
>> - Is "description for" implied not only on dt-bindings patches, but also
>>   dts? E.g. is this commit subject acceptable?:
>>   "arm64: dts: add solidrun solidsense-n8 board"
>>
>> Signed-off-by: Josua Mayer <josua@solid-run.com>
>> ---
>> Changes in v2:
>> - fix spelling mistakes in commit descriptions.
>> - remove redundant "binding for" from subject:
>>   https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst
>>   (Reported-by: Krzysztof Kozlowski <krzk@kernel.org>)
>> - remove one useless comment from imx8mm-sr-som.dtsi to fix long line
>>   warning.
>> - change ronbo panel binding to inherit panel-common and switch
>>   additionalProperties to unevaluatedProperties.
>>   (Reported-by: Krzysztof Kozlowski <krzk@kernel.org>)
>> - add dt binding for winstar lvds panel
>> - fix dtbs_check for dsi & lvds panel addons
>> - change n8 board dts comment-style in header
>> - collected ack on solidrun boards bindings patch (patch 1 in the series)
>>   (Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>)
>> - added standard led label property to hb-iiot multi-purpose leds
>>   ensuring consistent names in case lp5562 driver starts using it.
>> - Link to v1: https://lore.kernel.org/r/20251027-imx8mp-hb-iiot-v1-0-683f86357818@solid-run.com
>>
>> ---
>> Josua Mayer (11):
>>       dt-bindings: arm: fsl: Add various solidrun i.mx8m boards
>>       dt-bindings: display: panel: ronbo,rb070d30: panel-common ref
>>       dt-bindings: panel: lvds: add Winstar WF70A8SYJHLNGA
>>       Input: ilitek_ts_i2c: fix warning with gpio controllers that sleep
>>       drm/panel: ronbo-rb070d30: fix warning with gpio controllers that sleep
>>       arm64: dts: imx8mp-hummingboard-pulse/cubox-m: fix vmmc gpio polarity
>>       arm64: dts: imx8mp-hummingboard-pulse: fix mini-hdmi dsi port reference
>>       arm64: dts: imx8mp-sr-som: build dtbs with symbols for overlay support
>>       arm64: dts: add description for solidrun imx8mp hummingboard-iiot
>>       arm64: dts: add description for solidrun solidsense-n8 board
>>       arm64: dts: add description for solidrun i.mx8mm som and evb
>>
>>  Documentation/devicetree/bindings/arm/fsl.yaml     |   9 +
>>  .../bindings/display/panel/panel-lvds.yaml         |   2 +
>>  .../bindings/display/panel/ronbo,rb070d30.yaml     |  14 +-
>>  arch/arm64/boot/dts/freescale/Makefile             |  15 +
>>  .../dts/freescale/imx8mm-hummingboard-ripple.dts   | 335 ++++++++
>>  arch/arm64/boot/dts/freescale/imx8mm-sr-som.dtsi   | 395 ++++++++++
>>  .../dts/freescale/imx8mn-solidsense-n8-compact.dts | 853 +++++++++++++++++++++
>>  arch/arm64/boot/dts/freescale/imx8mp-cubox-m.dts   |   2 +-
>>  ...hummingboard-iiot-panel-dsi-WJ70N3TYJHMNG0.dtso |  69 ++
>>  ...ummingboard-iiot-panel-lvds-WF70A8SYJHLNGA.dtso | 105 +++
>>  .../imx8mp-hummingboard-iiot-rs485-a.dtso          |  18 +
>>  .../imx8mp-hummingboard-iiot-rs485-b.dtso          |  18 +
>>  .../dts/freescale/imx8mp-hummingboard-iiot.dts     | 712 +++++++++++++++++
>>  .../imx8mp-hummingboard-pulse-common.dtsi          |   2 +-
>>  .../imx8mp-hummingboard-pulse-mini-hdmi.dtsi       |  11 +-
>>  drivers/gpu/drm/panel/panel-ronbo-rb070d30.c       |   8 +-
>>  drivers/input/touchscreen/ilitek_ts_i2c.c          |   4 +-
>>  17 files changed, 2548 insertions(+), 24 deletions(-)
>> ---
>> base-commit: 607bcb1e539a7f2b2ba6832ba96fe3ad73b7401c
>> change-id: 20251026-imx8mp-hb-iiot-525b03beea62
>>
>> Best regards,
>> --
>> Josua Mayer <josua@solid-run.com>
>>
>>
>>
>
> My bot found new DTB warnings on the .dts files added or changed in this
> series.
>
> Some warnings may be from an existing SoC .dtsi. Or perhaps the warnings
> are fixed by another series. Ultimately, it is up to the platform
> maintainer whether these warnings are acceptable or not. No need to reply
> unless the platform maintainer has comments.
>
> If you already ran DT checks and didn't see these error(s), then
> make sure dt-schema is up to date:
>
>   pip3 install dtschema --upgrade
>
>
> This patch series was applied (using b4) to base:
>  Base: base-commit 607bcb1e539a7f2b2ba6832ba96fe3ad73b7401c not known, ignoring
>  Base: attempting to guess base-commit...
>  Base: tags/v6.18-rc1 (exact match)
>  Base: tags/v6.18-rc1 (use --merge-base to override)
>
> If this is not the correct base, please add 'base-commit' tag
> (or use b4 which does this automatically)
>
> New warnings running 'make CHECK_DTBS=y for arch/arm64/boot/dts/freescale/' for 20251107-imx8mp-hb-iiot-v2-0-d8233ded999e@solid-run.com:
>
> arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot.dtb: accelerometer@53 (adi,adxl345): 'interrupt-names' is a dependency of 'interrupts-extended'
> 	from schema $id: http://devicetree.org/schemas/iio/accel/adi,adxl345.yaml
Ack. Adding missing interrupt-name in v3 ...

^ permalink raw reply

* Re: [PATCH v8 00/10] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling
From: Antheas Kapenekakis @ 2025-11-12 12:44 UTC (permalink / raw)
  To: platform-driver-x86, linux-input
  Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
	Luke D . Jones, Ilpo Järvinen, Denis Benato, Hans de Goede
In-Reply-To: <20251101104712.8011-1-lkml@antheas.dev>

On Sat, 1 Nov 2025 at 11:47, Antheas Kapenekakis <lkml@antheas.dev> wrote:
>
> This is a two part series which does the following:
>   - Clean-up init sequence
>   - Unify backlight handling to happen under asus-wmi so that all Aura
>     devices have synced brightness controls and the backlight button works
>     properly when it is on a USB laptop keyboard instead of one w/ WMI.
>
> For more context, see cover letter of V1. Since V5, I removed some patches
> to make this easier to merge.

Small bump for this.

Unrelated but I was b4ing this series on Ubuntu 24 and got BADSIG:
DKIM/antheas.dev. Is there a reference for fixing this on my host?
Perhaps it would help with spam

Antheas

> ---
> V7: https://lore.kernel.org/all/20251018101759.4089-1-lkml@antheas.dev/
> V6: https://lore.kernel.org/all/20251013201535.6737-1-lkml@antheas.dev/
> V5: https://lore.kernel.org/all/20250325184601.10990-1-lkml@antheas.dev/
> V4: https://lore.kernel.org/lkml/20250324210151.6042-1-lkml@antheas.dev/
> V3: https://lore.kernel.org/lkml/20250322102804.418000-1-lkml@antheas.dev/
> V2: https://lore.kernel.org/all/20250320220924.5023-1-lkml@antheas.dev/
> V1: https://lore.kernel.org/all/20250319191320.10092-1-lkml@antheas.dev/
>
> Changes since V7:
>   - Readd legacy init quirk for Dennis
>   - Remove HID_QUIRK_INPUT_PER_APP as a courtesy to asusctl
>   - Fix warning due to enum_backlight receiving negative values
>
> Changes since V6:
>   - Split initialization refactor into three patches, update commit text
>     to be clearer in what it does
>   - Replace spinlock accesses with guard and scoped guard in all patches
>   - Add missing includes mentioned by Ilpo
>   - Reflow, tweak comment in prevent binding to all HID devices on ROG
>   - Replace asus_ref.asus with local reference in all patches
>   - Add missing kernel doc comments
>   - Other minor nits from Ilpo
>   - User reported warning due to scheduling work while holding a spinlock.
>     Restructure patch for multiple handlers to limit when spinlock is held to
>     variable access only. In parallel, setup a workqueue to handle registration
>     of led device and setting brightness. This is required as registering the
>     led device triggers kbd_led_get which needs to hold the spinlock to
>     protect the led_wk value. The workqueue is also required for the hid
>     event passthrough to avoid scheduling work while holding the spinlock.
>     Apply the workqueue to wmi brightness buttons as well, as that was
>     omitted before this series and WMI access was performed.
>   - On "HID: asus: prevent binding to all HID devices on ROG", rename
>     quirk HANDLE_GENERIC to SKIP_REPORT_FIXUP and only skip report fixup.
>     This allows other quirks to apply (applies quirk that fixes keyboard
>     being named as a pointer device).
>
> Changes since V5:
>   - It's been a long time
>   - Remove addition of RGB as that had some comments I need to work on
>   - Remove folio patch (already merged)
>   - Remove legacy fix patch 11 from V4. There is a small chance that
>     without this patch, some old NKEY keyboards might not respond to
>     RGB commands according to Luke, but the kernel driver does not do
>     RGB currently. The 0x5d init is done by Armoury crate software in
>     Windows. If an issue is found, we can re-add it or just remove patches
>     1/2 before merging. However, init could use the cleanup.
>
> Changes since V4:
>   - Fix KConfig (reported by kernel robot)
>   - Fix Ilpo's nits, if I missed anything lmk
>
> Changes since V3:
>   - Add initializer for 0x5d for old NKEY keyboards until it is verified
>     that it is not needed for their media keys to function.
>   - Cover init in asus-wmi with spinlock as per Hans
>   - If asus-wmi registers WMI handler with brightness, init the brightness
>     in USB Asus keyboards, per Hans.
>   - Change hid handler name to asus-UNIQ:rgb:peripheral to match led class
>   - Fix oops when unregistering asus-wmi by moving unregister outside of
>     the spin lock (but after the asus reference is set to null)
>
> Changes since V2:
>   - Check lazy init succeds in asus-wmi before setting register variable
>   - make explicit check in asus_hid_register_listener for listener existing
>     to avoid re-init
>   - rename asus_brt to asus_hid in most places and harmonize everything
>   - switch to a spinlock instead of a mutex to avoid kernel ooops
>   - fixup hid device quirks to avoid multiple RGB devices while still exposing
>     all input vendor devices. This includes moving rgb init to probe
>     instead of the input_configured callbacks.
>   - Remove fan key (during retest it appears to be 0xae that is already
>     supported by hid-asus)
>   - Never unregister asus::kbd_backlight while asus-wmi is active, as that
>   - removes fds from userspace and breaks backlight functionality. All
>   - current mainline drivers do not support backlight hotplugging, so most
>     userspace software (e.g., KDE, UPower) is built with that assumption.
>     For the Ally, since it disconnects its controller during sleep, this
>     caused the backlight slider to not work in KDE.
>
> Changes since V1:
>   - Add basic RGB support on hid-asus, (Z13/Ally) tested in KDE/Z13
>   - Fix ifdef else having an invalid signature (reported by kernel robot)
>   - Restore input arguments to init and keyboard function so they can
>     be re-used for RGB controls.
>   - Remove Z13 delay (it did not work to fix the touchpad) and replace it
>     with a HID_GROUP_GENERIC quirk to allow hid-multitouch to load. Squash
>     keyboard rename into it.
>   - Unregister brightness listener before removing work queue to avoid
>     a race condition causing corruption
>   - Remove spurious mutex unlock in asus_brt_event
>   - Place mutex lock in kbd_led_set after LED_UNREGISTERING check to avoid
>     relocking the mutex and causing a deadlock when unregistering leds
>   - Add extra check during unregistering to avoid calling unregister when
>     no led device is registered.
>   - Temporarily HID_QUIRK_INPUT_PER_APP from the ROG endpoint as it causes
>     the driver to create 4 RGB handlers per device. I also suspect some
>     extra events sneak through (KDE had the @@@@@@).
>
> Antheas Kapenekakis (10):
>   HID: asus: simplify RGB init sequence
>   HID: asus: use same report_id in response
>   HID: asus: fortify keyboard handshake
>   HID: asus: prevent binding to all HID devices on ROG
>   HID: asus: initialize LED endpoint early for old NKEY keyboards
>   platform/x86: asus-wmi: Add support for multiple kbd led handlers
>   HID: asus: listen to the asus-wmi brightness device instead of
>     creating one
>   platform/x86: asus-wmi: remove unused keyboard backlight quirk
>   platform/x86: asus-wmi: add keyboard brightness event handler
>   HID: asus: add support for the asus-wmi brightness handler
>
>  drivers/hid/hid-asus.c                     | 222 +++++++++++----------
>  drivers/platform/x86/asus-wmi.c            | 214 +++++++++++++++++---
>  include/linux/platform_data/x86/asus-wmi.h |  70 +++----
>  3 files changed, 331 insertions(+), 175 deletions(-)
>
>
> base-commit: 211ddde0823f1442e4ad052a2f30f050145ccada
> --
> 2.51.2
>
>


^ permalink raw reply

* [PATCH] Input: pixcir_i2c_ts - add support for one-time total calibration
From: Michal Vokáč @ 2025-11-12 13:00 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, Fabio Estevam, Michal Vokáč

The Pixcir Tango controller has support for a one-time total calibration
(manual calibration) procedure. Its purpose is to measure the capacitance
offsets of the electrode system and to store these values into EEPROM.

During normal operation this calibration data is subtracted from the values
measured. This calibration should be necessary only once in the product
lifetime. It should be performed as part of the final adjustment after
the panel is mounted in the product.

Add support for the calibration with sysfs interface.

Signed-off-by: Michal Vokáč <michal.vokac@ysoft.com>
---
 drivers/input/touchscreen/pixcir_i2c_ts.c | 34 +++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index dad5786e82a4..2215e56b1458 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -24,6 +24,7 @@
  */
 #define PIXCIR_REG_POWER_MODE	51
 #define PIXCIR_REG_INT_MODE	52
+#define PIXCIR_REG_SPECOP	58
 
 /*
  * Power modes:
@@ -82,6 +83,7 @@ struct pixcir_i2c_ts_data {
 	const struct pixcir_i2c_chip_data *chip;
 	struct touchscreen_properties prop;
 	bool running;
+	struct mutex sysfs_mutex;
 };
 
 struct pixcir_report_data {
@@ -462,6 +464,35 @@ static int pixcir_i2c_ts_resume(struct device *dev)
 static DEFINE_SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
 				pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
 
+static ssize_t calibrate_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client);
+	static const u8 cmd = 0x03;
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_SPECOP, cmd);
+	if (error)
+		dev_err(dev, "calibrate command failed: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static DEVICE_ATTR_WO(calibrate);
+
+static struct attribute *pixcir_i2c_ts_attrs[] = {
+	&dev_attr_calibrate.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(pixcir_i2c_ts);
+
 static int pixcir_i2c_ts_probe(struct i2c_client *client)
 {
 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
@@ -488,6 +519,8 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client)
 		return -ENOMEM;
 	}
 
+	mutex_init(&tsdata->sysfs_mutex);
+
 	tsdata->client = client;
 	tsdata->input = input;
 
@@ -600,6 +633,7 @@ MODULE_DEVICE_TABLE(of, pixcir_of_match);
 static struct i2c_driver pixcir_i2c_ts_driver = {
 	.driver = {
 		.name	= "pixcir_ts",
+		.dev_groups = pixcir_i2c_ts_groups,
 		.pm	= pm_sleep_ptr(&pixcir_dev_pm_ops),
 		.of_match_table = of_match_ptr(pixcir_of_match),
 	},
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v8 00/10] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling
From: Ilpo Järvinen @ 2025-11-12 13:21 UTC (permalink / raw)
  To: Antheas Kapenekakis, Denis Benato
  Cc: platform-driver-x86, linux-input, LKML, Jiri Kosina,
	Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede
In-Reply-To: <CAGwozwE+3vkm0-amRqnNJBzxTvXabgBF9h_G_vG_L7OJj91LBg@mail.gmail.com>

On Wed, 12 Nov 2025, Antheas Kapenekakis wrote:

> On Sat, 1 Nov 2025 at 11:47, Antheas Kapenekakis <lkml@antheas.dev> wrote:
> >
> > This is a two part series which does the following:
> >   - Clean-up init sequence
> >   - Unify backlight handling to happen under asus-wmi so that all Aura
> >     devices have synced brightness controls and the backlight button works
> >     properly when it is on a USB laptop keyboard instead of one w/ WMI.
> >
> > For more context, see cover letter of V1. Since V5, I removed some patches
> > to make this easier to merge.
> 
> Small bump for this.

I looked at v8 earlier but then got an impression some of Denis' comments 
against v7 were not taken into account in v8, which implies there will be 
delay until I've time to delve into the details (I need to understand 
things pretty deeply in such a case, which does take lots of time).

Alternatively, if Denis says v8 is acceptable, then I don't need to spend 
so much time on it, but somehow I've a feeling he isn't happy with v8 
but just hasn't voiced it again...

Please do realize that ignoring reviewer feedback without a very very good 
reason always risks adding delay or friction into getting things 
upstreamed. Especially, when the review comes from a person who has been 
around for me to learn to trust their reviews or from a maintainer of the 
code in question.

> Unrelated but I was b4ing this series on Ubuntu 24 and got BADSIG:
> DKIM/antheas.dev. Is there a reference for fixing this on my host?
> Perhaps it would help with spam

I see BADSIG very often these days from b4 (thanks to gmail expiring 
things after 7 days or so, I recall hearing somewhere), I just ignore them 
entirely.

AFAIK, that has never caused any delay to any patch in pdx86 domain so if 
that is what you thought is happening here, it's not the case.
If your patch does appear in the pdx86 patchwork, there's even less reason 
to worry as I mostly pick patches to process using patchwork's list.

-- 
 i.

> 
> Antheas
> 
> > ---
> > V7: https://lore.kernel.org/all/20251018101759.4089-1-lkml@antheas.dev/
> > V6: https://lore.kernel.org/all/20251013201535.6737-1-lkml@antheas.dev/
> > V5: https://lore.kernel.org/all/20250325184601.10990-1-lkml@antheas.dev/
> > V4: https://lore.kernel.org/lkml/20250324210151.6042-1-lkml@antheas.dev/
> > V3: https://lore.kernel.org/lkml/20250322102804.418000-1-lkml@antheas.dev/
> > V2: https://lore.kernel.org/all/20250320220924.5023-1-lkml@antheas.dev/
> > V1: https://lore.kernel.org/all/20250319191320.10092-1-lkml@antheas.dev/
> >
> > Changes since V7:
> >   - Readd legacy init quirk for Dennis
> >   - Remove HID_QUIRK_INPUT_PER_APP as a courtesy to asusctl
> >   - Fix warning due to enum_backlight receiving negative values
> >
> > Changes since V6:
> >   - Split initialization refactor into three patches, update commit text
> >     to be clearer in what it does
> >   - Replace spinlock accesses with guard and scoped guard in all patches
> >   - Add missing includes mentioned by Ilpo
> >   - Reflow, tweak comment in prevent binding to all HID devices on ROG
> >   - Replace asus_ref.asus with local reference in all patches
> >   - Add missing kernel doc comments
> >   - Other minor nits from Ilpo
> >   - User reported warning due to scheduling work while holding a spinlock.
> >     Restructure patch for multiple handlers to limit when spinlock is held to
> >     variable access only. In parallel, setup a workqueue to handle registration
> >     of led device and setting brightness. This is required as registering the
> >     led device triggers kbd_led_get which needs to hold the spinlock to
> >     protect the led_wk value. The workqueue is also required for the hid
> >     event passthrough to avoid scheduling work while holding the spinlock.
> >     Apply the workqueue to wmi brightness buttons as well, as that was
> >     omitted before this series and WMI access was performed.
> >   - On "HID: asus: prevent binding to all HID devices on ROG", rename
> >     quirk HANDLE_GENERIC to SKIP_REPORT_FIXUP and only skip report fixup.
> >     This allows other quirks to apply (applies quirk that fixes keyboard
> >     being named as a pointer device).
> >
> > Changes since V5:
> >   - It's been a long time
> >   - Remove addition of RGB as that had some comments I need to work on
> >   - Remove folio patch (already merged)
> >   - Remove legacy fix patch 11 from V4. There is a small chance that
> >     without this patch, some old NKEY keyboards might not respond to
> >     RGB commands according to Luke, but the kernel driver does not do
> >     RGB currently. The 0x5d init is done by Armoury crate software in
> >     Windows. If an issue is found, we can re-add it or just remove patches
> >     1/2 before merging. However, init could use the cleanup.
> >
> > Changes since V4:
> >   - Fix KConfig (reported by kernel robot)
> >   - Fix Ilpo's nits, if I missed anything lmk
> >
> > Changes since V3:
> >   - Add initializer for 0x5d for old NKEY keyboards until it is verified
> >     that it is not needed for their media keys to function.
> >   - Cover init in asus-wmi with spinlock as per Hans
> >   - If asus-wmi registers WMI handler with brightness, init the brightness
> >     in USB Asus keyboards, per Hans.
> >   - Change hid handler name to asus-UNIQ:rgb:peripheral to match led class
> >   - Fix oops when unregistering asus-wmi by moving unregister outside of
> >     the spin lock (but after the asus reference is set to null)
> >
> > Changes since V2:
> >   - Check lazy init succeds in asus-wmi before setting register variable
> >   - make explicit check in asus_hid_register_listener for listener existing
> >     to avoid re-init
> >   - rename asus_brt to asus_hid in most places and harmonize everything
> >   - switch to a spinlock instead of a mutex to avoid kernel ooops
> >   - fixup hid device quirks to avoid multiple RGB devices while still exposing
> >     all input vendor devices. This includes moving rgb init to probe
> >     instead of the input_configured callbacks.
> >   - Remove fan key (during retest it appears to be 0xae that is already
> >     supported by hid-asus)
> >   - Never unregister asus::kbd_backlight while asus-wmi is active, as that
> >   - removes fds from userspace and breaks backlight functionality. All
> >   - current mainline drivers do not support backlight hotplugging, so most
> >     userspace software (e.g., KDE, UPower) is built with that assumption.
> >     For the Ally, since it disconnects its controller during sleep, this
> >     caused the backlight slider to not work in KDE.
> >
> > Changes since V1:
> >   - Add basic RGB support on hid-asus, (Z13/Ally) tested in KDE/Z13
> >   - Fix ifdef else having an invalid signature (reported by kernel robot)
> >   - Restore input arguments to init and keyboard function so they can
> >     be re-used for RGB controls.
> >   - Remove Z13 delay (it did not work to fix the touchpad) and replace it
> >     with a HID_GROUP_GENERIC quirk to allow hid-multitouch to load. Squash
> >     keyboard rename into it.
> >   - Unregister brightness listener before removing work queue to avoid
> >     a race condition causing corruption
> >   - Remove spurious mutex unlock in asus_brt_event
> >   - Place mutex lock in kbd_led_set after LED_UNREGISTERING check to avoid
> >     relocking the mutex and causing a deadlock when unregistering leds
> >   - Add extra check during unregistering to avoid calling unregister when
> >     no led device is registered.
> >   - Temporarily HID_QUIRK_INPUT_PER_APP from the ROG endpoint as it causes
> >     the driver to create 4 RGB handlers per device. I also suspect some
> >     extra events sneak through (KDE had the @@@@@@).
> >
> > Antheas Kapenekakis (10):
> >   HID: asus: simplify RGB init sequence
> >   HID: asus: use same report_id in response
> >   HID: asus: fortify keyboard handshake
> >   HID: asus: prevent binding to all HID devices on ROG
> >   HID: asus: initialize LED endpoint early for old NKEY keyboards
> >   platform/x86: asus-wmi: Add support for multiple kbd led handlers
> >   HID: asus: listen to the asus-wmi brightness device instead of
> >     creating one
> >   platform/x86: asus-wmi: remove unused keyboard backlight quirk
> >   platform/x86: asus-wmi: add keyboard brightness event handler
> >   HID: asus: add support for the asus-wmi brightness handler
> >
> >  drivers/hid/hid-asus.c                     | 222 +++++++++++----------
> >  drivers/platform/x86/asus-wmi.c            | 214 +++++++++++++++++---
> >  include/linux/platform_data/x86/asus-wmi.h |  70 +++----
> >  3 files changed, 331 insertions(+), 175 deletions(-)
> >
> >
> > base-commit: 211ddde0823f1442e4ad052a2f30f050145ccada
> > --
> > 2.51.2
> >
> >
> 

^ permalink raw reply

* Re: [PATCH v8 00/10] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling
From: Antheas Kapenekakis @ 2025-11-12 13:41 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: Denis Benato, platform-driver-x86, linux-input, LKML, Jiri Kosina,
	Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede
In-Reply-To: <27a74ecc-bff7-f3ae-b23e-a8362ac3a6b3@linux.intel.com>

On Wed, 12 Nov 2025 at 14:22, Ilpo Järvinen
<ilpo.jarvinen@linux.intel.com> wrote:
>
> On Wed, 12 Nov 2025, Antheas Kapenekakis wrote:
>
> > On Sat, 1 Nov 2025 at 11:47, Antheas Kapenekakis <lkml@antheas.dev> wrote:
> > >
> > > This is a two part series which does the following:
> > >   - Clean-up init sequence
> > >   - Unify backlight handling to happen under asus-wmi so that all Aura
> > >     devices have synced brightness controls and the backlight button works
> > >     properly when it is on a USB laptop keyboard instead of one w/ WMI.
> > >
> > > For more context, see cover letter of V1. Since V5, I removed some patches
> > > to make this easier to merge.
> >
> > Small bump for this.
>
> I looked at v8 earlier but then got an impression some of Denis' comments
> against v7 were not taken into account in v8, which implies there will be
> delay until I've time to delve into the details (I need to understand
> things pretty deeply in such a case, which does take lots of time).
>
> Alternatively, if Denis says v8 is acceptable, then I don't need to spend
> so much time on it, but somehow I've a feeling he isn't happy with v8
> but just hasn't voiced it again...
>
> Please do realize that ignoring reviewer feedback without a very very good
> reason always risks adding delay or friction into getting things
> upstreamed. Especially, when the review comes from a person who has been
> around for me to learn to trust their reviews or from a maintainer of the
> code in question.

Sure, sorry if it came out this way. Dennis had two comments on the V7
version of the series.

The first one was that asusctl has a hang bug, which he hasn't had
time to look into yet. This should have been fixed by dropping the
HID_QUIRK_INPUT_PER_APP. I retested the series and that QUIRK was a
bit of a NOOP that does not need to be added in the future.

The second is he is concerned with dropping the 0x5d/0x5e inits. Luke
said (back in March) that it is fine to drop 0x5e because it is only
used for ANIME displays. However, for 0x5d, it is hard to verify some
of the older laptops because they have only been tested with 0x5d and
we do not have the hardware in question to test.

For this series, I re-added "initialize LED endpoint early for old
NKEY keyboards" that re-adds 0x5d for the keyboards that cannot be
tested again so this comment should be resolved too. With that in
mind, we do end up with an additional quirk/command that may be
unneeded and will remain there forever, but since it was a point of
contention, it is not worth arguing over.

So both comments should be resolved

@Denis: can give an ack if this is the case?

As for Derek's comment, he has a PR for his project where he removes
the name match for Ally devices with ample time for it to be merged
until kernel 6.19 is released. In addition, that specific software for
full functionality relies on OOT drivers on the distros it ships with,
so it is minimally affected in either case.

Moreover, that specific commit is needed for Xbox Ally devices anyway,
as the kernel kicks one of the RGB endpoints because it does not
register an input device (the check skipped by early return) so
userspace becomes unable to control RGB on a stock kernel
(hidraw/hiddev nodes are gone). For more context here, this specific
endpoint implements the RGB Lamparray protocol for Windows dynamic
lighting, and I think in an attempt to make it work properly in
Windows, Asus made it so Windows has to first disable dynamic lighting
for armoury crate RGB commands to work (the 0x5a ones over the 0xff31
hid page).

Hopefully this clears things up

Antheas

> > Unrelated but I was b4ing this series on Ubuntu 24 and got BADSIG:
> > DKIM/antheas.dev. Is there a reference for fixing this on my host?
> > Perhaps it would help with spam
>
> I see BADSIG very often these days from b4 (thanks to gmail expiring
> things after 7 days or so, I recall hearing somewhere), I just ignore them
> entirely.
>
> AFAIK, that has never caused any delay to any patch in pdx86 domain so if
> that is what you thought is happening here, it's not the case.
> If your patch does appear in the pdx86 patchwork, there's even less reason
> to worry as I mostly pick patches to process using patchwork's list.

Turns out I had to update my DNS records. It should be good now.

> --
>  i.
>
> >
> > Antheas
> >
> > > ---
> > > V7: https://lore.kernel.org/all/20251018101759.4089-1-lkml@antheas.dev/
> > > V6: https://lore.kernel.org/all/20251013201535.6737-1-lkml@antheas.dev/
> > > V5: https://lore.kernel.org/all/20250325184601.10990-1-lkml@antheas.dev/
> > > V4: https://lore.kernel.org/lkml/20250324210151.6042-1-lkml@antheas.dev/
> > > V3: https://lore.kernel.org/lkml/20250322102804.418000-1-lkml@antheas.dev/
> > > V2: https://lore.kernel.org/all/20250320220924.5023-1-lkml@antheas.dev/
> > > V1: https://lore.kernel.org/all/20250319191320.10092-1-lkml@antheas.dev/
> > >
> > > Changes since V7:
> > >   - Readd legacy init quirk for Dennis
> > >   - Remove HID_QUIRK_INPUT_PER_APP as a courtesy to asusctl
> > >   - Fix warning due to enum_backlight receiving negative values
> > >
> > > Changes since V6:
> > >   - Split initialization refactor into three patches, update commit text
> > >     to be clearer in what it does
> > >   - Replace spinlock accesses with guard and scoped guard in all patches
> > >   - Add missing includes mentioned by Ilpo
> > >   - Reflow, tweak comment in prevent binding to all HID devices on ROG
> > >   - Replace asus_ref.asus with local reference in all patches
> > >   - Add missing kernel doc comments
> > >   - Other minor nits from Ilpo
> > >   - User reported warning due to scheduling work while holding a spinlock.
> > >     Restructure patch for multiple handlers to limit when spinlock is held to
> > >     variable access only. In parallel, setup a workqueue to handle registration
> > >     of led device and setting brightness. This is required as registering the
> > >     led device triggers kbd_led_get which needs to hold the spinlock to
> > >     protect the led_wk value. The workqueue is also required for the hid
> > >     event passthrough to avoid scheduling work while holding the spinlock.
> > >     Apply the workqueue to wmi brightness buttons as well, as that was
> > >     omitted before this series and WMI access was performed.
> > >   - On "HID: asus: prevent binding to all HID devices on ROG", rename
> > >     quirk HANDLE_GENERIC to SKIP_REPORT_FIXUP and only skip report fixup.
> > >     This allows other quirks to apply (applies quirk that fixes keyboard
> > >     being named as a pointer device).
> > >
> > > Changes since V5:
> > >   - It's been a long time
> > >   - Remove addition of RGB as that had some comments I need to work on
> > >   - Remove folio patch (already merged)
> > >   - Remove legacy fix patch 11 from V4. There is a small chance that
> > >     without this patch, some old NKEY keyboards might not respond to
> > >     RGB commands according to Luke, but the kernel driver does not do
> > >     RGB currently. The 0x5d init is done by Armoury crate software in
> > >     Windows. If an issue is found, we can re-add it or just remove patches
> > >     1/2 before merging. However, init could use the cleanup.
> > >
> > > Changes since V4:
> > >   - Fix KConfig (reported by kernel robot)
> > >   - Fix Ilpo's nits, if I missed anything lmk
> > >
> > > Changes since V3:
> > >   - Add initializer for 0x5d for old NKEY keyboards until it is verified
> > >     that it is not needed for their media keys to function.
> > >   - Cover init in asus-wmi with spinlock as per Hans
> > >   - If asus-wmi registers WMI handler with brightness, init the brightness
> > >     in USB Asus keyboards, per Hans.
> > >   - Change hid handler name to asus-UNIQ:rgb:peripheral to match led class
> > >   - Fix oops when unregistering asus-wmi by moving unregister outside of
> > >     the spin lock (but after the asus reference is set to null)
> > >
> > > Changes since V2:
> > >   - Check lazy init succeds in asus-wmi before setting register variable
> > >   - make explicit check in asus_hid_register_listener for listener existing
> > >     to avoid re-init
> > >   - rename asus_brt to asus_hid in most places and harmonize everything
> > >   - switch to a spinlock instead of a mutex to avoid kernel ooops
> > >   - fixup hid device quirks to avoid multiple RGB devices while still exposing
> > >     all input vendor devices. This includes moving rgb init to probe
> > >     instead of the input_configured callbacks.
> > >   - Remove fan key (during retest it appears to be 0xae that is already
> > >     supported by hid-asus)
> > >   - Never unregister asus::kbd_backlight while asus-wmi is active, as that
> > >   - removes fds from userspace and breaks backlight functionality. All
> > >   - current mainline drivers do not support backlight hotplugging, so most
> > >     userspace software (e.g., KDE, UPower) is built with that assumption.
> > >     For the Ally, since it disconnects its controller during sleep, this
> > >     caused the backlight slider to not work in KDE.
> > >
> > > Changes since V1:
> > >   - Add basic RGB support on hid-asus, (Z13/Ally) tested in KDE/Z13
> > >   - Fix ifdef else having an invalid signature (reported by kernel robot)
> > >   - Restore input arguments to init and keyboard function so they can
> > >     be re-used for RGB controls.
> > >   - Remove Z13 delay (it did not work to fix the touchpad) and replace it
> > >     with a HID_GROUP_GENERIC quirk to allow hid-multitouch to load. Squash
> > >     keyboard rename into it.
> > >   - Unregister brightness listener before removing work queue to avoid
> > >     a race condition causing corruption
> > >   - Remove spurious mutex unlock in asus_brt_event
> > >   - Place mutex lock in kbd_led_set after LED_UNREGISTERING check to avoid
> > >     relocking the mutex and causing a deadlock when unregistering leds
> > >   - Add extra check during unregistering to avoid calling unregister when
> > >     no led device is registered.
> > >   - Temporarily HID_QUIRK_INPUT_PER_APP from the ROG endpoint as it causes
> > >     the driver to create 4 RGB handlers per device. I also suspect some
> > >     extra events sneak through (KDE had the @@@@@@).
> > >
> > > Antheas Kapenekakis (10):
> > >   HID: asus: simplify RGB init sequence
> > >   HID: asus: use same report_id in response
> > >   HID: asus: fortify keyboard handshake
> > >   HID: asus: prevent binding to all HID devices on ROG
> > >   HID: asus: initialize LED endpoint early for old NKEY keyboards
> > >   platform/x86: asus-wmi: Add support for multiple kbd led handlers
> > >   HID: asus: listen to the asus-wmi brightness device instead of
> > >     creating one
> > >   platform/x86: asus-wmi: remove unused keyboard backlight quirk
> > >   platform/x86: asus-wmi: add keyboard brightness event handler
> > >   HID: asus: add support for the asus-wmi brightness handler
> > >
> > >  drivers/hid/hid-asus.c                     | 222 +++++++++++----------
> > >  drivers/platform/x86/asus-wmi.c            | 214 +++++++++++++++++---
> > >  include/linux/platform_data/x86/asus-wmi.h |  70 +++----
> > >  3 files changed, 331 insertions(+), 175 deletions(-)
> > >
> > >
> > > base-commit: 211ddde0823f1442e4ad052a2f30f050145ccada
> > > --
> > > 2.51.2
> > >
> > >
> >
>


^ permalink raw reply

* Re: [PATCH] Input: xpad - Change buttons the D-Pad gets mapped as to BTN_DPAD_*
From: Antheas Kapenekakis @ 2025-11-12 14:16 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Cameron Gutman, Vicki Pfau, linux-input
In-Reply-To: <rkua5gpwvwcs5nb7u6dvab7xlgkpghaw6cezk5mztdkrdc7zch@lwflpa5wfe2e>

On Wed, 5 Nov 2025 at 06:04, Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
>
> On Sat, Nov 01, 2025 at 11:23:51AM -0500, Cameron Gutman wrote:
> > On Sat, Nov 1, 2025 at 12:52 AM Vicki Pfau <vi@endrift.com> wrote:
> > >
> > >
> > >
> > > On 10/31/25 8:18 PM, Cameron Gutman wrote:
> > > > On Sun, Jul 27, 2025 at 3:23 AM Dmitry Torokhov
> > > > <dmitry.torokhov@gmail.com> wrote:
> > > >>
> > > >> On Tue, Jul 01, 2025 at 08:47:40PM -0700, Vicki Pfau wrote:
> > > >>> Since dance pads can have both up/down or left/right pressed at the same time,
> > > >>> by design, they are not suitable for mapping the buttons to axes. Historically,
> > > >>> this driver mapped the D-pad to BTN_TRIGGER_HAPPY1-4 in these cases, and before
> > > >>> that as mouse buttons. However, BTN_DPAD_* exists for this and makes far more
> > > >>> sense than the arbitrary mapping it was before.
> > > >>>
> > > >>> Signed-off-by: Vicki Pfau <vi@endrift.com>
> > > >>
> > > >> This unfortunately changes existing mappings, but I guess new events are
> > > >> better than old ones...
> > > >>
> > > >> Applied, thank you.
> > > >
> > > > Unfortunately this mapping change caused major userspace breakages.
> > >
> > > This is unfortunate, however,>
> > > > I think it needs to be reverted.
> > > >
> > > > [0]: https://blog.dwickham.me.uk/why-your-xbox-360-wireless-controllers-d-pad-is-now-wrong-and-how-to-fix-it
> > > > [1]: https://github.com/libsdl-org/SDL/issues/14324
> > > > [2]: https://github.com/libsdl-org/SDL/pull/14339
> > >
> > > None of these pages suggest reverting the changes. The blog post suggests that these mappings are correct, and that the libraries need to be fixed. Reverting it is mentioned as an absolute last case scenario. The SDL issues say they need more testing, not an outright reversion.
> >
> > Breaking userspace is the #1 thing we cannot do as Linux kernel developers.
> >
> > The fact that it broke enough people to get a blog post is reason
> > enough to revert.
> >
> > >
> > > Reverting this is solely reverting to an old, uncomfortable status quo instead of actually fixing the things that broke. If this gets reverted, presumably more stuff will ship this broken code that will break if we ever fix this issue again, instead of things getting progressively fixed.
> > >
> >
> > The "broken" 360 wireless mapping has been used since wireless adapter
> > support was first implemented in 2008 [0].
> >
> > There's decades of software out there that already has workarounds for
> > these devices that we must not break.
> >
> > We can send the proper button codes for new controllers, but I think
> > we have to leave existing devices alone.
> >
> > [0]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=99de0912be6f384fc31c8e8e7ba0850d0d670385
> >
> > > The abandonware being broken is unfortunate, but things like Steam Input exist specifically to work around issues like that.
> > >
> >
> > This isn't just breaking abandonware. It's breaking the *latest*
> > releases of SDL2 and SDL3.
>
> I agree that keeping the old mapping is not great idea, especially if we
> start having newer devices using new mapping while other devices use the
> old one. However we can't really be breaking existing user setup and now
> that we are aware of such breakages we have to react. I am open to
> suggestions.
>
> From my side I see a few options:
>
> 1. Introduce a module option, and allow controlling the behavior. Hope
> distributions will flip it when updating to new version of SDL that can
> hopefully handle this. Eventually flip the default to be the new
> mapping.
>
> 2. Allow overriding mapping from userspace. Allow [main] user (SDL or
> not) load an updated keymap if they desire to do so.
>
> 3. Maybe we can send both old and new events at the same time and let
> consumers decide which ones to act upon? Eventually we can drop the old
> mapping (maybe again having an option to use old one or implement option
> 2 and allow loading old mapping via udev or a utility).
>
> 4. ???

Hi,
jumping on this to give some background.

Currently, the way the kernel handles evdev gamepads is a bit of a
mess, specifically when it comes to DInput.

Essentially, gamepads are consumed using the legacy js API which does
not have event names but only button indexes. Or an evdev wrapper if
using an SDL-like backend that does something similar.

There are no specific mappings for each index, this is different for
every gamepad. Userspace software is supposed to know the mapping for
each gamepad based on a heuristic. The most widespread one is the SDL
mappings, which are also available in the form of a community database
[1].

You can see in the button index code in the kernel that it starts with
buttons from index BTN_JOYSTICK, goes up to KEY_MAX, then loops around
and registers the buttons until BTN_MISC (which skips most keyboard
buttons). Those are the buttons available in the legacy API. SDL is
different, in that it keeps registering the buttons after BTN_MISC
which is important for some gamepads that use KEY_MENU for e.g., the
Xbox button. This means that it is possible to use SDL mappings with
the joydev API while missing some buttons, but since those buttons are
at the end and usually special functions it is a NOOP.

Dinput is where it gets interesting and why it is kind of a fruitless
endeavor to try to rename buttons. In [3], the kernel starts
registering buttons with HID_GD_JOYSTICK from BTN_JOYSTICK onwards, so
all HID gamepads that do not bind to a driver have completely invalid
evdev event names. And this is most non-XInput gamepads (around 400
models) so no one in their right mind would rely on evdev names from
userspace and they would instead use that mapping.

WIth that in mind, it is very important to not shift the gamepad
button order when adding new mappings. By default, the Xbox 360 order
is used. E.g., dont use BTN_BASE for an extra utility button like the
hid-steam driver (which otherwise has an xbox 360 order), as because
it is before BTN_GAMEPAD this shifts the index of all buttons by 1 and
breaks all userspace software without a mapping (this is fixed in
userspace now).

With that in mind, commit [4] that adds button grips is also
problematic, as those buttons are BELOW trigger-happy which used to do
dpad buttons. If you revert just this commit, the grips will become
dpad buttons for devices that have both.

So both commits would have to be reverted. With them together or
without either, the button order is maintained. However, they have a
different order within them so dpad is broken. I suspect the grip
buttons for Xbox Elite might have a similar issue... Although I have
not had a bug report about it.

I already received and merged a PR that fixes this from user space[5],
and I saw that the SDL3 fix is not coming to SDL2, but i think that
fix might leave that config stuck in the user's Steam config dir, so
perhaps it would be prudent for me to undo it and instead just revert
both commits.

Antheas

[1] https://github.com/mdqinc/SDL_GameControllerDB
[2] drivers/input/joydev.c
[3] drivers/hid/hid-input.c
[4] Input: xpad - use new BTN_GRIP* buttons
[5] https://github.com/ublue-os/bazzite/issues/3399

> Thanks.
>
> --
> Dmitry


^ permalink raw reply

* Re: [RFC PATCH 1/1] HID: input: Add support for multiple batteries per device
From: Bastien Nocera @ 2025-11-12 14:15 UTC (permalink / raw)
  To: Lucas Zampieri, linux-input
  Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Sebastian Reichel,
	linux-pm
In-Reply-To: <20251111105634.1684751-2-lzampier@redhat.com>

Hey Lucas,

(Follow-up to a chat we had about this patch privately)

On Tue, 2025-11-11 at 10:56 +0000, Lucas Zampieri wrote:
> Add support for multiple batteries per HID device by introducing
> struct hid_battery to encapsulate individual battery state and using
> a list to track multiple batteries identified by report ID. The
> legacy
> dev->battery field is maintained for backwards compatibility.

The cover letter mentions specific hardware, you probably want to
mention this in the commit message itself, as the cover letter will be
disconnected from this commit once this gets merged. Don't hesitate to
link to product pages directly if you want to show specific products as
potential users of that capability.

You mentioned that you tested this patchset with a custom firmware for
a split keyboard. It would be great if the firmware could be made
available to show how this was tested and mention that in the commit
message.

bentiss will also likely want a hid-recorder output for the device that
shows the batteries being instantiated. This would also likely be used
to test whether upower continues working as expected.

Talking of upower, I think we'll need an systemd/hwdb + upower changes
to differentiate batteries within a single device, as I don't think we
can have enough metadata in the HID report to differentiate them.

Last comment about the patch itself, do you think it would be feasible
to split this in 2 or 3? One to introduce the hid_battery struct,
another to use it to replace direct power_supply access, and finally
one to allow a list of hid_batteries?

Don't hesitate to CC: on future versions.

Cheers

> 
> Signed-off-by: Lucas Zampieri <lzampier@redhat.com>
> ---
>  drivers/hid/hid-core.c  |   4 +
>  drivers/hid/hid-input.c | 193 +++++++++++++++++++++++++++-----------
> --
>  include/linux/hid.h     |  42 ++++++++-
>  3 files changed, 176 insertions(+), 63 deletions(-)
> 
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index a5b3a8ca2fcb..76d628547e9a 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -2990,6 +2990,10 @@ struct hid_device *hid_allocate_device(void)
>  	mutex_init(&hdev->ll_open_lock);
>  	kref_init(&hdev->ref);
>  
> +#ifdef CONFIG_HID_BATTERY_STRENGTH
> +	INIT_LIST_HEAD(&hdev->batteries);
> +#endif
> +
>  	ret = hid_bpf_device_init(hdev);
>  	if (ret)
>  		goto out_err;
> diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> index e56e7de53279..071df319775b 100644
> --- a/drivers/hid/hid-input.c
> +++ b/drivers/hid/hid-input.c
> @@ -454,7 +454,8 @@ static int hidinput_get_battery_property(struct
> power_supply *psy,
>  					 enum power_supply_property
> prop,
>  					 union power_supply_propval
> *val)
>  {
> -	struct hid_device *dev = power_supply_get_drvdata(psy);
> +	struct hid_battery *bat = power_supply_get_drvdata(psy);
> +	struct hid_device *dev = bat->dev;
>  	int value;
>  	int ret = 0;
>  
> @@ -465,13 +466,13 @@ static int hidinput_get_battery_property(struct
> power_supply *psy,
>  		break;
>  
>  	case POWER_SUPPLY_PROP_CAPACITY:
> -		if (dev->battery_status != HID_BATTERY_REPORTED &&
> -		    !dev->battery_avoid_query) {
> +		if (bat->status != HID_BATTERY_REPORTED &&
> +		    !bat->avoid_query) {
>  			value =
> hidinput_query_battery_capacity(dev);
>  			if (value < 0)
>  				return value;
>  		} else  {
> -			value = dev->battery_capacity;
> +			value = bat->capacity;
>  		}
>  
>  		val->intval = value;
> @@ -482,20 +483,20 @@ static int hidinput_get_battery_property(struct
> power_supply *psy,
>  		break;
>  
>  	case POWER_SUPPLY_PROP_STATUS:
> -		if (dev->battery_status != HID_BATTERY_REPORTED &&
> -		    !dev->battery_avoid_query) {
> +		if (bat->status != HID_BATTERY_REPORTED &&
> +		    !bat->avoid_query) {
>  			value =
> hidinput_query_battery_capacity(dev);
>  			if (value < 0)
>  				return value;
>  
> -			dev->battery_capacity = value;
> -			dev->battery_status = HID_BATTERY_QUERIED;
> +			bat->capacity = value;
> +			bat->status = HID_BATTERY_QUERIED;
>  		}
>  
> -		if (dev->battery_status == HID_BATTERY_UNKNOWN)
> +		if (bat->status == HID_BATTERY_UNKNOWN)
>  			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
>  		else
> -			val->intval = dev->battery_charge_status;
> +			val->intval = bat->charge_status;
>  		break;
>  
>  	case POWER_SUPPLY_PROP_SCOPE:
> @@ -513,33 +514,53 @@ static int hidinput_get_battery_property(struct
> power_supply *psy,
>  static int hidinput_setup_battery(struct hid_device *dev, unsigned
> report_type,
>  				  struct hid_field *field, bool
> is_percentage)
>  {
> +	struct hid_battery *bat;
>  	struct power_supply_desc *psy_desc;
> -	struct power_supply_config psy_cfg = { .drv_data = dev, };
> +	struct power_supply_config psy_cfg;
>  	unsigned quirks;
>  	s32 min, max;
>  	int error;
> +	int battery_num = 0;
>  
> -	if (dev->battery)
> -		return 0;	/* already initialized? */
> +	list_for_each_entry(bat, &dev->batteries, list) {
> +		if (bat->report_id == field->report->id)
> +			return 0;	/* already initialized */
> +		battery_num++;
> +	}
>  
>  	quirks = find_battery_quirk(dev);
>  
> -	hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
> -		dev->bus, dev->vendor, dev->product, dev->version,
> quirks);
> +	hid_dbg(dev, "device %x:%x:%x %d quirks %d report_id %d\n",
> +		dev->bus, dev->vendor, dev->product, dev->version,
> quirks,
> +		field->report->id);
>  
>  	if (quirks & HID_BATTERY_QUIRK_IGNORE)
>  		return 0;
>  
> -	psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
> -	if (!psy_desc)
> +	bat = kzalloc(sizeof(*bat), GFP_KERNEL);
> +	if (!bat)
>  		return -ENOMEM;
>  
> -	psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery",
> -				   strlen(dev->uniq) ?
> -					dev->uniq : dev_name(&dev-
> >dev));
> +	psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
> +	if (!psy_desc) {
> +		error = -ENOMEM;
> +		goto err_free_bat;
> +	}
> +
> +	/* Create unique name for each battery based on report ID */
> +	if (battery_num == 0) {
> +		psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-
> battery",
> +					   strlen(dev->uniq) ?
> +						dev->uniq :
> dev_name(&dev->dev));
> +	} else {
> +		psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-
> battery-%d",
> +					   strlen(dev->uniq) ?
> +						dev->uniq :
> dev_name(&dev->dev),
> +					   battery_num);
> +	}
>  	if (!psy_desc->name) {
>  		error = -ENOMEM;
> -		goto err_free_mem;
> +		goto err_free_desc;
>  	}
>  
>  	psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
> @@ -559,98 +580,148 @@ static int hidinput_setup_battery(struct
> hid_device *dev, unsigned report_type,
>  	if (quirks & HID_BATTERY_QUIRK_FEATURE)
>  		report_type = HID_FEATURE_REPORT;
>  
> -	dev->battery_min = min;
> -	dev->battery_max = max;
> -	dev->battery_report_type = report_type;
> -	dev->battery_report_id = field->report->id;
> -	dev->battery_charge_status =
> POWER_SUPPLY_STATUS_DISCHARGING;
> +	/* Initialize battery structure */
> +	bat->dev = dev;
> +	bat->min = min;
> +	bat->max = max;
> +	bat->report_type = report_type;
> +	bat->report_id = field->report->id;
> +	bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
> +	bat->status = HID_BATTERY_UNKNOWN;
>  
>  	/*
>  	 * Stylus is normally not connected to the device and thus
> we
>  	 * can't query the device and get meaningful battery
> strength.
>  	 * We have to wait for the device to report it on its own.
>  	 */
> -	dev->battery_avoid_query = report_type == HID_INPUT_REPORT
> &&
> -				   field->physical == HID_DG_STYLUS;
> +	bat->avoid_query = report_type == HID_INPUT_REPORT &&
> +			   field->physical == HID_DG_STYLUS;
>  
>  	if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
> -		dev->battery_avoid_query = true;
> +		bat->avoid_query = true;
>  
> -	dev->battery = power_supply_register(&dev->dev, psy_desc,
> &psy_cfg);
> -	if (IS_ERR(dev->battery)) {
> -		error = PTR_ERR(dev->battery);
> +	psy_cfg.drv_data = bat;
> +	bat->ps = power_supply_register(&dev->dev, psy_desc,
> &psy_cfg);
> +	if (IS_ERR(bat->ps)) {
> +		error = PTR_ERR(bat->ps);
>  		hid_warn(dev, "can't register power supply: %d\n",
> error);
>  		goto err_free_name;
>  	}
>  
> -	power_supply_powers(dev->battery, &dev->dev);
> +	power_supply_powers(bat->ps, &dev->dev);
> +
> +	list_add_tail(&bat->list, &dev->batteries);
> +
> +	/*
> +	 * The legacy single battery API is preserved by exposing
> the first
> +	 * discovered battery. Systems relying on a single battery
> view maintain
> +	 * unchanged behavior.
> +	 */
> +	if (battery_num == 0) {
> +		dev->battery = bat->ps;
> +		dev->battery_min = bat->min;
> +		dev->battery_max = bat->max;
> +		dev->battery_report_type = bat->report_type;
> +		dev->battery_report_id = bat->report_id;
> +		dev->battery_charge_status = bat->charge_status;
> +		dev->battery_status = bat->status;
> +		dev->battery_avoid_query = bat->avoid_query;
> +	}
> +
>  	return 0;
>  
>  err_free_name:
>  	kfree(psy_desc->name);
> -err_free_mem:
> +err_free_desc:
>  	kfree(psy_desc);
> -	dev->battery = NULL;
> +err_free_bat:
> +	kfree(bat);
>  	return error;
>  }
>  
>  static void hidinput_cleanup_battery(struct hid_device *dev)
>  {
> +	struct hid_battery *bat, *next;
>  	const struct power_supply_desc *psy_desc;
>  
> -	if (!dev->battery)
> -		return;
> +	list_for_each_entry_safe(bat, next, &dev->batteries, list) {
> +		psy_desc = bat->ps->desc;
> +		power_supply_unregister(bat->ps);
> +		kfree(psy_desc->name);
> +		kfree(psy_desc);
> +		list_del(&bat->list);
> +		kfree(bat);
> +	}
>  
> -	psy_desc = dev->battery->desc;
> -	power_supply_unregister(dev->battery);
> -	kfree(psy_desc->name);
> -	kfree(psy_desc);
>  	dev->battery = NULL;
>  }
>  
> -static bool hidinput_update_battery_charge_status(struct hid_device
> *dev,
> +static struct hid_battery *hidinput_find_battery(struct hid_device
> *dev,
> +						 int report_id)
> +{
> +	struct hid_battery *bat;
> +
> +	list_for_each_entry(bat, &dev->batteries, list) {
> +		if (bat->report_id == report_id)
> +			return bat;
> +	}
> +	return NULL;
> +}
> +
> +static bool hidinput_update_battery_charge_status(struct hid_battery
> *bat,
>  						  unsigned int
> usage, int value)
>  {
>  	switch (usage) {
>  	case HID_BAT_CHARGING:
> -		dev->battery_charge_status = value ?
> -					    
> POWER_SUPPLY_STATUS_CHARGING :
> -					    
> POWER_SUPPLY_STATUS_DISCHARGING;
> +		bat->charge_status = value ?
> +				     POWER_SUPPLY_STATUS_CHARGING :
> +				    
> POWER_SUPPLY_STATUS_DISCHARGING;
> +		if (bat->dev->battery == bat->ps)
> +			bat->dev->battery_charge_status = bat-
> >charge_status;
>  		return true;
>  	}
>  
>  	return false;
>  }
>  
> -static void hidinput_update_battery(struct hid_device *dev, unsigned
> int usage,
> -				    int value)
> +static void hidinput_update_battery(struct hid_device *dev, int
> report_id,
> +				    unsigned int usage, int value)
>  {
> +	struct hid_battery *bat;
>  	int capacity;
>  
> -	if (!dev->battery)
> +	bat = hidinput_find_battery(dev, report_id);
> +	if (!bat)
>  		return;
>  
> -	if (hidinput_update_battery_charge_status(dev, usage,
> value)) {
> -		power_supply_changed(dev->battery);
> +	if (hidinput_update_battery_charge_status(bat, usage,
> value)) {
> +		power_supply_changed(bat->ps);
>  		return;
>  	}
>  
>  	if ((usage & HID_USAGE_PAGE) == HID_UP_DIGITIZER && value ==
> 0)
>  		return;
>  
> -	if (value < dev->battery_min || value > dev->battery_max)
> +	if (value < bat->min || value > bat->max)
>  		return;
>  
>  	capacity = hidinput_scale_battery_capacity(dev, value);
>  
> -	if (dev->battery_status != HID_BATTERY_REPORTED ||
> -	    capacity != dev->battery_capacity ||
> -	    ktime_after(ktime_get_coarse(), dev-
> >battery_ratelimit_time)) {
> -		dev->battery_capacity = capacity;
> -		dev->battery_status = HID_BATTERY_REPORTED;
> -		dev->battery_ratelimit_time =
> +	if (bat->status != HID_BATTERY_REPORTED ||
> +	    capacity != bat->capacity ||
> +	    ktime_after(ktime_get_coarse(), bat->ratelimit_time)) {
> +		bat->capacity = capacity;
> +		bat->status = HID_BATTERY_REPORTED;
> +		bat->ratelimit_time =
>  			ktime_add_ms(ktime_get_coarse(), 30 * 1000);
> -		power_supply_changed(dev->battery);
> +
> +		if (dev->battery == bat->ps) {
> +			dev->battery_capacity = bat->capacity;
> +			dev->battery_status = bat->status;
> +			dev->battery_ratelimit_time = bat-
> >ratelimit_time;
> +		}
> +
> +		power_supply_changed(bat->ps);
>  	}
>  }
>  #else  /* !CONFIG_HID_BATTERY_STRENGTH */
> @@ -664,8 +735,8 @@ static void hidinput_cleanup_battery(struct
> hid_device *dev)
>  {
>  }
>  
> -static void hidinput_update_battery(struct hid_device *dev, unsigned
> int usage,
> -				    int value)
> +static void hidinput_update_battery(struct hid_device *dev, int
> report_id,
> +				    unsigned int usage, int value)
>  {
>  }
>  #endif	/* CONFIG_HID_BATTERY_STRENGTH */
> @@ -1533,7 +1604,7 @@ void hidinput_hid_event(struct hid_device *hid,
> struct hid_field *field, struct
>  		return;
>  
>  	if (usage->type == EV_PWR) {
> -		hidinput_update_battery(hid, usage->hid, value);
> +		hidinput_update_battery(hid, report->id, usage->hid,
> value);
>  		return;
>  	}
>  
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index a4ddb94e3ee5..a6e36835fb3c 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -634,6 +634,36 @@ enum hid_battery_status {
>  	HID_BATTERY_REPORTED,		/* Device sent unsolicited
> battery strength report */
>  };
>  
> +/**
> + * struct hid_battery - represents a single battery power supply
> + * @list: list node for linking into hid_device's battery list
> + * @dev: pointer to the parent hid_device
> + * @ps: the power supply device
> + * @capacity: current battery capacity
> + * @min: minimum battery value
> + * @max: maximum battery value
> + * @report_type: type of report (HID_INPUT_REPORT,
> HID_FEATURE_REPORT)
> + * @report_id: report ID for this battery
> + * @charge_status: current charge status
> + * @status: battery status (unknown, queried, reported)
> + * @avoid_query: if true, don't query battery (wait for device
> reports)
> + * @ratelimit_time: time for rate limiting battery updates
> + */
> +struct hid_battery {
> +	struct list_head list;
> +	struct hid_device *dev;
> +	struct power_supply *ps;
> +	__s32 capacity;
> +	__s32 min;
> +	__s32 max;
> +	__s32 report_type;
> +	__s32 report_id;
> +	__s32 charge_status;
> +	enum hid_battery_status status;
> +	bool avoid_query;
> +	ktime_t ratelimit_time;
> +};
> +
>  struct hid_driver;
>  struct hid_ll_driver;
>  
> @@ -670,8 +700,16 @@ struct hid_device {
>  #ifdef CONFIG_HID_BATTERY_STRENGTH
>  	/*
>  	 * Power supply information for HID devices which report
> -	 * battery strength. power_supply was successfully
> registered if
> -	 * battery is non-NULL.
> +	 * battery strength. Each battery is tracked separately in
> the
> +	 * batteries list.
> +	 */
> +	struct list_head batteries;		/* List of
> hid_battery structures */
> +
> +	/*
> +	 * Legacy single battery support - kept for backwards
> compatibility.
> +	 * Points to the first battery in the list if any exists.
> +	 * power_supply was successfully registered if battery is
> non-NULL.
> +	 * DEPRECATED: New code should iterate through batteries
> list instead.
>  	 */
>  	struct power_supply *battery;
>  	__s32 battery_capacity;

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox