Linux Integrity Measurement development
 help / color / mirror / Atom feed
* [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc
@ 2026-10-29  8:51 Atharva Tiwari
  2026-10-29  8:51 ` [RFC PATCH 01/14] treewide: Add backend for macsmc Atharva Tiwari
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:51 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

applesmc is very old, and all of its code is lumped into one
driver. Fan control, sensors, the light sensor, the motion
sensor and the keyboard backlight in a single file
with its own way of talking to the SMC. It is hard to extend
and to support new machines.

This series adds Intel Mac support to the macsmc drivers that
were written for Apple Silicon (the Asahi implementation).
The SMC works the same way on both platforms, so we share a
lot of code with Asahi. Only the way of talking to the SMC
differs.

To make that work, patch 1 splits the macsmc driver into a
common core and a backend. The existing RTKit code moves into
its own backend. Patch 2 adds an ACPI backend for Intel Macs
with I/O port, MMIO and interrupt support. After that, each
function gets its own small driver using the standard kernel
interfaces (hwmon, IIO, LEDs, power supply, RTC, watchdog),
instead of one large driver doing everything.

On Intel Macs these drivers replace applesmc (hwmon, light
sensor, motion sensor and keyboard backlight) and acpi-sbs
(battery and adapter). They are mutually exclusive with the
drivers they replace, so only one is used at a time.

This is an RFC to get early feedback on the design. The macsmc
split and the Intel Mac backend touch fundamental parts, so I
would rather change them now if maintainers ask. After that I
will test the series on more macsand send it upstream as a
proper series.

The series is organised as follows:

  1      Split macsmc into a core and backends
  2      ACPI backend for Intel Macs
  3-6    New drivers: watchdog, IIO light and accelerometer,
         keyboard backlight LED
  7-9    RTC support for T2 Macs, a T2 detection flag, and
         disabling the broken ACPI RTCs
  10-11  Power supply support, replacing acpi-sbs
  12-13  hwmon support for Intel Macs
  14     ioctl for userspace access to SMC keys


Tested-on: MacBookPro16,1 , iMac20,1 , MacBookAir4,2 , MacBookPro12,1 ,
MacBookPro15,1

NOTE: Almost all drivers have been tested except the motion sensor
driver as i was not able to find any device that had one.

Atharva Tiwari (14):
  treewide: Add backend for macsmc
  platform/x86: add backend for macsmc in intel macs
  watchdog: add driver for apple smc
  iio/light: add new driver for macsmc
  iio/accel: add driver for apple SMS
  leds: add support for macsmc kbl
  rtc: add support for x86 macs in macsmc-rtc
  treewide: Add a flag to detect the Apple T2 chip
  treewide: disable other rtc's in t2 macs
  power/supply: add support for x86 macs in macsmc-power
  acpi: disable battery and adapter for x86 apple machines
  hwmon/macsmc: add more types
  hwmon: add support for x86 macs in macsmc-hwmon
  mfd/macsmc: add ioctl to access smc keys

 MAINTAINERS                                   |    2 +-
 arch/x86/kernel/quirks.c                      |   37 +
 drivers/acpi/acpi_tad.c                       |    4 +
 drivers/acpi/sbs.c                            |   17 +-
 drivers/acpi/x86/utils.c                      |    4 +
 drivers/gpio/Kconfig                          |    2 +-
 drivers/gpio/gpio-macsmc.c                    |   13 +-
 drivers/hwmon/Kconfig                         |    7 +-
 drivers/hwmon/macsmc-hwmon.c                  | 1126 ++++++++++++++++-
 drivers/iio/accel/Kconfig                     |   10 +
 drivers/iio/accel/Makefile                    |    1 +
 drivers/iio/accel/macsmc-accel.c              |  248 ++++
 drivers/iio/light/Kconfig                     |   11 +
 drivers/iio/light/Makefile                    |    1 +
 drivers/iio/light/macsmc-light.c              |  189 +++
 drivers/leds/Kconfig                          |   11 +
 drivers/leds/Makefile                         |    1 +
 drivers/leds/leds-macsmc.c                    |   69 +
 drivers/mfd/Kconfig                           |   15 +-
 drivers/mfd/Makefile                          |    2 +-
 drivers/mfd/macsmc-core.c                     |  362 ++++++
 drivers/platform/x86/Kconfig                  |   10 +
 drivers/platform/x86/Makefile                 |    1 +
 drivers/platform/x86/macsmc-acpi.c            |  642 ++++++++++
 drivers/power/reset/Kconfig                   |    2 +-
 drivers/power/supply/Kconfig                  |    2 +-
 drivers/power/supply/macsmc-power.c           |  751 ++++++++---
 drivers/rtc/Kconfig                           |    2 +-
 drivers/rtc/rtc-cmos.c                        |    4 +
 drivers/rtc/rtc-macsmc.c                      |  126 +-
 drivers/soc/apple/Kconfig                     |   14 +
 drivers/soc/apple/Makefile                    |    2 +
 .../macsmc.c => soc/apple/macsmc-rtkit.c}     |  185 ++-
 drivers/watchdog/Kconfig                      |   11 +
 drivers/watchdog/Makefile                     |    2 +-
 drivers/watchdog/macsmc_wdt.c                 |  140 ++
 include/linux/mfd/macsmc.h                    |  104 +-
 include/linux/platform_data/x86/apple.h       |    5 +
 .../platform_certs/keyring_handler.h          |    8 -
 security/integrity/platform_certs/load_uefi.c |   38 +-
 40 files changed, 3786 insertions(+), 395 deletions(-)
 create mode 100644 drivers/iio/accel/macsmc-accel.c
 create mode 100644 drivers/iio/light/macsmc-light.c
 create mode 100644 drivers/leds/leds-macsmc.c
 create mode 100644 drivers/mfd/macsmc-core.c
 create mode 100644 drivers/platform/x86/macsmc-acpi.c
 rename drivers/{mfd/macsmc.c => soc/apple/macsmc-rtkit.c} (70%)
 create mode 100644 drivers/watchdog/macsmc_wdt.c

-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [RFC PATCH 01/14] treewide: Add backend for macsmc
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
@ 2026-10-29  8:51 ` Atharva Tiwari
  2026-10-29  8:51 ` [RFC PATCH 02/14] platform/x86: add backend for macsmc in intel macs Atharva Tiwari
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:51 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Split the Apple SMC driver into a common core and a backend
that handles the actual communication. The existing RTKit
code moves to its own backend, and nothing changes for
Apple Silicon.

This lets the same SMC drivers run on other kinds of
hardware, starting with Intel Macs, as a replacement for
applesmc.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 MAINTAINERS                                   |   2 +-
 drivers/gpio/Kconfig                          |   2 +-
 drivers/gpio/gpio-macsmc.c                    |  13 +-
 drivers/hwmon/Kconfig                         |   2 +-
 drivers/mfd/Kconfig                           |  15 +-
 drivers/mfd/Makefile                          |   2 +-
 drivers/mfd/macsmc-core.c                     | 164 ++++++++++++++++
 drivers/power/reset/Kconfig                   |   2 +-
 drivers/power/supply/Kconfig                  |   2 +-
 drivers/power/supply/macsmc-power.c           |   4 +-
 drivers/rtc/Kconfig                           |   2 +-
 drivers/soc/apple/Kconfig                     |  14 ++
 drivers/soc/apple/Makefile                    |   2 +
 .../macsmc.c => soc/apple/macsmc-rtkit.c}     | 185 ++++++++----------
 include/linux/mfd/macsmc.h                    |  81 +++++---
 15 files changed, 326 insertions(+), 166 deletions(-)
 create mode 100644 drivers/mfd/macsmc-core.c
 rename drivers/{mfd/macsmc.c => soc/apple/macsmc-rtkit.c} (70%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 03649f08c..1e5ae12ce 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2727,7 +2727,7 @@ F:	drivers/input/touchscreen/apple_z2.c
 F:	drivers/iommu/apple-dart.c
 F:	drivers/iommu/io-pgtable-dart.c
 F:	drivers/irqchip/irq-apple-aic.c
-F:	drivers/mfd/macsmc.c
+F:	drivers/mfd/macsmc-core.c
 F:	drivers/nvme/host/apple.c
 F:	drivers/nvmem/apple-efuses.c
 F:	drivers/nvmem/apple-spmi-nvmem.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 55e428002..63a2b1e2e 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1582,7 +1582,7 @@ config GPIO_LP87565
 
 config GPIO_MACSMC
 	tristate "Apple Mac SMC GPIO"
-	depends on MFD_MACSMC
+	depends on MACSMC_RTKIT
 	help
 	  Support for GPIOs controlled by the SMC microcontroller on Apple Mac
 	  systems.
diff --git a/drivers/gpio/gpio-macsmc.c b/drivers/gpio/gpio-macsmc.c
index b0952d066..8c7a942ad 100644
--- a/drivers/gpio/gpio-macsmc.c
+++ b/drivers/gpio/gpio-macsmc.c
@@ -97,20 +97,9 @@ static int macsmc_gpio_find_first_gpio_index(struct macsmc_gpio *smcgp)
 {
 	struct apple_smc *smc = smcgp->smc;
 	smc_key key = macsmc_gpio_key(0);
-	smc_key first_key, last_key;
 	int start, count, ret;
 
-	/* Return early if the key is out of bounds */
-	ret = apple_smc_get_key_by_index(smc, 0, &first_key);
-	if (ret)
-		return ret;
-	if (key <= first_key)
-		return -ENODEV;
-
-	ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &last_key);
-	if (ret)
-		return ret;
-	if (key > last_key)
+	if (key <= smc->first_key || key > smc->last_key)
 		return -ENODEV;
 
 	/* Binary search to find index of first SMC key bigger or equal to key */
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 18c682e1a..91ca7031c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1245,7 +1245,7 @@ config SENSORS_LTQ_CPUTEMP
 
 config SENSORS_MACSMC_HWMON
 	tristate "Apple SMC (Apple Silicon)"
-	depends on MFD_MACSMC && OF
+	depends on MFD_MACSMC_CORE
 	help
 	  This driver enables hwmon support for current, power, temperature,
 	  and voltage sensors, as well as fan speed reporting and control
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 87ed55c15..c58cc3315 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -339,24 +339,15 @@ config MFD_LOCHNAGAR
 	help
 	  Support for Cirrus Logic Lochnagar audio development board.
 
-config MFD_MACSMC
-	tristate "Apple Silicon System Management Controller (SMC)"
-	depends on ARCH_APPLE || COMPILE_TEST
-	depends on OF
-	depends on APPLE_RTKIT
+config MFD_MACSMC_CORE
+	tristate "Apple System Management Controller (SMC) Core"
 	select MFD_CORE
 	help
-	  The System Management Controller (SMC) on Apple Silicon machines is a
+	  The System Management Controller (SMC) on Apple machines is a
 	  piece of hardware that exposes various functionalities such as
 	  temperature sensors, voltage/power meters, shutdown/reboot handling,
 	  GPIOs and more.
 
-	  Communication happens via a shared mailbox using the RTKit protocol
-	  which is also used for other co-processors. The SMC protocol then
-	  allows reading and writing many different keys which implement the
-	  various features. The MFD core device handles this protocol and
-	  exposes it to the sub-devices.
-
 config MFD_MADERA
 	tristate "Cirrus Logic Madera codecs"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f8101d2a9..0b0ccf91e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -22,7 +22,7 @@ obj-$(CONFIG_MFD_CS42L43_SDW)	+= cs42l43-sdw.o
 obj-$(CONFIG_MFD_ENE_KB3930)	+= ene-kb3930.o
 obj-$(CONFIG_MFD_EXYNOS_LPASS)	+= exynos-lpass.o
 obj-$(CONFIG_MFD_GATEWORKS_GSC)	+= gateworks-gsc.o
-obj-$(CONFIG_MFD_MACSMC)	+= macsmc.o
+obj-$(CONFIG_MFD_MACSMC_CORE)	+= macsmc-core.o
 
 obj-$(CONFIG_MFD_TI_LP873X)	+= lp873x.o
 obj-$(CONFIG_MFD_TI_LP87565)	+= lp87565.o
diff --git a/drivers/mfd/macsmc-core.c b/drivers/mfd/macsmc-core.c
new file mode 100644
index 000000000..b8905c834
--- /dev/null
+++ b/drivers/mfd/macsmc-core.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Apple SMC core framework
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+static const struct mfd_cell apple_smc_rtkit_devs[] = {
+	MFD_CELL_NAME("macsmc-input"),
+	MFD_CELL_NAME("macsmc-power"),
+	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"),
+};
+
+/*
+ * There isn't device tree on acpi backend, so we need to use
+ * apple_smc_key_exists to register mfd devs
+ */
+static const struct mfd_cell apple_smc_acpi_common[] = {
+	MFD_CELL_NAME("macsmc-hwmon"),
+	MFD_CELL_NAME("macsmc-power"),
+};
+
+static const struct mfd_cell apple_smc_acpi_rtc[] = {
+	MFD_CELL_NAME("macsmc-rtc"),
+};
+
+int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size)
+{
+	guard(mutex)(&smc->mutex);
+	return smc->be->read(smc->be_cookie, key, buf, size);
+}
+EXPORT_SYMBOL(apple_smc_read);
+
+int apple_smc_write(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
+{
+	guard(mutex)(&smc->mutex);
+	return smc->be->write(smc->be_cookie, key, buf, size);
+}
+EXPORT_SYMBOL(apple_smc_write);
+
+int apple_smc_enter_atomic(struct apple_smc *smc)
+{
+	guard(mutex)(&smc->mutex);
+	return smc->be->enter_atomic(smc->be_cookie);
+}
+EXPORT_SYMBOL(apple_smc_enter_atomic);
+
+int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
+{
+	guard(spinlock_irqsave)(&smc->lock);
+	return smc->be->write_atomic(smc->be_cookie, key, buf, size);
+}
+EXPORT_SYMBOL(apple_smc_write_atomic);
+
+int apple_smc_rw(struct apple_smc *smc, smc_key key, const void *wbuf, size_t wsize,
+		void *rbuf, size_t rsize)
+{
+	guard(mutex)(&smc->mutex);
+	return smc->be->rw(smc->be_cookie, key, wbuf, wsize, rbuf, rsize);
+}
+EXPORT_SYMBOL(apple_smc_rw);
+
+int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key)
+{
+	guard(mutex)(&smc->mutex);
+	int ret;
+
+	ret = smc->be->get_key_by_index(smc->be_cookie, index, key);
+	*key = swab32(*key);
+
+	return ret;
+}
+EXPORT_SYMBOL(apple_smc_get_key_by_index);
+
+int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info)
+{
+	guard(mutex)(&smc->mutex);
+	return smc->be->get_key_info(smc->be_cookie, key, info);
+}
+EXPORT_SYMBOL(apple_smc_get_key_info);
+
+static void apple_smc_disable_notifications(void *data)
+{
+	struct apple_smc *smc = data;
+
+	guard(mutex)(&smc->mutex);
+	smc->be->disable_notifications(smc->be_cookie);
+}
+
+int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
+		void *cookie, struct blocking_notifier_head *event_handlers, bool is_acpi)
+{
+	struct apple_smc *smc;
+	__be32 count;
+	int ret;
+
+	smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL);
+	if (!smc)
+		return -ENOMEM;
+
+	smc->dev = dev;
+	smc->be_cookie = cookie;
+	smc->be = ops;
+	smc->is_acpi = is_acpi;
+	smc->event_handlers = event_handlers;
+	mutex_init(&smc->mutex);
+	spin_lock_init(&smc->lock);
+
+	ret = apple_smc_read_u32(smc, SMC_KEY(#KEY), &count);
+	if (ret < 0)
+		return ret;
+	smc->key_count = be32_to_cpu(count);
+
+	ret = apple_smc_get_key_by_index(smc, 0, &smc->first_key);
+	if (ret < 0)
+		return ret;
+
+	ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &smc->last_key);
+	if (ret < 0)
+		return ret;
+
+	dev_set_drvdata(dev, smc);
+
+	ret = devm_add_action_or_reset(dev, apple_smc_disable_notifications, smc);
+	if (ret)
+		return ret;
+
+	if (smc->is_acpi) {
+		ret = devm_mfd_add_devices(dev, -1, apple_smc_acpi_common,
+			ARRAY_SIZE(apple_smc_acpi_common), NULL, 0, NULL);
+		if (ret < 0)
+			return ret;
+
+		if (apple_smc_key_exists(smc, SMC_KEY(CLKO))) {
+			ret = devm_mfd_add_devices(dev, -1, apple_smc_acpi_rtc,
+				ARRAY_SIZE(apple_smc_acpi_rtc), NULL, 0, NULL);
+			if (ret < 0)
+				return ret;
+		}
+
+	} else
+		ret = devm_mfd_add_devices(dev, -1, apple_smc_rtkit_devs,
+			ARRAY_SIZE(apple_smc_rtkit_devs), NULL, 0, NULL);
+	if (ret < 0)
+		return ret;
+
+	dev_info(dev, "Successfully Initialized (%d keys First key: %p4ch Last key: %p4ch)\n",
+			smc->key_count, &smc->first_key, &smc->last_key);
+	return 0;
+}
+EXPORT_SYMBOL(apple_smc_probe);
+
+MODULE_AUTHOR("Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("Apple SMC core");
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index bce996bbe..14d62afc1 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -142,7 +142,7 @@ config POWER_RESET_MCF_RCM
 
 config POWER_RESET_MACSMC
 	tristate "Apple SMC reset/power-off driver"
-	depends on MFD_MACSMC
+	depends on MACSMC_RTKIT
 	help
 	  This driver supports reset and power-off on Apple Mac machines
 	  that implement this functionality via the SMC.
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index b89ef40df..6e944cc59 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -1193,7 +1193,7 @@ config FUEL_GAUGE_MM8013
 
 config MACSMC_POWER
 	tristate "Apple SMC Battery and Power Driver"
-	depends on MFD_MACSMC
+	depends on MFD_MACSMC_CORE
 	help
 	  This driver provides support for the battery and AC adapter on
 	  Apple Silicon machines. It exposes battery telemetry (voltage,
diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c
index ef735f863..ced37a701 100644
--- a/drivers/power/supply/macsmc-power.c
+++ b/drivers/power/supply/macsmc-power.c
@@ -859,7 +859,7 @@ static int macsmc_power_probe(struct platform_device *pdev)
 		return -ENODEV;
 
 	power->nb.notifier_call = macsmc_power_event;
-	blocking_notifier_chain_register(&smc->event_handlers, &power->nb);
+	blocking_notifier_chain_register(smc->event_handlers, &power->nb);
 
 	return 0;
 }
@@ -868,7 +868,7 @@ static void macsmc_power_remove(struct platform_device *pdev)
 {
 	struct macsmc_power *power = dev_get_drvdata(&pdev->dev);
 
-	blocking_notifier_chain_unregister(&power->smc->event_handlers, &power->nb);
+	blocking_notifier_chain_unregister(power->smc->event_handlers, &power->nb);
 }
 
 static const struct platform_device_id macsmc_power_id[] = {
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 05b9233b9..8cc27891b 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -2096,7 +2096,7 @@ config RTC_DRV_WILCO_EC
 
 config RTC_DRV_MACSMC
 	tristate "Apple Mac System Management Controller RTC"
-	depends on MFD_MACSMC
+	depends on MFD_MACSMC_CORE
 	help
 	  If you say yes here you get support for RTC functions
 	  inside Apple SPMI PMUs accessed through the SoC's
diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig
index db30c0ade..e89bc9cb4 100644
--- a/drivers/soc/apple/Kconfig
+++ b/drivers/soc/apple/Kconfig
@@ -52,6 +52,20 @@ config APPLE_TUNABLE
 	tristate
 	depends on ARCH_APPLE || COMPILE_TEST
 
+config MACSMC_RTKIT
+	tristate "Apple Silicon System Management Controller (SMC) Backend"
+	depends on ARCH_APPLE || COMPILE_TEST
+	depends on OF
+	depends on APPLE_RTKIT
+	depends on MFD_MACSMC_CORE
+	help
+	  MACSMC RTKIT allows communicatation with the System Management
+	  Controller on M-series Macs. It is required to use GPIO, control fans
+	  and to control battery start and end threshold.
+
+	  Say 'y' here if you have an Apple Soc
+
+
 endmenu
 
 endif
diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile
index 0481f7f43..7227cf69e 100644
--- a/drivers/soc/apple/Makefile
+++ b/drivers/soc/apple/Makefile
@@ -13,3 +13,5 @@ apple-sart-y = sart.o
 
 obj-$(CONFIG_APPLE_TUNABLE) += apple-tunable.o
 apple-tunable-y = tunable.o
+
+obj-$(CONFIG_MACSMC_RTKIT) += macsmc-rtkit.o
diff --git a/drivers/mfd/macsmc.c b/drivers/soc/apple/macsmc-rtkit.c
similarity index 70%
rename from drivers/mfd/macsmc.c
rename to drivers/soc/apple/macsmc-rtkit.c
index 514cba7dc..eaebeb1be 100644
--- a/drivers/mfd/macsmc.c
+++ b/drivers/soc/apple/macsmc-rtkit.c
@@ -11,7 +11,6 @@
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/math.h>
-#include <linux/mfd/core.h>
 #include <linux/mfd/macsmc.h>
 #include <linux/notifier.h>
 #include <linux/of.h>
@@ -27,14 +26,6 @@
 #define SMC_SHMEM_SIZE			0x1000
 #define SMC_MAX_SIZE			255
 
-#define SMC_MSG_READ_KEY		0x10
-#define SMC_MSG_WRITE_KEY		0x11
-#define SMC_MSG_GET_KEY_BY_INDEX	0x12
-#define SMC_MSG_GET_KEY_INFO		0x13
-#define SMC_MSG_INITIALIZE		0x17
-#define SMC_MSG_NOTIFICATION		0x18
-#define SMC_MSG_RW_KEY			0x20
-
 #define SMC_DATA			GENMASK_ULL(63, 32)
 #define SMC_WSIZE			GENMASK_ULL(31, 24)
 #define SMC_SIZE			GENMASK_ULL(23, 16)
@@ -44,24 +35,35 @@
 
 #define SMC_TIMEOUT_MS		500
 
-static const struct mfd_cell apple_smc_devs[] = {
-	MFD_CELL_NAME("macsmc-input"),
-	MFD_CELL_NAME("macsmc-power"),
-	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"),
+struct apple_smc_rtkit {
+	struct device *dev;
+
+	struct blocking_notifier_head event_handlers;
+
+	struct apple_rtkit *rtk;
+
+	struct completion init_done;
+	enum apple_smc_boot_stage boot_stage;
+
+	struct resource *sram;
+	void __iomem *sram_base;
+	struct apple_rtkit_shmem shmem;
+
+	unsigned int msg_id;
+
+	bool atomic_mode;
+	bool atomic_pending;
+	struct completion cmd_done;
+	u64 cmd_ret;
 };
 
-static int apple_smc_cmd_locked(struct apple_smc *smc, u64 cmd, u64 arg,
+static int apple_smc_rtkit_cmd_locked(struct apple_smc_rtkit *smc, u64 cmd, u64 arg,
 				  u64 size, u64 wsize, u32 *ret_data)
 {
 	u8 result;
 	int ret;
 	u64 msg;
 
-	lockdep_assert_held(&smc->mutex);
-
 	if (smc->boot_stage != APPLE_SMC_INITIALIZED)
 		return -EIO;
 	if (smc->atomic_mode)
@@ -103,15 +105,7 @@ static int apple_smc_cmd_locked(struct apple_smc *smc, u64 cmd, u64 arg,
 	return FIELD_GET(SMC_SIZE, smc->cmd_ret);
 }
 
-static int apple_smc_cmd(struct apple_smc *smc, u64 cmd, u64 arg,
-			 u64 size, u64 wsize, u32 *ret_data)
-{
-	guard(mutex)(&smc->mutex);
-
-	return apple_smc_cmd_locked(smc, cmd, arg, size, wsize, ret_data);
-}
-
-static int apple_smc_rw_locked(struct apple_smc *smc, smc_key key,
+static int apple_smc_rtkit_rw_locked(struct apple_smc_rtkit *smc, smc_key key,
 				const void *wbuf, size_t wsize,
 				void *rbuf, size_t rsize)
 {
@@ -120,8 +114,6 @@ static int apple_smc_rw_locked(struct apple_smc *smc, smc_key key,
 	int ret;
 	u64 cmd;
 
-	lockdep_assert_held(&smc->mutex);
-
 	if (rsize > SMC_MAX_SIZE)
 		return -EINVAL;
 	if (wsize > SMC_MAX_SIZE)
@@ -149,7 +141,7 @@ static int apple_smc_rw_locked(struct apple_smc *smc, smc_key key,
 		return -EINVAL;
 	}
 
-	ret = apple_smc_cmd_locked(smc, cmd, key, smc_size, smc_wsize, &rdata);
+	ret = apple_smc_rtkit_cmd_locked(smc, cmd, key, smc_size, smc_wsize, &rdata);
 	if (ret < 0)
 		return ret;
 
@@ -169,48 +161,42 @@ static int apple_smc_rw_locked(struct apple_smc *smc, smc_key key,
 	return ret;
 }
 
-int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size)
+static int apple_smc_rtkit_read(void *cookie, smc_key key, void *buf, size_t size)
 {
-	guard(mutex)(&smc->mutex);
+	struct apple_smc_rtkit *smc = cookie;
 
-	return apple_smc_rw_locked(smc, key, NULL, 0, buf, size);
+	return apple_smc_rtkit_rw_locked(smc, key, NULL, 0, buf, size);
 }
-EXPORT_SYMBOL(apple_smc_read);
 
-int apple_smc_write(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
+static int apple_smc_rtkit_write(void *cookie, smc_key key, const void *buf, size_t size)
 {
-	guard(mutex)(&smc->mutex);
+	struct apple_smc_rtkit *smc = cookie;
 
-	return apple_smc_rw_locked(smc, key, buf, size, NULL, 0);
+	return apple_smc_rtkit_rw_locked(smc, key, buf, size, NULL, 0);
 }
-EXPORT_SYMBOL(apple_smc_write);
 
-int apple_smc_rw(struct apple_smc *smc, smc_key key, const void *wbuf, size_t wsize,
+static int apple_smc_rtkit_rw(void *cookie, smc_key key, const void *wbuf, size_t wsize,
 		 void *rbuf, size_t rsize)
 {
-	guard(mutex)(&smc->mutex);
+	struct apple_smc_rtkit *smc = cookie;
 
-	return apple_smc_rw_locked(smc, key, wbuf, wsize, rbuf, rsize);
+	return apple_smc_rtkit_rw_locked(smc, key, wbuf, wsize, rbuf, rsize);
 }
-EXPORT_SYMBOL(apple_smc_rw);
 
-int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key)
+static int apple_smc_rtkit_get_key_by_index(void *cookie, int index, smc_key *key)
 {
-	int ret;
+	struct apple_smc_rtkit *smc = cookie;
 
-	ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_BY_INDEX, index, 0, 0, key);
-
-	*key = swab32(*key);
-	return ret;
+	return apple_smc_rtkit_cmd_locked(smc, SMC_MSG_GET_KEY_BY_INDEX, index, 0, 0, key);
 }
-EXPORT_SYMBOL(apple_smc_get_key_by_index);
 
-int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info)
+static int apple_smc_rtkit_get_key_info(void *cookie, smc_key key, struct apple_smc_key_info *info)
 {
+	struct apple_smc_rtkit *smc = cookie;
 	u8 key_info[6];
 	int ret;
 
-	ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_INFO, key, 0, 0, NULL);
+	ret = apple_smc_rtkit_cmd_locked(smc, SMC_MSG_GET_KEY_INFO, key, 0, 0, NULL);
 	if (ret >= 0 && info) {
 		memcpy_fromio(key_info, smc->shmem.iomem, sizeof(key_info));
 		info->size = key_info[0];
@@ -219,12 +205,9 @@ int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_
 	}
 	return ret;
 }
-EXPORT_SYMBOL(apple_smc_get_key_info);
 
-int apple_smc_enter_atomic(struct apple_smc *smc)
+static int apple_smc_rtkit_enter_atomic(void *cookie)
 {
-	guard(mutex)(&smc->mutex);
-
 	/*
 	 * Disable notifications since this is called before shutdown and no
 	 * notification handler will be able to handle the notification
@@ -233,19 +216,19 @@ int apple_smc_enter_atomic(struct apple_smc *smc)
 	 * We can't use apple_smc_write_flag here since that would try to lock
 	 * smc->mutex again.
 	 */
+	struct apple_smc_rtkit *smc = cookie;
 	const u8 flag = 0;
 
-	apple_smc_rw_locked(smc, SMC_KEY(NTAP), &flag, sizeof(flag), NULL, 0);
+	apple_smc_rtkit_rw_locked(smc, SMC_KEY(NTAP), &flag, sizeof(flag), NULL, 0);
 
 	smc->atomic_mode = true;
 
 	return 0;
 }
-EXPORT_SYMBOL(apple_smc_enter_atomic);
 
-int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
+static int apple_smc_rtkit_write_atomic(void *cookie, smc_key key, const void *buf, size_t size)
 {
-	guard(spinlock_irqsave)(&smc->lock);
+	struct apple_smc_rtkit *smc = cookie;
 	u8 result;
 	int ret;
 	u64 msg;
@@ -293,11 +276,28 @@ int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf,
 
 	return FIELD_GET(SMC_SIZE, smc->cmd_ret);
 }
-EXPORT_SYMBOL(apple_smc_write_atomic);
+
+static void apple_smc_rtkit_disable_notifications(void *cookie)
+{
+	const u8 flag = 0;
+
+	apple_smc_rtkit_write(cookie, SMC_KEY(NTAP), &flag, sizeof(flag));
+}
+
+static const struct apple_smc_backend_ops apple_smc_rtkit_backend_ops = {
+	.read = apple_smc_rtkit_read,
+	.write = apple_smc_rtkit_write,
+	.enter_atomic = apple_smc_rtkit_enter_atomic,
+	.write_atomic = apple_smc_rtkit_write_atomic,
+	.rw = apple_smc_rtkit_rw,
+	.get_key_by_index = apple_smc_rtkit_get_key_by_index,
+	.get_key_info = apple_smc_rtkit_get_key_info,
+	.disable_notifications = apple_smc_rtkit_disable_notifications,
+};
 
 static void apple_smc_rtkit_crashed(void *cookie, const void *bfr, size_t bfr_len)
 {
-	struct apple_smc *smc = cookie;
+	struct apple_smc_rtkit *smc = cookie;
 
 	smc->boot_stage = APPLE_SMC_ERROR_CRASHED;
 	dev_err(smc->dev, "SMC crashed! Your system will reboot in a few seconds...\n");
@@ -305,7 +305,7 @@ static void apple_smc_rtkit_crashed(void *cookie, const void *bfr, size_t bfr_le
 
 static int apple_smc_rtkit_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr)
 {
-	struct apple_smc *smc = cookie;
+	struct apple_smc_rtkit *smc = cookie;
 	size_t bfr_end;
 
 	if (!bfr->iova) {
@@ -332,7 +332,7 @@ static int apple_smc_rtkit_shmem_setup(void *cookie, struct apple_rtkit_shmem *b
 
 static bool apple_smc_rtkit_recv_early(void *cookie, u8 endpoint, u64 message)
 {
-	struct apple_smc *smc = cookie;
+	struct apple_smc_rtkit *smc = cookie;
 
 	if (endpoint != SMC_ENDPOINT) {
 		dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint);
@@ -368,7 +368,7 @@ static bool apple_smc_rtkit_recv_early(void *cookie, u8 endpoint, u64 message)
 
 static void apple_smc_rtkit_recv(void *cookie, u8 endpoint, u64 message)
 {
-	struct apple_smc *smc = cookie;
+	struct apple_smc_rtkit *smc = cookie;
 
 	if (endpoint != SMC_ENDPOINT) {
 		dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint);
@@ -392,32 +392,24 @@ static const struct apple_rtkit_ops apple_smc_rtkit_ops = {
 
 static void apple_smc_rtkit_shutdown(void *data)
 {
-	struct apple_smc *smc = data;
+	struct apple_smc_rtkit *smc = data;
 
 	/* Shut down SMC firmware, if it's not completely wedged */
 	if (apple_rtkit_is_running(smc->rtk))
 		apple_rtkit_quiesce(smc->rtk);
 }
 
-static void apple_smc_disable_notifications(void *data)
-{
-	struct apple_smc *smc = data;
-
-	apple_smc_write_flag(smc, SMC_KEY(NTAP), false);
-}
-
-static int apple_smc_probe(struct platform_device *pdev)
+static int apple_smc_rtkit_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct apple_smc *smc;
-	__be32 count;
+	struct apple_smc_rtkit *smc;
+	const u8 flag = 1;
 	int ret;
 
 	smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL);
 	if (!smc)
 		return -ENOMEM;
 
-	mutex_init(&smc->mutex);
 	smc->dev = &pdev->dev;
 	smc->sram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &smc->sram);
 	if (IS_ERR(smc->sram_base))
@@ -458,49 +450,32 @@ static int apple_smc_probe(struct platform_device *pdev)
 		return -EIO;
 	}
 
-	dev_set_drvdata(&pdev->dev, smc);
 	BLOCKING_INIT_NOTIFIER_HEAD(&smc->event_handlers);
 
-	ret = apple_smc_read(smc, SMC_KEY(#KEY), &count, sizeof(count));
-	if (ret >= 0 && ret != sizeof(count))
-		ret = -EINVAL;
-	if (ret < 0)
-		return dev_err_probe(smc->dev, ret, "Failed to get key count");
-	smc->key_count = be32_to_cpu(count);
-
 	/* Enable notifications */
-	apple_smc_write_flag(smc, SMC_KEY(NTAP), true);
-	ret = devm_add_action_or_reset(dev, apple_smc_disable_notifications, smc);
-	if (ret)
-		return ret;
+	apple_smc_rtkit_rw_locked(smc, SMC_KEY(NTAP), &flag, sizeof(flag), NULL, 0);
 
-	ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE,
-				   apple_smc_devs, ARRAY_SIZE(apple_smc_devs),
-				   NULL, 0, NULL);
-	if (ret)
-		return dev_err_probe(smc->dev, ret, "Failed to register sub-devices");
-
-
-	return 0;
+	return apple_smc_probe(dev, &apple_smc_rtkit_backend_ops, smc,
+			&smc->event_handlers, false);
 }
 
-static const struct of_device_id apple_smc_of_match[] = {
+static const struct of_device_id apple_smc_rtkit_of_match[] = {
 	{ .compatible = "apple,t8103-smc" },
 	{ .compatible = "apple,smc" },
 	{},
 };
-MODULE_DEVICE_TABLE(of, apple_smc_of_match);
+MODULE_DEVICE_TABLE(of, apple_smc_rtkit_of_match);
 
-static struct platform_driver apple_smc_driver = {
+static struct platform_driver apple_smc_rtkit_driver = {
 	.driver = {
-		.name = "macsmc",
-		.of_match_table = apple_smc_of_match,
+		.name = "macsmc-rtkit",
+		.of_match_table = apple_smc_rtkit_of_match,
 	},
-	.probe = apple_smc_probe,
+	.probe = apple_smc_rtkit_probe,
 };
-module_platform_driver(apple_smc_driver);
+module_platform_driver(apple_smc_rtkit_driver);
 
 MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
 MODULE_AUTHOR("Sven Peter <sven@kernel.org>");
 MODULE_LICENSE("Dual MIT/GPL");
-MODULE_DESCRIPTION("Apple SMC driver");
+MODULE_DESCRIPTION("Apple SMC RTKIT Backend driver");
diff --git a/include/linux/mfd/macsmc.h b/include/linux/mfd/macsmc.h
index cc09ecce0..4b4368b99 100644
--- a/include/linux/mfd/macsmc.h
+++ b/include/linux/mfd/macsmc.h
@@ -47,6 +47,14 @@ typedef u32 smc_key;
 #define APPLE_SMC_WRITABLE BIT(6)
 #define APPLE_SMC_FUNCTION BIT(4)
 
+#define SMC_MSG_READ_KEY                0x10
+#define SMC_MSG_WRITE_KEY               0x11
+#define SMC_MSG_GET_KEY_BY_INDEX        0x12
+#define SMC_MSG_GET_KEY_INFO            0x13
+#define SMC_MSG_INITIALIZE              0x17
+#define SMC_MSG_NOTIFICATION            0x18
+#define SMC_MSG_RW_KEY                  0x20
+
 /**
  * struct apple_smc_key_info - Information for a SMC key as returned by SMC
  * @type_code: FourCC code indicating the type for this key.
@@ -80,24 +88,41 @@ enum apple_smc_boot_stage {
 	APPLE_SMC_ERROR_CRASHED
 };
 
+/**
+ * struct apple_smc_backend_ops - SMC backend interface
+ * @read: Read SMC key data
+ * @write: Write SMC key data
+ * @enter_atomic: Enter atomic access mode
+ * @write_atomic: Atomic write to SMC key
+ * @rw: Combined write/read transaction
+ * @get_key_by_index: Resolve key by index
+ * @get_key_info: Get metadata for key
+ * @disable_notifications: Disable notfications on removal
+ */
+struct apple_smc_backend_ops {
+	int (*read)(void *cookie, smc_key key, void *buf, size_t size);
+	int (*write)(void *cookie, smc_key key, const void *buf, size_t size);
+	int (*enter_atomic)(void *cookie);
+	int (*write_atomic)(void *cookie, smc_key key, const void *buf, size_t size);
+	int (*rw)(void *cookie, smc_key key, const void *wbuf, size_t wsize,
+		  void *rbuf, size_t rsize);
+	int (*get_key_by_index)(void *cookie, int index, smc_key *key);
+	int (*get_key_info)(void *cookie, smc_key key,
+			    struct apple_smc_key_info *info);
+	void (*disable_notifications)(void *cookie);
+};
+
+
 /**
  * struct apple_smc
  * @dev: Underlying device struct for the physical backend device
  * @key_count: Number of available SMC keys
  * @first_key: First valid SMC key
  * @last_key: Last valid SMC key
- * @event_handlers: Notifier call chain for events received from SMC
- * @rtk: Pointer to Apple RTKit instance
- * @init_done: Completion for initialization
- * @boot_stage: Current boot stage of SMC
- * @sram: Pointer to SRAM resource
- * @sram_base: SRAM base address
- * @shmem: RTKit shared memory structure for SRAM
- * @msg_id: Current message id for commands, will be incremented for each command
- * @atomic_mode: Flag set when atomic mode is entered
- * @atomic_pending: Flag indicating pending atomic command
- * @cmd_done: Completion for command execution in non-atomic mode
- * @cmd_ret: Return value from SMC for last command
+ * @event_handlers: Pointer to Notifier call chain from the backend
+ * @be: Backend operations for SMC access
+ * @be_cookie: Backend context passed to ops callbacks
+ * @is_acpi: Is the backend ACPI based
  * @mutex: Mutex for non-atomic mode
  * @lock: Spinlock for atomic mode
  */
@@ -108,23 +133,11 @@ struct apple_smc {
 	smc_key first_key;
 	smc_key last_key;
 
-	struct blocking_notifier_head event_handlers;
-
-	struct apple_rtkit *rtk;
+	struct blocking_notifier_head *event_handlers;
+	const struct apple_smc_backend_ops *be;
+	void *be_cookie;
 
-	struct completion init_done;
-	enum apple_smc_boot_stage boot_stage;
-
-	struct resource *sram;
-	void __iomem *sram_base;
-	struct apple_rtkit_shmem shmem;
-
-	unsigned int msg_id;
-
-	bool atomic_mode;
-	bool atomic_pending;
-	struct completion cmd_done;
-	u64 cmd_ret;
+	bool is_acpi;
 
 	struct mutex mutex;
 	spinlock_t lock;
@@ -225,6 +238,18 @@ static inline bool apple_smc_key_exists(struct apple_smc *smc, smc_key key)
 	return apple_smc_get_key_info(smc, key, NULL) >= 0;
 }
 
+/**
+ * apple_smc_probe - Initialize Apple SMC device
+ * @dev: Device for the SMC instance
+ * @ops: Backend operations for SMC access
+ * @cookie: Opaque backend context
+ * @event_handlers: Pointer to Notifier call chain from backend
+ * @is_acpi: Is the backend ACPI based.
+ * Return: Zero on success, negative errno on error
+ */
+int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
+		void *cookie, struct blocking_notifier_head *event_handlers, bool is_acpi);
+
 #define APPLE_SMC_TYPE_OPS(type) \
 	static inline int apple_smc_read_##type(struct apple_smc *smc, smc_key key, type *p) \
 	{ \
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 02/14] platform/x86: add backend for macsmc in intel macs
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
  2026-10-29  8:51 ` [RFC PATCH 01/14] treewide: Add backend for macsmc Atharva Tiwari
@ 2026-10-29  8:51 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 03/14] watchdog: add driver for apple smc Atharva Tiwari
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:51 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Add a backend that talks to the SMC on Intel Macs over ACPI.
It supports the old I/O port interface, and also MMIO and
interrupt-driven completion, which applesmc doesn't have.
If an interrupt is missed it falls back to polling.

This is the base that lets the macsmc drivers replace
applesmc.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/platform/x86/Kconfig       |  10 +
 drivers/platform/x86/Makefile      |   1 +
 drivers/platform/x86/macsmc-acpi.c | 642 +++++++++++++++++++++++++++++
 3 files changed, 653 insertions(+)
 create mode 100644 drivers/platform/x86/macsmc-acpi.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 4851bf451..448aa2db6 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -202,6 +202,16 @@ config ADV_SWBUTTON
 	  To compile this driver as a module, choose M here. The module will
 	  be called adv_swbutton.
 
+config MACSMC_ACPI
+	tristate "Apple SMC (System Management Controller) ACPI Backend"
+	depends on MFD_MACSMC_CORE && ACPI && !SENSORS_APPLESMC
+	help
+	  This driver allows communication with the SMC (System Management Controller)
+	  on Intel-Macs. It is used to control fans, control battery, and more.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called macsmc_acpi.
+
 config APPLE_GMUX
 	tristate "Apple Gmux Driver"
 	depends on ACPI && PCI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index cab20a414..933aed4a8 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -28,6 +28,7 @@ obj-y				+= amd/
 obj-$(CONFIG_ADV_SWBUTTON)	+= adv_swbutton.o
 
 # Apple
+obj-$(CONFIG_MACSMC_ACPI)	+= macsmc-acpi.o
 obj-$(CONFIG_APPLE_GMUX)	+= apple-gmux.o
 
 # ASUS
diff --git a/drivers/platform/x86/macsmc-acpi.c b/drivers/platform/x86/macsmc-acpi.c
new file mode 100644
index 000000000..a06ec921a
--- /dev/null
+++ b/drivers/platform/x86/macsmc-acpi.c
@@ -0,0 +1,642 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Apple SMC ACPI backend
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
+ *
+ * PIO code taken from drivers/hwmon/applesmc.c:
+ * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ */
+
+#include <linux/acpi.h>
+#include <linux/byteorder/generic.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kfifo.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/unaligned.h>
+#include <linux/workqueue.h>
+
+#define SMC_IO_DATA_PORT	0
+#define SMC_IO_CMD_PORT		4
+#define SMC_IO_NR_PORTS		32
+
+#define SMC_IO_STATUS_AWAITING_DATA	BIT(0)
+#define SMC_IO_STATUS_IB_CLOSED		BIT(1)
+#define SMC_IO_STATUS_BUSY		BIT(2)
+
+#define SMC_MMIO_KEY_DATA		0
+#define SMC_MMIO_KEY_INTERRUPT_STATUS	0x4000
+#define SMC_MMIO_KEY_STATUS		0x4005
+#define SMC_MMIO_KEY_NAME		0x78
+#define SMC_MMIO_KEY_DATA_LEN		0x7D
+#define SMC_MMIO_KEY_SMC_ID		0x7E
+#define SMC_MMIO_KEY_CMD		0x7F
+#define SMC_MMIO_MIN_SIZE		0x4006
+
+#define SMC_TIMEOUT_MS			1000
+
+struct apple_smc_acpi {
+	struct device *dev;
+
+	u8 __iomem *iomem_base;
+	u32 iomem_base_addr, iomem_base_size;
+	bool is_mmio;
+
+	u16 port_base;
+	bool has_port;
+
+	bool has_interrupt;
+	bool interrupt_enabled;
+	struct completion cmd_done;
+
+	struct blocking_notifier_head event_handlers;
+	struct work_struct shutdown_work;
+	struct work_struct notifier_work;
+
+	DECLARE_KFIFO(event_fifo, u8, 64);
+	spinlock_t event_lock;
+};
+
+
+static int apple_smc_acpi_port_wait(struct apple_smc_acpi *smc, u8 val, u8 mask)
+{
+	int ms;
+
+	for (ms = SMC_TIMEOUT_MS; ms != 0; ms--) {
+		if ((inb(smc->port_base + SMC_IO_CMD_PORT) & mask) == val)
+			return 0;
+
+		fsleep(1000);
+	}
+	dev_dbg(smc->dev, "port/wait: timeout value:%u, mask:%u\n", val, mask);
+	return -ETIMEDOUT;
+}
+
+static int apple_smc_acpi_port_write_byte(struct apple_smc_acpi *smc, u8 buf)
+{
+	int ret;
+
+	ret = apple_smc_acpi_port_wait(smc, 0, SMC_IO_STATUS_IB_CLOSED) ? :
+		apple_smc_acpi_port_wait(smc, SMC_IO_STATUS_BUSY, SMC_IO_STATUS_BUSY);
+	if (ret)
+		return ret;
+
+	outb(buf, smc->port_base + SMC_IO_DATA_PORT);
+	return 0;
+}
+
+static int apple_smc_acpi_port_write_cmd(struct apple_smc_acpi *smc, u8 buf)
+{
+	int ret;
+
+	ret = apple_smc_acpi_port_wait(smc, 0, SMC_IO_STATUS_IB_CLOSED);
+	if (ret)
+		return ret;
+
+	outb(buf, smc->port_base + SMC_IO_CMD_PORT);
+	return 0;
+}
+
+static int apple_smc_acpi_port_write_key(struct apple_smc_acpi *smc, smc_key key)
+{
+	int i;
+
+	for (i = 3; i >= 0; i--) {
+		if (apple_smc_acpi_port_write_byte(smc, (u8)(key >> (i * 8))))
+			return -EIO;
+	}
+	return 0;
+}
+
+static int apple_smc_acpi_port_valid(struct apple_smc_acpi *smc)
+{
+	int ret;
+
+	ret = apple_smc_acpi_port_wait(smc, 0, SMC_IO_STATUS_BUSY);
+	if (!ret)
+		return ret;
+
+	ret = apple_smc_acpi_port_write_cmd(smc, SMC_MSG_READ_KEY);
+	if (ret)
+		return ret;
+
+	return apple_smc_acpi_port_wait(smc, 0, SMC_IO_STATUS_BUSY);
+}
+
+static int apple_smc_acpi_port_read(struct apple_smc_acpi *smc, u8 cmd, smc_key key,
+				void *buffer, size_t len)
+{
+	int ret;
+
+	ret = apple_smc_acpi_port_valid(smc);
+	if (ret)
+		return ret;
+
+	if (apple_smc_acpi_port_write_cmd(smc, cmd) || apple_smc_acpi_port_write_key(smc, key)) {
+		dev_dbg(smc->dev, "port/read: read argument failed, key: 0x%X, cmd: 0x%X\n",
+				key, cmd);
+		return -EIO;
+	}
+
+	if (apple_smc_acpi_port_write_byte(smc, len)) {
+		dev_dbg(smc->dev, "port/read: read len failed, key: 0x%X, len: 0x%lX\n", key, len);
+		return -EIO;
+	}
+
+	for (ret = 0; ret < len; ret++) {
+		if (apple_smc_acpi_port_wait(smc,
+				SMC_IO_STATUS_AWAITING_DATA | SMC_IO_STATUS_BUSY,
+				SMC_IO_STATUS_AWAITING_DATA | SMC_IO_STATUS_BUSY)) {
+			dev_dbg(smc->dev, "port/read: read failed, key: 0x%X\n", key);
+			return -EIO;
+		}
+		((u8 *)buffer)[ret] = inb(smc->port_base + SMC_IO_DATA_PORT);
+	}
+
+	return apple_smc_acpi_port_wait(smc, 0, SMC_IO_STATUS_BUSY);
+}
+
+static int apple_smc_acpi_port_write(struct apple_smc_acpi *smc, smc_key key, const void *buffer,
+		size_t len)
+{
+	int ret;
+
+	ret = apple_smc_acpi_port_valid(smc);
+	if (ret)
+		return ret;
+
+	if (apple_smc_acpi_port_write_cmd(smc, SMC_MSG_WRITE_KEY) ||
+			apple_smc_acpi_port_write_key(smc, key)) {
+		dev_dbg(smc->dev, "port/write: write argument failed, key: 0x%X\n", key);
+		return -EIO;
+	}
+
+	if (apple_smc_acpi_port_write_byte(smc, len)) {
+		dev_dbg(smc->dev, "port/write: write len failed, key: 0x%X, len: 0x%lX\n",
+				key, len);
+		return -EIO;
+	}
+
+	for (ret = 0; ret < len; ret++) {
+		if (apple_smc_acpi_port_write_byte(smc, ((const u8 *)buffer)[ret])) {
+			dev_dbg(smc->dev, "port/write: write failed, key: 0x%X\n", key);
+			return -EIO;
+		}
+	}
+	return apple_smc_acpi_port_wait(smc, 0, SMC_IO_STATUS_BUSY);
+}
+
+static int apple_smc_acpi_port_get_key_info(struct apple_smc_acpi *smc,
+			smc_key key, struct apple_smc_key_info *info)
+{
+	u8 buf[6];
+
+	if (apple_smc_acpi_port_read(smc, SMC_MSG_GET_KEY_INFO, key, buf, 6))
+		return -EIO;
+
+	if (!info)
+		return 0;
+
+	info->size = buf[0];
+	info->type_code = get_unaligned_be32(&buf[1]);
+	info->flags = buf[5];
+
+	return 0;
+}
+
+static void apple_smc_acpi_mmio_clear(struct apple_smc_acpi *smc)
+{
+	if (ioread8(smc->iomem_base + SMC_MMIO_KEY_STATUS))
+		return iowrite8(0, smc->iomem_base + SMC_MMIO_KEY_STATUS);
+}
+
+static int apple_smc_acpi_mmio_wait(struct apple_smc_acpi *smc)
+{
+	int ms = SMC_TIMEOUT_MS;
+
+	if (smc->interrupt_enabled) {
+		if (wait_for_completion_timeout(&smc->cmd_done,
+					msecs_to_jiffies(SMC_TIMEOUT_MS)) > 0)
+			return 0;
+
+		smc->interrupt_enabled = 0;
+		dev_dbg(smc->dev, "mmio/wait: interrupt missed, reverting to polling\n");
+	}
+
+	for (ms = SMC_TIMEOUT_MS; ms != 0; ms--) {
+		if (ioread8(smc->iomem_base + SMC_MMIO_KEY_STATUS) & 0x20)
+			return 0;
+
+		fsleep(1000);
+	}
+	dev_dbg(smc->dev, "mmio/wait: timeout");
+	return -ETIMEDOUT;
+}
+
+static int apple_smc_acpi_mmio_read(struct apple_smc_acpi *smc, u8 cmd, smc_key key,
+		void *buffer, size_t len)
+{
+	u8 ret;
+
+	reinit_completion(&smc->cmd_done);
+	apple_smc_acpi_mmio_clear(smc);
+	iowrite32be(key, smc->iomem_base + SMC_MMIO_KEY_NAME);
+	iowrite8(0, smc->iomem_base + SMC_MMIO_KEY_SMC_ID);
+	iowrite8(cmd, smc->iomem_base + SMC_MMIO_KEY_CMD);
+
+	if (apple_smc_acpi_mmio_wait(smc))
+		return -EIO;
+
+	ret = ioread8(smc->iomem_base + SMC_MMIO_KEY_CMD);
+	if (ret) {
+		dev_dbg(smc->dev, "mmio/read: read failed, ret: 0x%X, key: 0x%X\n", ret, key);
+		return -ret;
+	}
+
+	memcpy_fromio(buffer, smc->iomem_base + SMC_MMIO_KEY_DATA, len);
+	return 0;
+}
+
+static int apple_smc_acpi_mmio_write(struct apple_smc_acpi *smc, smc_key key, const void *buffer,
+		size_t len)
+{
+	u8 ret;
+
+	reinit_completion(&smc->cmd_done);
+	apple_smc_acpi_mmio_clear(smc);
+	iowrite32be(key, smc->iomem_base + SMC_MMIO_KEY_NAME);
+	memcpy_toio(smc->iomem_base + SMC_MMIO_KEY_DATA, buffer, len);
+	iowrite8(len, smc->iomem_base + SMC_MMIO_KEY_DATA_LEN);
+	iowrite8(0, smc->iomem_base + SMC_MMIO_KEY_SMC_ID);
+	iowrite8(SMC_MSG_WRITE_KEY, smc->iomem_base + SMC_MMIO_KEY_CMD);
+
+	if (apple_smc_acpi_mmio_wait(smc))
+		return -EIO;
+
+	ret = ioread8(smc->iomem_base + SMC_MMIO_KEY_CMD);
+	if (ret) {
+		dev_dbg(smc->dev, "mmio/write: write failed, ret: 0x%X, key: 0x%X\n", ret, key);
+		return -ret;
+	}
+
+	return 0;
+}
+
+static int apple_smc_acpi_mmio_get_key_info(struct apple_smc_acpi *smc, smc_key key,
+					struct apple_smc_key_info *info)
+{
+	u8 ret;
+
+	reinit_completion(&smc->cmd_done);
+	apple_smc_acpi_mmio_clear(smc);
+	iowrite32be(key, smc->iomem_base + SMC_MMIO_KEY_NAME);
+	iowrite8(0, smc->iomem_base + SMC_MMIO_KEY_SMC_ID);
+	iowrite8(SMC_MSG_GET_KEY_INFO, smc->iomem_base + SMC_MMIO_KEY_CMD);
+
+	if (apple_smc_acpi_mmio_wait(smc))
+		return -EIO;
+
+	ret = ioread8(smc->iomem_base + SMC_MMIO_KEY_CMD);
+	if (ret) {
+		dev_dbg(smc->dev, "mmio/get_key_info failed, ret: 0x%X, key: 0x%X\n", ret, key);
+		return -ret;
+	}
+
+
+	if (!info)
+		return 0;
+	info->type_code = cpu_to_be32(ioread32(smc->iomem_base + SMC_MMIO_KEY_DATA));
+	info->size = ioread8(smc->iomem_base + 5);
+	info->flags = ioread8(smc->iomem_base + 6);
+
+	return 0;
+}
+
+static void apple_smc_acpi_shutdown_work(struct work_struct *work)
+{
+	orderly_poweroff(true);
+}
+
+static void apple_smc_acpi_notifier_work(struct work_struct *work)
+{
+	struct apple_smc_acpi *smc =
+		container_of(work, struct apple_smc_acpi, notifier_work);
+
+	unsigned long flags;
+	uint8_t event;
+
+	for (;;) {
+		spin_lock_irqsave(&smc->event_lock, flags);
+
+		if (!kfifo_get(&smc->event_fifo, &event)) {
+			spin_unlock_irqrestore(&smc->event_lock, flags);
+			break;
+		}
+		spin_unlock_irqrestore(&smc->event_lock, flags);
+
+		blocking_notifier_call_chain(&smc->event_handlers, event, NULL);
+	}
+}
+
+static irqreturn_t apple_smc_acpi_irq(int irq, void *data)
+{
+	struct apple_smc_acpi *smc = data;
+	uint8_t status = 0, int_id = 0;
+	unsigned long flags;
+
+	if (smc->is_mmio) {
+		status = ioread8(smc->iomem_base + SMC_MMIO_KEY_INTERRUPT_STATUS);
+		if ((status & 0x20) == 0 && status != 0)
+			return IRQ_HANDLED;
+	} else {
+		status = inb(smc->port_base + SMC_IO_CMD_PORT);
+		if ((status & 0x40) == 0 && status != 0)
+			return IRQ_HANDLED;
+	}
+
+	int_id = inb(smc->port_base + 0x1F);
+	switch (int_id) {
+	case 0x4B: // SMC Command done
+		complete(&smc->cmd_done);
+		break;
+	case 0x40: // Shutdown Imminet
+		schedule_work(&smc->shutdown_work);
+		break;
+	case 0x41: // bridgeOS Crash
+		dev_crit(smc->dev, "BridgeOS Crashed!!!\n");
+		break;
+	case 0x2A: // ALS Change
+	case 0x54: // Thermal Level Changed
+	case 0x55: // PlatformFuntion
+	case 0x60: // SMS Drop
+	case 0x69: // SMS Orientation
+	case 0x6F: // SMS Shock
+	case 0x80: // PLimit Changed
+		spin_lock_irqsave(&smc->event_lock, flags);
+		if (!kfifo_put(&smc->event_fifo, int_id))
+			dev_warn_ratelimited(smc->dev, "Notification lost: %u\n", int_id);
+
+		spin_unlock_irqrestore(&smc->event_lock, flags);
+
+		schedule_work(&smc->notifier_work);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static acpi_status apple_smc_acpi_walk_resources(struct acpi_resource *res,
+	void *data)
+{
+	struct apple_smc_acpi *smc = data;
+	int ret = 0;
+
+	switch (res->type) {
+	case ACPI_RESOURCE_TYPE_IO:
+		if (!smc->has_port) {
+			if (res->data.io.address_length < SMC_IO_NR_PORTS)
+				return AE_ERROR;
+			smc->port_base = res->data.io.minimum;
+			smc->has_port = true;
+		}
+		return AE_OK;
+
+	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
+		if (!smc->is_mmio) {
+			if (res->data.fixed_memory32.address_length < SMC_MMIO_MIN_SIZE) {
+				dev_dbg(smc->dev, "found mmio support but its too small: %u\n",
+					res->data.fixed_memory32.address_length);
+				return AE_OK;
+			}
+			smc->iomem_base_addr = res->data.fixed_memory32.address;
+			smc->iomem_base_size = res->data.fixed_memory32.address_length;
+			smc->is_mmio = true;
+		}
+		return AE_OK;
+
+	case ACPI_RESOURCE_TYPE_IRQ:
+		ret = devm_request_irq(smc->dev, res->data.irq.interrupts[0],
+					apple_smc_acpi_irq,
+					0, "macsmc-acpi", smc);
+		if (ret < 0) {
+			dev_dbg(smc->dev, "Unable to register interrupts\n");
+			smc->has_interrupt = false;
+			return AE_OK;
+		}
+		smc->has_interrupt = true;
+		return AE_OK;
+
+	case ACPI_RESOURCE_TYPE_END_TAG:
+		if (smc->has_port)
+			return AE_OK;
+		else
+			return AE_NOT_FOUND;
+
+	default:
+		return AE_OK;
+	}
+}
+
+static void apple_smc_acpi_try_enable_mmio(struct apple_smc_acpi *smc)
+{
+	if (!smc->is_mmio)
+		return;
+
+	u8 ldkn;
+
+	smc->iomem_base = ioremap(smc->iomem_base_addr, smc->iomem_base_size);
+	if (!smc->iomem_base)
+		goto out;
+
+	if (ioread8(smc->iomem_base + SMC_MMIO_KEY_STATUS) == 0xFF) {
+		dev_dbg(smc->dev, "mmio enable failed: init status is 0xff\n");
+		goto out_iomem;
+	}
+
+	if (apple_smc_acpi_mmio_read(smc, SMC_MSG_READ_KEY, SMC_KEY(LDKN), &ldkn, 1)) {
+		dev_dbg(smc->dev, "mmio enable failed: ldkn read failed\n");
+		goto out_iomem;
+	}
+
+	if (ldkn < 2) {
+		dev_dbg(smc->dev, "mmio enable failed: ldkn version less than 2\n");
+		goto out_iomem;
+	}
+	return;
+
+out_iomem:
+	iounmap(smc->iomem_base);
+out:
+	smc->is_mmio = false;
+}
+
+static void apple_smc_acpi_try_enable_interrupts(struct apple_smc_acpi *smc)
+{
+	if (!smc->has_interrupt)
+		return;
+
+	const u8 enable = 1;
+	int ret;
+
+	if (smc->is_mmio)
+		ret = apple_smc_acpi_mmio_write(smc, SMC_KEY(NTOK), &enable, 1);
+	else
+		ret = apple_smc_acpi_port_write(smc, SMC_KEY(NTOK), &enable, 1);
+
+	if (ret < 0) {
+		dev_dbg(smc->dev, "Unable to enable interrupts\n");
+		smc->interrupt_enabled = 0;
+		return;
+	}
+
+	smc->interrupt_enabled = 1;
+}
+
+static int apple_smc_acpi_read(void *cookie, smc_key key, void *buf, size_t size)
+{
+	struct apple_smc_acpi *smc = cookie;
+	int ret;
+
+	if (smc->is_mmio)
+		ret = apple_smc_acpi_mmio_read(smc, SMC_MSG_READ_KEY,
+			key, buf, size);
+	else
+		ret = apple_smc_acpi_port_read(smc, SMC_MSG_READ_KEY,
+			key, buf, size);
+
+	if (ret < 0)
+		return ret;
+
+	return size;
+
+}
+
+static int apple_smc_acpi_write(void *cookie, smc_key key, const void *buf, size_t size)
+{
+	struct apple_smc_acpi *smc = cookie;
+	int ret;
+
+	if (smc->is_mmio)
+		ret = apple_smc_acpi_mmio_write(smc, key, buf, size);
+	else
+		ret = apple_smc_acpi_port_write(smc, key, buf, size);
+
+	if (ret < 0)
+		return ret;
+
+	return size;
+}
+
+static int apple_smc_acpi_get_key_by_index(void *cookie, int index, smc_key *key)
+{
+	struct apple_smc_acpi *smc = cookie;
+
+	if (smc->is_mmio)
+		return apple_smc_acpi_mmio_read(smc, SMC_MSG_GET_KEY_BY_INDEX, (smc_key) index,
+				key, 4);
+	else
+		return apple_smc_acpi_port_read(smc, SMC_MSG_GET_KEY_BY_INDEX, (smc_key) index,
+				key, 4);
+
+}
+
+static int apple_smc_acpi_get_key_info(void *cookie, smc_key key,
+		struct apple_smc_key_info *info)
+{
+	struct apple_smc_acpi *smc = cookie;
+
+	if (smc->is_mmio)
+		return apple_smc_acpi_mmio_get_key_info(smc, key, info);
+	else
+		return apple_smc_acpi_port_get_key_info(smc, key, info);
+
+}
+
+static void apple_smc_acpi_notifications(void *cookie)
+{
+	const u8 buf = 0;
+
+	apple_smc_acpi_write(cookie, SMC_KEY(NTOK), &buf, 1);
+}
+
+static const struct apple_smc_backend_ops apple_smc_acpi_be_ops = {
+	.read = apple_smc_acpi_read,
+	.write = apple_smc_acpi_write,
+	.get_key_by_index = apple_smc_acpi_get_key_by_index,
+	.get_key_info = apple_smc_acpi_get_key_info,
+	.disable_notifications = apple_smc_acpi_notifications,
+};
+
+static int apple_smc_acpi_probe(struct platform_device *pdev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+	struct apple_smc_acpi *smc;
+	acpi_status status;
+
+	smc = devm_kzalloc(&pdev->dev, sizeof(*smc), GFP_KERNEL);
+	if (!smc)
+		return -ENOMEM;
+
+	smc->dev = &pdev->dev;
+	init_completion(&smc->cmd_done);
+	status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+				apple_smc_acpi_walk_resources, smc);
+
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	apple_smc_acpi_try_enable_mmio(smc);
+	BLOCKING_INIT_NOTIFIER_HEAD(&smc->event_handlers);
+	INIT_WORK(&smc->shutdown_work, apple_smc_acpi_shutdown_work);
+	INIT_WORK(&smc->notifier_work, apple_smc_acpi_notifier_work);
+	spin_lock_init(&smc->event_lock);
+	INIT_KFIFO(smc->event_fifo);
+	apple_smc_acpi_try_enable_interrupts(smc);
+
+	return apple_smc_probe(smc->dev, &apple_smc_acpi_be_ops, smc, &smc->event_handlers, true);
+}
+
+static int apple_smc_acpi_resume(struct device *dev)
+{
+	struct apple_smc *smc = dev_get_drvdata(dev);
+	struct apple_smc_acpi *smc_acpi = smc->be_cookie;
+
+	/* NTOK may be set to 0 on resume */
+	if (smc_acpi->interrupt_enabled) {
+		smc_acpi->interrupt_enabled = 0;
+		apple_smc_acpi_try_enable_interrupts(smc_acpi);
+	}
+	return 0;
+}
+
+static const struct dev_pm_ops apple_smc_acpi_ops = {
+	.resume = apple_smc_acpi_resume,
+};
+
+static const struct acpi_device_id apple_smc_acpi_ids[] = {
+	{"APP0001", 0},
+	{"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, apple_smc_acpi_ids);
+
+static struct platform_driver apple_smc_acpi_driver = {
+	.probe = apple_smc_acpi_probe,
+	.driver = {
+		.name = "macsmc-acpi",
+		.acpi_match_table = apple_smc_acpi_ids,
+		.pm = &apple_smc_acpi_ops,
+	},
+};
+module_platform_driver(apple_smc_acpi_driver);
+
+MODULE_AUTHOR("Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("Apple SMC ACPI backend");
+MODULE_DEVICE_TABLE(acpi, apple_smc_acpi_ids);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 03/14] watchdog: add driver for apple smc
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
  2026-10-29  8:51 ` [RFC PATCH 01/14] treewide: Add backend for macsmc Atharva Tiwari
  2026-10-29  8:51 ` [RFC PATCH 02/14] platform/x86: add backend for macsmc in intel macs Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 04/14] iio/light: add new driver for macsmc Atharva Tiwari
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Add a watchdog driver for the SMC on Intel Macs. It can
reset the machine if the system stops responding. It works
on both newer and older SMC versions.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/mfd/macsmc-core.c     |  11 +++
 drivers/watchdog/Kconfig      |  11 +++
 drivers/watchdog/Makefile     |   2 +-
 drivers/watchdog/macsmc_wdt.c | 140 ++++++++++++++++++++++++++++++++++
 4 files changed, 163 insertions(+), 1 deletion(-)
 create mode 100644 drivers/watchdog/macsmc_wdt.c

diff --git a/drivers/mfd/macsmc-core.c b/drivers/mfd/macsmc-core.c
index b8905c834..1c1469828 100644
--- a/drivers/mfd/macsmc-core.c
+++ b/drivers/mfd/macsmc-core.c
@@ -33,6 +33,10 @@ static const struct mfd_cell apple_smc_acpi_rtc[] = {
 	MFD_CELL_NAME("macsmc-rtc"),
 };
 
+static const struct mfd_cell apple_smc_acpi_wdt[] = {
+	MFD_CELL_NAME("macsmc-wdt"),
+};
+
 int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size)
 {
 	guard(mutex)(&smc->mutex);
@@ -147,6 +151,13 @@ int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
 				return ret;
 		}
 
+		if (apple_smc_key_exists(smc, SMC_KEY(NATJ))) {
+			ret = devm_mfd_add_devices(dev, -1, apple_smc_acpi_wdt,
+				ARRAY_SIZE(apple_smc_acpi_wdt), NULL, 0, NULL);
+			if (ret < 0)
+				return ret;
+		}
+
 	} else
 		ret = devm_mfd_add_devices(dev, -1, apple_smc_rtkit_devs,
 			ARRAY_SIZE(apple_smc_rtkit_devs), NULL, 0, NULL);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f7d0a0c2c..854ab772f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1810,6 +1810,17 @@ config SIEMENS_SIMATIC_IPC_WDT
 	  To compile this driver as a module, choose M here: the module will be
 	  called simatic-ipc-wdt.
 
+config MACSMC_WDT
+	tristate "Apple SMC Watchdog"
+	depends on X86 && MACSMC_ACPI
+	select WATCHDOG_CORE
+	help
+	  This driver adds support for Watchdogs in Intel macs Using
+	  Apple SMC.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called macsmc_wdt.
+
 # M68K Architecture
 
 config M54xx_WATCHDOG
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 9ac406677..f142a5d8d 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -158,7 +158,7 @@ obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o
 obj-$(CONFIG_MLX_WDT) += mlx_wdt.o
 obj-$(CONFIG_KEEMBAY_WATCHDOG) += keembay_wdt.o
 obj-$(CONFIG_SIEMENS_SIMATIC_IPC_WDT) += simatic-ipc-wdt.o
-
+obj-$(CONFIG_MACSMC_WDT) += macsmc_wdt.o
 # M68K Architecture
 obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
 
diff --git a/drivers/watchdog/macsmc_wdt.c b/drivers/watchdog/macsmc_wdt.c
new file mode 100644
index 000000000..38f68d0d8
--- /dev/null
+++ b/drivers/watchdog/macsmc_wdt.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Apple SMC Watchdog Driver
+ *
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
+ */
+
+#include <linux/mfd/macsmc.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started default="
+		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int timeout = WATCHDOG_TIMEOUT; /* in secounds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+		 "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
+		 __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+struct macsmc_wdt {
+	struct apple_smc *smc;
+	bool has_oswd; /* Newer version of watchdogs in macsmc */
+	struct watchdog_device wdd;
+};
+
+static int macsmc_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
+{
+	struct macsmc_wdt *drv = watchdog_get_drvdata(wdd);
+	int ret;
+
+	if (timeout)
+		wdd->timeout = timeout;
+
+	if (drv->has_oswd) {
+		ret = apple_smc_write_u16(drv->smc, SMC_KEY(OSWD), (u16)(timeout << 8));
+		if (ret < 0)
+			return ret;
+	} else {
+		/*
+		 * NATJ = 2, makes the watchdog do force restart if NATi == 0
+		 * NATJ = 0, makes the watchdog do nothing if NATi == 0
+		 */
+		if (timeout == 0) {
+			ret = apple_smc_write_u16(drv->smc, SMC_KEY(NATJ), 0);
+			if (ret < 0)
+				return ret;
+		}
+		ret = apple_smc_write_u16(drv->smc, SMC_KEY(NATi), (u16)(timeout << 8));
+		if (ret < 0)
+			return ret;
+
+		if (timeout != 0) {
+			ret = apple_smc_write_u16(drv->smc, SMC_KEY(NATJ), 2);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+static int macsmc_wdt_start(struct watchdog_device *wdd)
+{
+	return macsmc_wdt_set_timeout(wdd, wdd->timeout);
+}
+static int macsmc_wdt_stop(struct watchdog_device *wdd)
+{
+	return macsmc_wdt_set_timeout(wdd, 0);
+}
+
+static int macsmc_wdt_ping(struct watchdog_device *wdd)
+{
+	struct macsmc_wdt *drv = watchdog_get_drvdata(wdd);
+	int ret;
+
+	if (drv->has_oswd)
+		ret = apple_smc_write_u16(drv->smc, SMC_KEY(OSWD), (u16)(wdd->timeout << 8));
+	else
+		ret = apple_smc_write_u16(drv->smc, SMC_KEY(NATi), (u16)(wdd->timeout << 8));
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static const struct watchdog_ops macsmc_wdt_ops = {
+	.owner =	THIS_MODULE,
+	.start =	macsmc_wdt_start,
+	.stop  =	macsmc_wdt_stop,
+	.set_timeout =	macsmc_wdt_set_timeout,
+	.ping =	macsmc_wdt_ping,
+};
+
+static const struct watchdog_info macsmc_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+	.identity = "Macsmc Watchdog timer",
+};
+
+static int macsmc_wdt_probe(struct platform_device *pdev)
+{
+	struct macsmc_wdt *drv;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct macsmc_wdt), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	drv->smc = dev_get_drvdata(pdev->dev.parent);
+	if (apple_smc_key_exists(drv->smc, SMC_KEY(OSWD)))
+		drv->has_oswd = true;
+	else
+		drv->has_oswd = false;
+
+	drv->wdd.ops = &macsmc_wdt_ops;
+	drv->wdd.info = &macsmc_wdt_info;
+	drv->wdd.min_timeout = 1;
+	drv->wdd.max_timeout = 255;
+	watchdog_init_timeout(&drv->wdd, timeout, &pdev->dev);
+	watchdog_set_nowayout(&drv->wdd, nowayout);
+	watchdog_set_drvdata(&drv->wdd, drv);
+
+	return devm_watchdog_register_device(&pdev->dev, &drv->wdd);
+}
+
+static struct platform_driver macsmc_wdt_driver = {
+	.probe = macsmc_wdt_probe,
+	.driver = {
+		.name = "macsmc-wdt"
+	},
+};
+module_platform_driver(macsmc_wdt_driver);
+
+MODULE_AUTHOR("Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("Apple SMC Watchdog Driver");
+MODULE_ALIAS("platform:macsmc-wdt");
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 04/14] iio/light: add new driver for macsmc
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (2 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 03/14] watchdog: add driver for apple smc Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 05/14] iio/accel: add driver for apple SMS Atharva Tiwari
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Add a driver for the ambient light sensors, exposed through
the standard IIO interface with left and right channels.

This replaces the light sensor support in applesmc.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/iio/light/Kconfig        |  11 ++
 drivers/iio/light/Makefile       |   1 +
 drivers/iio/light/macsmc-light.c | 189 +++++++++++++++++++++++++++++++
 drivers/mfd/macsmc-core.c        |  11 ++
 4 files changed, 212 insertions(+)
 create mode 100644 drivers/iio/light/macsmc-light.c

diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 462fff1dd..f84707edc 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -457,6 +457,17 @@ config LV0104CS
 	  To compile this driver as a module, choose M here:
 	  the module will be called lv0104cs.
 
+config MACSMC_LIGHT
+	tristate "Apple SMC Light Sensor"
+	depends on MACSMC_ACPI
+	help
+	  Say Y here if you have a Intel-based Mac. and you
+	  want support for light sensors.
+
+	  To compile this driver as a module, choose M here:
+	  the module will be called macsmc_light
+
+
 config MAX44000
 	tristate "MAX44000 Ambient and Infrared Proximity Sensor"
 	depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 6672ed01a..2ef4c054d 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_LTR390)		+= ltr390.o
 obj-$(CONFIG_LTR501)		+= ltr501.o
 obj-$(CONFIG_LTRF216A)		+= ltrf216a.o
 obj-$(CONFIG_LV0104CS)		+= lv0104cs.o
+obj-$(CONFIG_MACSMC_LIGHT)	+= macsmc-light.o
 obj-$(CONFIG_MAX44000)		+= max44000.o
 obj-$(CONFIG_MAX44009)		+= max44009.o
 obj-$(CONFIG_NOA1305)		+= noa1305.o
diff --git a/drivers/iio/light/macsmc-light.c b/drivers/iio/light/macsmc-light.c
new file mode 100644
index 000000000..9d4532b34
--- /dev/null
+++ b/drivers/iio/light/macsmc-light.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Apple SMC Light Sensor Driver
+ *
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
+ *
+ * Based on drivers/hwmom/applesmc.c:
+ * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ */
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/byteorder/generic.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/platform_device.h>
+#include <linux/unaligned.h>
+
+enum {
+	LEFT_LIGHT = 0,
+	RIGHT_LIGHT
+};
+
+struct macsmc_light {
+	struct apple_smc *smc;
+	struct notifier_block nb;
+	struct iio_dev *indio_dev;
+	u8 size;
+};
+
+static int macsmc_light_read_raw(struct iio_dev *indio_dev,
+		const struct iio_chan_spec *chan,
+		int *val, int *val2, long mask)
+
+{
+	struct macsmc_light *drv = iio_priv(indio_dev);
+	u8 buffer[10];
+	int ret;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;
+
+	switch (chan->channel) {
+	case LEFT_LIGHT:
+		ret = apple_smc_read(drv->smc, SMC_KEY(ALV0), buffer, drv->size);
+		if (ret < 0)
+			return ret;
+
+		if (drv->size == 10)
+			*val = be16_to_cpu(get_unaligned_be16(buffer + 6)) >> 10;
+		else
+			*val = buffer[2];
+
+		break;
+	case RIGHT_LIGHT:
+		if (drv->size == 10) {
+			*val = 0;
+			break;
+		}
+		ret = apple_smc_read(drv->smc, SMC_KEY(ALV1), buffer, drv->size);
+		if (ret < 0)
+			return ret;
+
+		*val = buffer[2];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return IIO_VAL_INT;
+}
+
+static int macsmc_light_event(struct notifier_block *nb, unsigned long event, void *data)
+{
+	struct macsmc_light *drv = container_of(nb, struct macsmc_light, nb);
+	s64 timestamp = iio_get_time_ns(drv->indio_dev);
+
+	/* check if event is for us */
+	if (event != 0x2A)
+		return NOTIFY_DONE;
+
+	iio_push_event(drv->indio_dev,
+		IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+				LEFT_LIGHT,
+				IIO_EV_TYPE_CHANGE,
+				IIO_EV_DIR_EITHER),
+		timestamp);
+
+	if (drv->size != 10) {
+		iio_push_event(drv->indio_dev,
+			IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+					RIGHT_LIGHT,
+					IIO_EV_TYPE_CHANGE,
+					IIO_EV_DIR_EITHER),
+				timestamp);
+	}
+
+	return NOTIFY_OK;
+}
+
+static void macsmc_light_unregister_notifier(void *data)
+{
+	struct macsmc_light *drv = data;
+
+	blocking_notifier_chain_unregister(drv->smc->event_handlers,
+					   &drv->nb);
+}
+
+static const struct iio_event_spec macsmc_light_events[] = {
+	{
+		.type = IIO_EV_TYPE_CHANGE,
+		.dir = IIO_EV_DIR_EITHER,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+	},
+};
+
+static const struct iio_chan_spec macsmc_light_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.channel = LEFT_LIGHT,
+		.extend_name = "left",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.event_spec = macsmc_light_events,
+		.num_event_specs = ARRAY_SIZE(macsmc_light_events),
+	},
+	{
+		.type = IIO_LIGHT,
+		.channel = RIGHT_LIGHT,
+		.extend_name = "right",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.event_spec = macsmc_light_events,
+		.num_event_specs = ARRAY_SIZE(macsmc_light_events),
+	},
+};
+
+static const struct iio_info macsmc_light_info = {
+	.read_raw = macsmc_light_read_raw,
+};
+
+static int macsmc_light_probe(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct macsmc_light *drv;
+	struct apple_smc_key_info info;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*drv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	drv = iio_priv(indio_dev);
+	drv->smc = dev_get_drvdata(pdev->dev.parent);
+
+	ret = apple_smc_get_key_info(drv->smc, SMC_KEY(ALV0), &info);
+	if (ret < 0)
+		return -ENODEV;
+
+	drv->size = info.size;
+	drv->indio_dev = indio_dev;
+	drv->nb.notifier_call = macsmc_light_event;
+	ret = blocking_notifier_chain_register(drv->smc->event_handlers, &drv->nb);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(&pdev->dev, macsmc_light_unregister_notifier, drv);
+	if (ret)
+		return ret;
+
+	indio_dev->name = "macsmc-light";
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &macsmc_light_info;
+	indio_dev->channels = macsmc_light_channels;
+	indio_dev->num_channels = ARRAY_SIZE(macsmc_light_channels);
+
+	return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static struct platform_driver macsmc_light_driver = {
+	.probe = macsmc_light_probe,
+	.driver = {
+		.name = "macsmc-light",
+	},
+};
+module_platform_driver(macsmc_light_driver);
+
+MODULE_AUTHOR("Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("Apple SMC Light Sensor Driver");
+MODULE_ALIAS("platform:macsmc-light");
diff --git a/drivers/mfd/macsmc-core.c b/drivers/mfd/macsmc-core.c
index 1c1469828..6014df4fe 100644
--- a/drivers/mfd/macsmc-core.c
+++ b/drivers/mfd/macsmc-core.c
@@ -29,6 +29,10 @@ static const struct mfd_cell apple_smc_acpi_common[] = {
 	MFD_CELL_NAME("macsmc-power"),
 };
 
+static const struct mfd_cell apple_smc_acpi_light[] = {
+	MFD_CELL_NAME("macsmc-light"),
+};
+
 static const struct mfd_cell apple_smc_acpi_rtc[] = {
 	MFD_CELL_NAME("macsmc-rtc"),
 };
@@ -158,6 +162,13 @@ int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
 				return ret;
 		}
 
+		if (apple_smc_key_exists(smc, SMC_KEY(ALV0))) {
+			ret = devm_mfd_add_devices(dev, -1, apple_smc_acpi_light,
+				ARRAY_SIZE(apple_smc_acpi_light), NULL, 0, NULL);
+			if (ret < 0)
+				return ret;
+		}
+
 	} else
 		ret = devm_mfd_add_devices(dev, -1, apple_smc_rtkit_devs,
 			ARRAY_SIZE(apple_smc_rtkit_devs), NULL, 0, NULL);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 05/14] iio/accel: add driver for apple SMS
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (3 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 04/14] iio/light: add new driver for macsmc Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 06/14] leds: add support for macsmc kbl Atharva Tiwari
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Add a driver for the Sudden Motion Sensor accelerometer,
exposed through IIO. It reports the three axes and also
drop, orientation and shock events.

This replaces the motion sensor support in applesmc.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/iio/accel/Kconfig        |  10 ++
 drivers/iio/accel/Makefile       |   1 +
 drivers/iio/accel/macsmc-accel.c | 248 +++++++++++++++++++++++++++++++
 drivers/mfd/macsmc-core.c        |  10 ++
 4 files changed, 269 insertions(+)
 create mode 100644 drivers/iio/accel/macsmc-accel.c

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 141ac9842..50bc127a4 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -532,6 +532,16 @@ config KXCJK1013
 	  To compile this driver as a module, choose M here: the module will
 	  be called kxcjk-1013.
 
+config MACSMC_ACCEL
+	tristate "Apple SMC Motion Sensor Driver"
+	depends on MACSMC_ACPI
+	help
+	  Say yes here if you want support for Apple Motion Sensors, that uses
+	  the System Management Controller.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called macsmc-accel.
+
 config MC3230
 	tristate "mCube MC3230 Digital Accelerometer Driver"
 	depends on I2C
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index fa440a859..52fe661de 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_KXSD9)	+= kxsd9.o
 obj-$(CONFIG_KXSD9_SPI)	+= kxsd9-spi.o
 obj-$(CONFIG_KXSD9_I2C)	+= kxsd9-i2c.o
 obj-$(CONFIG_MC3230)	+= mc3230.o
+obj-$(CONFIG_MACSMC_ACCEL)	+= macsmc-accel.o
 
 obj-$(CONFIG_MMA7455)		+= mma7455_core.o
 obj-$(CONFIG_MMA7455_I2C)	+= mma7455_i2c.o
diff --git a/drivers/iio/accel/macsmc-accel.c b/drivers/iio/accel/macsmc-accel.c
new file mode 100644
index 000000000..0abf81e7b
--- /dev/null
+++ b/drivers/iio/accel/macsmc-accel.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Apple SMC sudden motion sensor (SMS) driver
+ *
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
+ *
+ * Based on drivers/hwmon/applesmc.c:
+ * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ */
+
+#include <linux/delay.h>
+#include <linux/byteorder/generic.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+
+struct macsmc_accel {
+	struct apple_smc *smc;
+	struct iio_dev *indio_dev;
+	struct notifier_block nb;
+};
+
+enum {
+	SMS_DROP = 0,
+	SMS_ORIENTATION,
+	SMS_SHOCK,
+};
+
+static int macsmc_accel_read(struct macsmc_accel *drv, smc_key key, s16 *p)
+{
+	int ret;
+
+	ret = apple_smc_read_s16(drv->smc, key, p);
+	if (ret < 0)
+		return ret;
+
+	*p = (s16)be16_to_cpu(*p);
+	return 0;
+}
+
+static int macsmc_accel_dev_init(struct macsmc_accel *drv)
+{
+	int ret, total;
+	u16 buf;
+
+	for (total = 0; total < 100; total++) {
+		ret = apple_smc_read_u16(drv->smc, SMC_KEY(MOCN), &buf);
+		if (!ret && buf)
+			return 0;
+
+		buf = 0xE0;
+		apple_smc_write_u16(drv->smc, SMC_KEY(MOCN), buf);
+		msleep(50);
+	}
+
+	return -ENODEV;
+}
+
+static int macsmc_accel_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+		int *val, int *val2, long mask)
+{
+	struct macsmc_accel *drv = iio_priv(indio_dev);
+	int ret;
+	s16 buf;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;
+
+	switch (chan->channel2) {
+	case IIO_MOD_X:
+		ret = macsmc_accel_read(drv, SMC_KEY(MO_X), &buf);
+		if (ret < 0)
+			return ret;
+
+		*val = buf;
+		return IIO_VAL_INT;
+	case IIO_MOD_Y:
+		ret = macsmc_accel_read(drv, SMC_KEY(MO_Y), &buf);
+		if (ret < 0)
+			return ret;
+
+		*val = buf;
+		return IIO_VAL_INT;
+	case IIO_MOD_Z:
+		ret = macsmc_accel_read(drv, SMC_KEY(MO_Z), &buf);
+		if (ret < 0)
+			return ret;
+
+		*val = buf;
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static int macsmc_accel_event(struct notifier_block *nb, unsigned long event, void *data)
+{
+	struct macsmc_accel *drv = container_of(nb, struct macsmc_accel, nb);
+	u64 code;
+
+	/* check if event is for us */
+	if (event != 0x60 && event != 0x69 && event != 0x6F)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case 0x60:
+		code = IIO_UNMOD_EVENT_CODE(IIO_ACCEL, SMS_DROP,
+					    IIO_EV_TYPE_CHANGE,
+					    IIO_EV_DIR_NONE);
+		break;
+	case 0x69:
+		code = IIO_UNMOD_EVENT_CODE(IIO_ACCEL, SMS_ORIENTATION,
+					    IIO_EV_TYPE_CHANGE,
+					    IIO_EV_DIR_NONE);
+		break;
+	case 0x6F:
+		code = IIO_UNMOD_EVENT_CODE(IIO_ACCEL, SMS_SHOCK,
+					    IIO_EV_TYPE_CHANGE,
+					    IIO_EV_DIR_NONE);
+		break;
+	}
+
+	iio_push_event(drv->indio_dev, code, iio_get_time_ns(drv->indio_dev));
+	return NOTIFY_OK;
+}
+
+static void macsmc_accel_unregister_notifier(void *data)
+{
+	struct macsmc_accel *drv = data;
+
+	blocking_notifier_chain_unregister(drv->smc->event_handlers,
+					&drv->nb);
+}
+
+static const struct iio_event_spec macsmc_accel_event_spec = {
+	.type = IIO_EV_TYPE_CHANGE,
+	.dir = IIO_EV_DIR_NONE,
+	.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+};
+
+static const struct iio_chan_spec macsmc_accel_channels[] = {
+	{
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	},
+	{
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	},
+	{
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	},
+	{
+		.type = IIO_ACCEL,
+		.channel = SMS_DROP,
+		.event_spec = &macsmc_accel_event_spec,
+		.num_event_specs = 1,
+	},
+	{
+		.type = IIO_ACCEL,
+		.channel = SMS_ORIENTATION,
+		.event_spec = &macsmc_accel_event_spec,
+		.num_event_specs = 1,
+	},
+	{
+		.type = IIO_ACCEL,
+		.channel = SMS_SHOCK,
+		.event_spec = &macsmc_accel_event_spec,
+		.num_event_specs = 1,
+	},
+};
+
+static const struct iio_info macsmc_accel_info = {
+	.read_raw = macsmc_accel_read_raw
+};
+
+static int macsmc_accel_restore(struct device *dev)
+{
+	struct macsmc_accel *drv = dev_get_drvdata(dev);
+
+	return macsmc_accel_dev_init(drv);
+}
+
+static const struct dev_pm_ops macsmc_accel_pm_ops = {
+	.restore = macsmc_accel_restore,
+};
+
+static int macsmc_accel_probe(struct platform_device *pdev)
+{
+	struct macsmc_accel *drv;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*drv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	drv = iio_priv(indio_dev);
+	drv->smc = dev_get_drvdata(pdev->dev.parent);
+
+	ret = macsmc_accel_dev_init(drv);
+	if (ret)
+		return ret;
+
+	drv->indio_dev = indio_dev;
+	drv->nb.notifier_call = macsmc_accel_event;
+	ret = blocking_notifier_chain_register(drv->smc->event_handlers, &drv->nb);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(&pdev->dev, macsmc_accel_unregister_notifier, drv);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, drv);
+
+	indio_dev->name = "macsmc-accel";
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &macsmc_accel_info;
+	indio_dev->channels = macsmc_accel_channels;
+	indio_dev->num_channels = ARRAY_SIZE(macsmc_accel_channels);
+
+	return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static struct platform_driver macsmc_accel_driver = {
+	.probe = macsmc_accel_probe,
+	.driver = {
+		.name = "macsmc-accel",
+		.pm = &macsmc_accel_pm_ops,
+	},
+};
+module_platform_driver(macsmc_accel_driver);
+
+MODULE_AUTHOR("Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>");
+MODULE_DESCRIPTION("Apple SMC Motion Sensor Driver");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_ALIAS("platform:macsmc-accel");
diff --git a/drivers/mfd/macsmc-core.c b/drivers/mfd/macsmc-core.c
index 6014df4fe..6567d2f1f 100644
--- a/drivers/mfd/macsmc-core.c
+++ b/drivers/mfd/macsmc-core.c
@@ -24,6 +24,10 @@ static const struct mfd_cell apple_smc_rtkit_devs[] = {
  * There isn't device tree on acpi backend, so we need to use
  * apple_smc_key_exists to register mfd devs
  */
+static const struct mfd_cell apple_smc_acpi_accel[] = {
+	MFD_CELL_NAME("macsmc-accel"),
+};
+
 static const struct mfd_cell apple_smc_acpi_common[] = {
 	MFD_CELL_NAME("macsmc-hwmon"),
 	MFD_CELL_NAME("macsmc-power"),
@@ -169,6 +173,12 @@ int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
 				return ret;
 		}
 
+		if (apple_smc_key_exists(smc, SMC_KEY(MOCN))) {
+			ret = devm_mfd_add_devices(dev, -1, apple_smc_acpi_accel,
+				ARRAY_SIZE(apple_smc_acpi_accel), NULL, 0, NULL);
+			if (ret < 0)
+				return ret;
+		}
 	} else
 		ret = devm_mfd_add_devices(dev, -1, apple_smc_rtkit_devs,
 			ARRAY_SIZE(apple_smc_rtkit_devs), NULL, 0, NULL);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 06/14] leds: add support for macsmc kbl
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (4 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 05/14] iio/accel: add driver for apple SMS Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 07/14] rtc: add support for x86 macs in macsmc-rtc Atharva Tiwari
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Add a LED class driver to control the keyboard backlight.

This replaces the keyboard backlight support in applesmc.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/leds/Kconfig       | 11 ++++++
 drivers/leds/Makefile      |  1 +
 drivers/leds/leds-macsmc.c | 69 ++++++++++++++++++++++++++++++++++++++
 drivers/mfd/macsmc-core.c  | 11 ++++++
 4 files changed, 92 insertions(+)
 create mode 100644 drivers/leds/leds-macsmc.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 7ec2c8d78..30cd64d41 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -729,6 +729,17 @@ config LEDS_ADP5520
 	  To compile this driver as a module, choose M here: the module will
 	  be called leds-adp5520.
 
+config LEDS_MACSMC
+	tristate "Keyboard Backlight support for Intel-Macs"
+	depends on LEDS_CLASS
+	depends on MACSMC_ACPI
+	help
+	  This option enables support for the Keyboard backlight in
+	  Intel-Macs using macsmc.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called leds-macsmc.
+
 config LEDS_MAX5970
 	tristate "LED Support for Maxim 5970"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4d4b08915..7c38dbef4 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_LEDS_KUNIT_TEST)		+= led-test.o
 obj-$(CONFIG_LEDS_88PM860X)		+= leds-88pm860x.o
 obj-$(CONFIG_LEDS_ACER_A500)		+= leds-acer-a500.o
 obj-$(CONFIG_LEDS_ADP5520)		+= leds-adp5520.o
+obj-$(CONFIG_LEDS_MACSMC)		+= leds-macsmc.o
 obj-$(CONFIG_LEDS_AN30259A)		+= leds-an30259a.o
 obj-$(CONFIG_LEDS_APU)			+= leds-apu.o
 obj-$(CONFIG_LEDS_ARIEL)		+= leds-ariel.o
diff --git a/drivers/leds/leds-macsmc.c b/drivers/leds/leds-macsmc.c
new file mode 100644
index 000000000..f38ef2113
--- /dev/null
+++ b/drivers/leds/leds-macsmc.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Apple SMC Keyboard backlight driver
+ *
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
+ *
+ * Based on drivers/hwmon/applesmc.c
+ * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ *
+ */
+
+#include <linux/leds.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/platform_device.h>
+
+struct macsmc_led {
+	struct led_classdev cdev;
+	struct apple_smc *smc;
+	u8 brightness;
+};
+
+static enum led_brightness macsmc_led_get_brightness(struct led_classdev *cdev)
+{
+	struct macsmc_led *drv = container_of(cdev, struct macsmc_led, cdev);
+
+	return drv->brightness;
+}
+
+static void macsmc_led_set_brightness(struct led_classdev *cdev, enum led_brightness value)
+{
+	struct macsmc_led *drv = container_of(cdev, struct macsmc_led, cdev);
+
+	drv->brightness = (u8) value;
+
+	apple_smc_write_u16(drv->smc, SMC_KEY(LKSB), drv->brightness);
+}
+
+static int macsmc_led_probe(struct platform_device *pdev)
+{
+	struct macsmc_led *drv;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	drv->smc = dev_get_drvdata(pdev->dev.parent);
+
+	drv->cdev.name = "macsmc-led";
+	drv->cdev.max_brightness = 255;
+	drv->cdev.brightness_get = macsmc_led_get_brightness;
+	drv->cdev.brightness_set = macsmc_led_set_brightness;
+	drv->cdev.dev = &pdev->dev;
+
+	return devm_led_classdev_register(&pdev->dev, &drv->cdev);
+}
+
+static struct platform_driver macsmc_led_driver = {
+	.probe = macsmc_led_probe,
+	.driver = {
+		.name = "macsmc-led",
+	},
+};
+module_platform_driver(macsmc_led_driver);
+
+MODULE_AUTHOR("Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>");
+MODULE_DESCRIPTION("LED keyboard backlight driver for Intel-macs using macsmc");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_ALIAS("platform:macsmc-led");
diff --git a/drivers/mfd/macsmc-core.c b/drivers/mfd/macsmc-core.c
index 6567d2f1f..bc4200f57 100644
--- a/drivers/mfd/macsmc-core.c
+++ b/drivers/mfd/macsmc-core.c
@@ -33,6 +33,10 @@ static const struct mfd_cell apple_smc_acpi_common[] = {
 	MFD_CELL_NAME("macsmc-power"),
 };
 
+static const struct mfd_cell apple_smc_acpi_led[] = {
+	MFD_CELL_NAME("macsmc-led"),
+};
+
 static const struct mfd_cell apple_smc_acpi_light[] = {
 	MFD_CELL_NAME("macsmc-light"),
 };
@@ -179,6 +183,13 @@ int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
 			if (ret < 0)
 				return ret;
 		}
+
+		if (apple_smc_key_exists(smc, SMC_KEY(LKSB))) {
+			ret = devm_mfd_add_devices(dev, -1, apple_smc_acpi_led,
+				ARRAY_SIZE(apple_smc_acpi_led), NULL, 0, NULL);
+			if (ret < 0)
+				return ret;
+		}
 	} else
 		ret = devm_mfd_add_devices(dev, -1, apple_smc_rtkit_devs,
 			ARRAY_SIZE(apple_smc_rtkit_devs), NULL, 0, NULL);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 07/14] rtc: add support for x86 macs in macsmc-rtc
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (5 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 06/14] leds: add support for macsmc kbl Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 08/14] treewide: Add a flag to detect the Apple T2 chip Atharva Tiwari
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Make macsmc-rtc work on Intel Macs. The ACPI RTC doesn't
work correctly on T2 Macs, so the clock from the SMC is
used instead.

On these machines the time offset is kept in the SMC instead
of NVMEM, and the clock rate is read from the SMC too.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/rtc/rtc-macsmc.c | 126 ++++++++++++++++++++++++++++++++-------
 1 file changed, 106 insertions(+), 20 deletions(-)

diff --git a/drivers/rtc/rtc-macsmc.c b/drivers/rtc/rtc-macsmc.c
index 8fe883066..37395c64c 100644
--- a/drivers/rtc/rtc-macsmc.c
+++ b/drivers/rtc/rtc-macsmc.c
@@ -2,9 +2,12 @@
 /*
  * Apple SMC RTC driver
  * Copyright The Asahi Linux Contributors
+ *
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
  */
 
 #include <linux/bitops.h>
+#include <linux/delay.h>
 #include <linux/mfd/macsmc.h>
 #include <linux/module.h>
 #include <linux/nvmem-consumer.h>
@@ -20,13 +23,54 @@
 /* 32768 Hz clock */
 #define RTC_SEC_SHIFT 15
 
+static bool force_clkm;
+module_param(force_clkm, bool, 0644);
+MODULE_PARM_DESC(force_clkm, "Force CLKM Key, instead of trying to use CLKL");
+
 struct macsmc_rtc {
 	struct device *dev;
 	struct apple_smc *smc;
 	struct rtc_device *rtc_dev;
 	struct nvmem_cell *rtc_offset;
+	uint8_t shift;
+	bool async_rtc;
 };
 
+static int macsmc_rtc_read_clkl(struct macsmc_rtc *rtc, u64 *ctr)
+{
+	u64 request = 0xffffffffffffULL;
+	u64 response = 0;
+	int i, j, ret;
+
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < 40; j++) {
+			ret = apple_smc_write(rtc->smc, SMC_KEY(CLKL), &request, RTC_BYTES);
+			if (ret == RTC_BYTES)
+				break;
+		}
+		if (ret != RTC_BYTES)
+			continue;
+
+		msleep(20);
+
+		for (j = 0; j < 20; j++) {
+			ret = apple_smc_read(rtc->smc, SMC_KEY(CLKL), &response, RTC_BYTES);
+			if (ret == RTC_BYTES) {
+				if ((response & 0x7fff) == 5 || (response & 0x7fff) == 6)
+					return -EAGAIN;
+
+				if (response != 0xffffffffffffULL) {
+					*ctr = response;
+					return RTC_BYTES;
+				}
+			}
+			fsleep(1000);
+		}
+	}
+
+	return -EIO;
+}
+
 static int macsmc_rtc_get_time(struct device *dev, struct rtc_time *tm)
 {
 	struct macsmc_rtc *rtc = dev_get_drvdata(dev);
@@ -36,25 +80,36 @@ static int macsmc_rtc_get_time(struct device *dev, struct rtc_time *tm)
 	size_t len;
 	int ret;
 
-	ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES);
+	if (rtc->async_rtc && !force_clkm)
+		ret = macsmc_rtc_read_clkl(rtc, &ctr);
+	else
+		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) {
+	if (rtc->smc->is_acpi) {
+		ret = apple_smc_read(rtc->smc, SMC_KEY(CLKO), &off, RTC_BYTES);
+		if (ret < 0)
+			return ret;
+		if (ret != RTC_BYTES)
+			return -EIO;
+	} else {
+		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);
-		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;
+	now = sign_extend64(ctr + off, RTC_BITS - 1) >> rtc->shift;
 	rtc_time64_to_tm(now, tm);
 
 	return ret;
@@ -66,14 +121,20 @@ static int macsmc_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	u64 ctr = 0, off = 0;
 	int ret;
 
-	ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES);
+	if (rtc->async_rtc && !force_clkm)
+		ret = macsmc_rtc_read_clkl(rtc, &ctr);
+	else
+		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;
+	off = (rtc_tm_to_time64(tm) << rtc->shift) - ctr;
+	if (rtc->smc->is_acpi)
+		return (apple_smc_write(rtc->smc, SMC_KEY(CLKO), &off, RTC_BYTES) != RTC_BYTES);
+
 	return nvmem_cell_write(rtc->rtc_offset, &off, RTC_BYTES);
 }
 
@@ -86,13 +147,15 @@ static int macsmc_rtc_probe(struct platform_device *pdev)
 {
 	struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
 	struct macsmc_rtc *rtc;
+	int ret;
+	uint32_t clkr = 0xFFFFFFFF;
 
 	/*
 	 * 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)
+	if (!pdev->dev.of_node && !smc->is_acpi)
 		return -ENODEV;
 
 	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
@@ -102,18 +165,40 @@ static int macsmc_rtc_probe(struct platform_device *pdev)
 	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");
+	if (!rtc->smc->is_acpi) {
+		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");
+	}
+
+	if (rtc->smc->is_acpi) {
+		ret = apple_smc_read_u32(rtc->smc, SMC_KEY(CLKR), &clkr);
+		if (ret < 0 || clkr == 0xFFFFFFFF)
+			rtc->shift = RTC_SEC_SHIFT;
+		else
+			rtc->shift = ffs(clkr) - 1;
+
+		if (apple_smc_key_exists(rtc->smc, SMC_KEY(CLKL))) {
+			u64 clkl;
+
+			ret = apple_smc_read(rtc->smc, SMC_KEY(CLKL), &clkl, RTC_BYTES);
+			if (ret == RTC_BYTES && clkl != 0xFFFFFFFFFFFFFFFFULL)
+				rtc->async_rtc = true;
+		}
+	} else
+		rtc->shift = RTC_SEC_SHIFT;
+
+	dev_info(&pdev->dev, "%u clock, using %s key\n", (clkr != 0xFFFFFFFF) ? clkr : 32768,
+				rtc->async_rtc ? "CLKL" : "CLKM");
 
 	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));
+	rtc->rtc_dev->range_min = S64_MIN >> (rtc->shift + (64 - RTC_BITS));
+	rtc->rtc_dev->range_max = S64_MAX >> (rtc->shift + (64 - RTC_BITS));
 
 	platform_set_drvdata(pdev, rtc);
 
@@ -138,3 +223,4 @@ module_platform_driver(macsmc_rtc_driver);
 MODULE_LICENSE("Dual MIT/GPL");
 MODULE_DESCRIPTION("Apple SMC RTC driver");
 MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
+MODULE_ALIAS("platform:macsmc-rtc");
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 08/14] treewide: Add a flag to detect the Apple T2 chip
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (6 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 07/14] rtc: add support for x86 macs in macsmc-rtc Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 09/14] treewide: disable other rtc's in t2 macs Atharva Tiwari
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Add a flag to detect Apple T2 chips on Intel Macs.
Cache the result to avoid repeated checks. That will
be used in upcoming patches.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 arch/x86/kernel/quirks.c                      | 37 ++++++++++++++++++
 include/linux/platform_data/x86/apple.h       |  5 +++
 .../platform_certs/keyring_handler.h          |  8 ----
 security/integrity/platform_certs/load_uefi.c | 38 ++++---------------
 4 files changed, 50 insertions(+), 38 deletions(-)

diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c
index a92f18db9..6651e5d4d 100644
--- a/arch/x86/kernel/quirks.c
+++ b/arch/x86/kernel/quirks.c
@@ -664,8 +664,45 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2083, quirk_intel_purley_xeon_ras
 bool x86_apple_machine;
 EXPORT_SYMBOL(x86_apple_machine);
 
+bool has_apple_t2_chip;
+EXPORT_SYMBOL(has_apple_t2_chip);
+
+static const char * const __initconst apple_t2_models[] = {
+	"MacBookPro15,1",
+	"MacBookPro15,2",
+	"MacBookPro15,3",
+	"MacBookPro15,4",
+	"MacBookPro16,1",
+	"MacBookPro16,2",
+	"MacBookPro16,3",
+	"MacBookPro16,4",
+	"MacBookAir8,1",
+	"MacBookAir8,2",
+	"MacBookAir9,1",
+	"Macmini8,1",
+	"MacPro7,1",
+	"iMac20,1",
+	"iMac20,2",
+	"iMacPro1,1",
+};
+
 void __init early_platform_quirks(void)
 {
 	x86_apple_machine = dmi_match(DMI_SYS_VENDOR, "Apple Inc.") ||
 			    dmi_match(DMI_SYS_VENDOR, "Apple Computer, Inc.");
+
+	if (x86_apple_machine) {
+		const char *product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
+		int i;
+
+		if (!product_name)
+			return;
+
+		for (i = 0; i < ARRAY_SIZE(apple_t2_models); i++) {
+			if (!strcmp(product_name, apple_t2_models[i])) {
+				has_apple_t2_chip = true;
+				break;
+			}
+		}
+	}
 }
diff --git a/include/linux/platform_data/x86/apple.h b/include/linux/platform_data/x86/apple.h
index 079e816c3..47b27dcea 100644
--- a/include/linux/platform_data/x86/apple.h
+++ b/include/linux/platform_data/x86/apple.h
@@ -6,8 +6,13 @@
  * x86_apple_machine - whether the machine is an x86 Apple Macintosh
  */
 extern bool x86_apple_machine;
+/**
+ * has_apple_t2_chip - whether the machine has the Apple T2 chip
+ */
+extern bool has_apple_t2_chip;
 #else
 #define x86_apple_machine false
+#define has_apple_t2_chip false
 #endif
 
 #endif
diff --git a/security/integrity/platform_certs/keyring_handler.h b/security/integrity/platform_certs/keyring_handler.h
index f92895cc5..a355f4400 100644
--- a/security/integrity/platform_certs/keyring_handler.h
+++ b/security/integrity/platform_certs/keyring_handler.h
@@ -45,11 +45,3 @@ efi_element_handler_t get_handler_for_code_signing_keys(const efi_guid_t *sig_ty
 efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type);
 
 #endif
-
-#ifndef UEFI_QUIRK_SKIP_CERT
-#define UEFI_QUIRK_SKIP_CERT(vendor, product) \
-		 .matches = { \
-			DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
-			DMI_MATCH(DMI_PRODUCT_NAME, product), \
-		},
-#endif
diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c
index c0d694844..b4096d4c8 100644
--- a/security/integrity/platform_certs/load_uefi.c
+++ b/security/integrity/platform_certs/load_uefi.c
@@ -3,42 +3,16 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/cred.h>
-#include <linux/dmi.h>
 #include <linux/err.h>
 #include <linux/efi.h>
 #include <linux/slab.h>
 #include <linux/ima.h>
+#include <linux/platform_data/x86/apple.h>
 #include <keys/asymmetric-type.h>
 #include <keys/system_keyring.h>
 #include "../integrity.h"
 #include "keyring_handler.h"
 
-/*
- * On T2 Macs reading the db and dbx efi variables to load UEFI Secure Boot
- * certificates causes occurrence of a page fault in Apple's firmware and
- * a crash disabling EFI runtime services. The following quirk skips reading
- * these variables.
- */
-static const struct dmi_system_id uefi_skip_cert[] = {
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,1") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,2") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,3") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,4") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,1") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,2") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,3") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,4") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,1") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,2") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir9,1") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "Macmini8,1") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacPro7,1") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,1") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,2") },
-	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMacPro1,1") },
-	{ }
-};
-
 /*
  * Look to see if a UEFI variable called MokIgnoreDB exists and return true if
  * it does.
@@ -165,10 +139,14 @@ static int __init load_uefi_certs(void)
 	unsigned long dbsize = 0, dbxsize = 0, mokxsize = 0;
 	efi_status_t status;
 	int rc = 0;
-	const struct dmi_system_id *dmi_id;
 
-	dmi_id = dmi_first_match(uefi_skip_cert);
-	if (dmi_id) {
+	/*
+	 * On T2 Macs reading the db and dbx efi variables to load UEFI Secure Boot
+	 * certificates causes occurrence of a page fault in Apple's firmware and
+	 * a crash disabling EFI runtime services. The following quirk skips reading
+	 * these variables.
+	 */
+	if (has_apple_t2_chip) {
 		pr_err("Reading UEFI Secure Boot Certs is not supported on T2 Macs.\n");
 		return false;
 	}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 09/14] treewide: disable other rtc's in t2 macs
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (7 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 08/14] treewide: Add a flag to detect the Apple T2 chip Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 10/14] power/supply: add support for x86 macs in macsmc-power Atharva Tiwari
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

The CMOS RTC and the ACPI TAD clock don't work correctly on
T2 Macs. When macsmc-rtc is enabled, turn them off on these
machines so macsmc-rtc is the only clock.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/acpi/acpi_tad.c | 4 ++++
 drivers/rtc/rtc-cmos.c  | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
index 45459a4ca..a137a39e5 100644
--- a/drivers/acpi/acpi_tad.c
+++ b/drivers/acpi/acpi_tad.c
@@ -28,6 +28,7 @@
 #include <linux/ktime.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/platform_data/x86/apple.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/rtc.h>
@@ -845,6 +846,9 @@ static int acpi_tad_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	if (IS_ENABLED(CONFIG_RTC_DRV_MACSMC) && has_apple_t2_chip)
+		caps &= ~ACPI_TAD_RT;
+
 	if (!acpi_has_method(handle, "_PRW")) {
 		dev_info(dev, "Missing _PRW\n");
 		caps &= ~(ACPI_TAD_AC_WAKE | ACPI_TAD_DC_WAKE);
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index fa04ece15..bdb436e24 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -33,6 +33,7 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
+#include <linux/platform_data/x86/apple.h>
 #include <linux/platform_device.h>
 #include <linux/log2.h>
 #include <linux/pm.h>
@@ -944,6 +945,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
 		.priv = &cmos_rtc,
 	};
 
+	if (IS_ENABLED(CONFIG_RTC_DRV_MACSMC) && has_apple_t2_chip)
+		return -ENODEV;
+
 	/* there can be only one ... */
 	if (cmos_rtc.dev)
 		return -EBUSY;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 10/14] power/supply: add support for x86 macs in macsmc-power
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (8 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 09/14] treewide: disable other rtc's in t2 macs Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 11/14] acpi: disable battery and adapter for x86 apple machines Atharva Tiwari
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Make macsmc-power work on Intel Macs, as a replacement for
acpi-sbs. It exposes a lot more sensors than acpi-sbs, such
as voltage, current, power, temperature, charge and health
info, and also the adapter state.

It also adds a charge limit, which acpi-sbs doesn't offer.

And now we have power stats on non-mobile macs as well (iMacs,
MacPros,etc), using macsmc-ac.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/power/supply/macsmc-power.c | 751 ++++++++++++++++++++++------
 1 file changed, 606 insertions(+), 145 deletions(-)

diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c
index ced37a701..1d64d8185 100644
--- a/drivers/power/supply/macsmc-power.c
+++ b/drivers/power/supply/macsmc-power.c
@@ -7,8 +7,11 @@
  * on Apple Silicon systems.
  *
  * Copyright The Asahi Linux Contributors
+ *
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
  */
 
+#include <linux/acpi.h>
 #include <linux/ctype.h>
 #include <linux/delay.h>
 #include <linux/devm-helpers.h>
@@ -80,12 +83,16 @@ struct macsmc_power {
 	char serial_number[MAX_STRING_LENGTH];
 	char mfg_date[MAX_STRING_LENGTH];
 
+	bool has_battery;
+
 	/* Supported feature flags based on SMC key presence */
 	bool has_chwa; /* Charge limit (Modern firmware) */
-	bool has_chls; /* Charge limit (Older firmware) */
+	bool has_chls; /* Charge limit (Older firmware And New firmware on Intel macs) */
 	bool has_ch0i; /* Force discharge (Older firmware) */
 	bool has_ch0c; /* Inhibit charge (Older firmware) */
 	bool has_chte; /* Inhibit charge (Modern firmware) */
+	bool has_pd0r; /* AC Adapter current power in watts (Old Intel macs) */
+
 	/*
 	 * Battery critical key is 1 byte and charge key is little endian
 	 * (Modern firmware)
@@ -94,7 +101,9 @@ struct macsmc_power {
 
 	u8 num_cells;
 	int nominal_voltage_mv;
+	unsigned long long adapter_status;
 
+	struct acpi_device *adapter;
 	struct notifier_block nb;
 	struct work_struct critical_work;
 	bool emergency_shutdown_triggered;
@@ -195,6 +204,37 @@ static int macsmc_battery_get_status(struct macsmc_power *power)
 	return POWER_SUPPLY_STATUS_CHARGING;
 }
 
+static int macsmc_battery_acpi_get_status(struct macsmc_power *power)
+{
+	int ret;
+	s16 vs16;
+	u16 vu16;
+
+	ret = apple_smc_read_s16(power->smc, SMC_KEY(B0AC), &vs16);
+	if (ret < 0)
+		return ret;
+
+	ret = apple_smc_read_u16(power->smc, SMC_KEY(BRSC), &vu16);
+	if (ret < 0)
+		return ret;
+
+	vu16 = swab16(vu16);
+	vs16 = (s16)swab16(vs16);
+
+	if (power->adapter_status) {
+		if (vu16 == 100 && vs16 <= 0)
+			return POWER_SUPPLY_STATUS_FULL;
+
+		if (vs16 > 0)
+			return POWER_SUPPLY_STATUS_CHARGING;
+
+		return POWER_SUPPLY_STATUS_NOT_CHARGING;
+	}
+
+	return POWER_SUPPLY_STATUS_DISCHARGING;
+}
+
+
 static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power)
 {
 	int ret;
@@ -322,6 +362,198 @@ static int macsmc_battery_get_capacity_level(struct macsmc_power *power)
 		return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
 }
 
+static int macsmc_battery_acpi_get_capacity_level(struct macsmc_power *power)
+{
+	int ret;
+	u16 vu16;
+
+	ret = apple_smc_read_u16(power->smc, SMC_KEY(BRSC), &vu16);
+	if (ret < 0)
+		return ret;
+
+	vu16 = swab16(vu16);
+
+	if (vu16 <= 5)
+		return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+	if (vu16 <= 20)
+		return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+	if (vu16 <= 80)
+		return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+	if (vu16 < 100)
+		return POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+
+	return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+}
+
+static int macsmc_battery_acpi_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 & GENMASK(22, 0)) | BIT(23));
+	exp = ((fval >> 23) & 0xff) - 127 - 23;
+
+	val *= (u64)scale;
+
+	if (exp > 63)
+		val = U64_MAX;
+	else if (exp < -63)
+		val = 0;
+	else if (exp < 0)
+		val >>= -exp;
+	else if (exp != 0 &&
+		 (val & ~((1ULL << (64 - exp)) - 1)))
+		val = U64_MAX;
+	else
+		val <<= exp;
+
+	if (fval & BIT(31)) {
+		if (val > (u64)INT_MAX + 1)
+			*p = INT_MIN;
+		else
+			*p = -(int)val;
+	} else {
+		if (val > INT_MAX)
+			*p = INT_MAX;
+		else
+			*p = (int)val;
+	}
+
+	return 0;
+}
+
+static int macsmc_battery_acpi_read(struct apple_smc *smc, smc_key key, int *val,
+	int scale)
+{
+	int ret;
+	struct apple_smc_key_info info;
+
+	ret = apple_smc_get_key_info(smc, key, &info);
+	if (ret)
+		return ret;
+
+	switch (info.type_code) {
+	/* 32-bit IEEE 754 float */
+	case __SMC_KEY('f', 'l', 't', ' '): {
+		int flt_ = 0;
+
+		ret = macsmc_battery_acpi_read_f32_scaled(smc, key, &flt_, scale);
+		if (ret)
+			return ret;
+
+		*val = flt_;
+		break;
+	}
+
+	/* 3.13 fixed point decimal */
+	case __SMC_KEY('f', 'p', '3', 'd'): {
+		u16 fp3d;
+
+		ret = apple_smc_read_u16(smc, key, &fp3d);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp3d), scale, BIT(13));
+		break;
+	}
+
+	/* 5.11 fixed point decimal */
+	case __SMC_KEY('f', 'p', '5', 'b'): {
+		u16 fp5b;
+
+		ret = apple_smc_read_u16(smc, key, &fp5b);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp5b), scale, BIT(11));
+		break;
+	}
+
+	/* 8.8 fixed point decimal */
+	case __SMC_KEY('f', 'p', '8', '8'): {
+		u16 fp88;
+
+		ret = apple_smc_read_u16(smc, key, &fp88);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp88), scale, BIT(8));
+		break;
+	}
+
+	/* 3.12 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '3', 'c'): {
+		s16 sp3c;
+
+		ret = apple_smc_read_s16(smc, key, &sp3c);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp3c), scale, BIT(12));
+		break;
+	}
+
+	/* 4.11 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '4', 'b'): {
+		s16 sp4b;
+
+		ret = apple_smc_read_s16(smc, key, &sp4b);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp4b), scale, BIT(11));
+		break;
+	}
+
+	/* 5.10 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '5', 'a'): {
+		s16 sp5a;
+
+		ret = apple_smc_read_s16(smc, key, &sp5a);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp5a), scale, BIT(10));
+		break;
+	}
+
+	/* 6.9 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '6', '9'): {
+		s16 sp69;
+
+		ret = apple_smc_read_s16(smc, key, &sp69);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp69), scale, BIT(9));
+		break;
+	}
+
+	/* 7.8 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '7', '8'): {
+		s16 sp78;
+
+		ret = apple_smc_read_s16(smc, key, &sp78);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp78), scale, BIT(8));
+		break;
+	}
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
 static s16 macsmc_swap_b0rm(struct macsmc_power *power, s16 b0rm)
 {
 	/* B0RM was Big Endian, likely pass through from TI gas gauge */
@@ -343,7 +575,10 @@ static int macsmc_battery_get_property(struct power_supply *psy,
 
 	switch (psp) {
 	case POWER_SUPPLY_PROP_STATUS:
-		val->intval = macsmc_battery_get_status(power);
+		if (power->smc->is_acpi)
+			val->intval = macsmc_battery_acpi_get_status(power);
+		else
+			val->intval = macsmc_battery_get_status(power);
 		ret = val->intval < 0 ? val->intval : 0;
 		break;
 	case POWER_SUPPLY_PROP_PRESENT:
@@ -359,36 +594,71 @@ static int macsmc_battery_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TF), &vu16);
-		val->intval = vu16 == 0xffff ? 0 : vu16 * 60;
+		if (power->smc->is_acpi)
+			val->intval = vu16 == 0xffff ? 0 : swab16(vu16) * 60;
+		else
+			val->intval = vu16 == 0xffff ? 0 : vu16 * 60;
 		break;
 	case POWER_SUPPLY_PROP_CAPACITY:
-		ret = apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &vu8);
-		val->intval = vu8;
+		if (power->smc->is_acpi) {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(BRSC), &vu16);
+			val->intval = swab16(vu16);
+		} else {
+			ret = apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &vu8);
+			val->intval = vu8;
+		}
 		break;
 	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-		val->intval = macsmc_battery_get_capacity_level(power);
+		if (power->smc->is_acpi)
+			val->intval = macsmc_battery_acpi_get_capacity_level(power);
+		else
+			val->intval = macsmc_battery_get_capacity_level(power);
 		ret = val->intval < 0 ? val->intval : 0;
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16);
-		val->intval = vu16 * 1000;
+		if (power->smc->is_acpi)
+			val->intval = swab16(vu16) * 1000;
+		else
+			val->intval = vu16 * 1000;
 		break;
 	case POWER_SUPPLY_PROP_CURRENT_NOW:
 		ret = apple_smc_read_s16(power->smc, SMC_KEY(B0AC), &vs16);
-		val->intval = vs16 * 1000;
+		if (power->smc->is_acpi)
+			val->intval = (s16)swab16(vs16) * 1000;
+		else
+			val->intval = vs16 * 1000;
 		break;
 	case POWER_SUPPLY_PROP_POWER_NOW:
-		ret = apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &vs32);
-		val->intval = vs32 * 1000;
+		if (power->smc->is_acpi) {
+			ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(B0AP),
+					&val->intval, 1000000);
+			if (ret < 0)
+				return ret;
+
+			val->intval = -val->intval;
+			}
+		else {
+			ret = apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &vs32);
+			val->intval = vs32 * 1000;
+		}
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(BITV), &vu16);
-		val->intval = vu16 * 1000;
+		if (power->smc->is_acpi)
+			val->intval = swab16(vu16) * 1000;
+		else
+			val->intval = vu16 * 1000;
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
 		/* Calculate total max design voltage from per-cell maximum voltage */
-		ret = apple_smc_read_u16(power->smc, SMC_KEY(BVVN), &vu16);
-		val->intval = vu16 * 1000 * power->num_cells;
+		if (power->smc->is_acpi) {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(BCMV), &vu16);
+			val->intval = swab16(vu16) * 1000 * power->num_cells;
+		} else {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(BVVN), &vu16);
+			val->intval = vu16 * 1000 * power->num_cells;
+		}
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
 		/* Lifetime min */
@@ -400,17 +670,36 @@ static int macsmc_battery_get_property(struct power_supply *psy,
 		ret = apple_smc_read_s16(power->smc, SMC_KEY(BLPX), &vs16);
 		val->intval = vs16 * 1000;
 		break;
+	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+		if (power->has_chls) {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16);
+			val->intval = vu16 & 0xFF;
+		} else {
+			ret = apple_smc_read_u8(power->smc, SMC_KEY(BCLM), &vu8);
+			val->intval = vu8;
+		}
+		break;
 	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RC), &vu16);
 		val->intval = vu16 * 1000;
 		break;
 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
-		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RI), &vu16);
-		val->intval = vu16 * 1000;
+		if (power->smc->is_acpi) {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(CHBI), &vu16);
+			val->intval = swab16(vu16) * 1000;
+		} else {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RI), &vu16);
+			val->intval = vu16 * 1000;
+		}
 		break;
 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RV), &vu16);
-		val->intval = vu16 * 1000;
+		if (power->smc->is_acpi) {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(CHBV), &vu16);
+			val->intval = swab16(vu16) * 1000;
+		} else {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RV), &vu16);
+			val->intval = vu16 * 1000;
+		}
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16);
@@ -418,11 +707,17 @@ static int macsmc_battery_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_FULL:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16);
-		val->intval = vu16 * 1000;
+		if (power->smc->is_acpi)
+			val->intval = swab16(vu16) * 1000;
+		else
+			val->intval = vu16 * 1000;
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_NOW:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16);
-		val->intval = macsmc_swap_b0rm(power, vu16) * 1000;
+		if (power->smc->is_acpi)
+			val->intval = swab16(vu16) * 1000;
+		else
+			val->intval = macsmc_swap_b0rm(power, vu16) * 1000;
 		break;
 	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16);
@@ -430,15 +725,37 @@ static int macsmc_battery_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_ENERGY_FULL:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16);
-		val->intval = vu16 * power->nominal_voltage_mv;
+		if (power->smc->is_acpi) {
+			if (ret < 0)
+				return ret;
+
+			u16 voltage;
+
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &voltage);
+			val->intval = swab16(vu16) * swab16(voltage);
+		} else
+			val->intval = vu16 * power->nominal_voltage_mv;
 		break;
 	case POWER_SUPPLY_PROP_ENERGY_NOW:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16);
-		val->intval = macsmc_swap_b0rm(power, vu16) * power->nominal_voltage_mv;
+		if (power->smc->is_acpi) {
+			if (ret < 0)
+				return ret;
+
+			u16 voltage;
+
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &voltage);
+			val->intval = swab16(vu16) * swab16(voltage);
+		} else
+			val->intval = macsmc_swap_b0rm(power, vu16) * power->nominal_voltage_mv;
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
-		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16);
-		val->intval = vu16 - 2732; /* Kelvin x10 to Celsius x10 */
+		if (power->smc->is_acpi)
+			ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(TB0T), &val->intval, 10);
+		else {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16);
+			val->intval = vu16 - 2732; /* Kelvin x10 to Celsius x10 */
+		}
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
 		ret = apple_smc_read_s64(power->smc, SMC_KEY(BAAC), &vs64);
@@ -446,7 +763,10 @@ static int macsmc_battery_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_CYCLE_COUNT:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16);
-		val->intval = vu16;
+		if (power->smc->is_acpi)
+			val->intval = swab16(vu16);
+		else
+			val->intval = vu16;
 		break;
 	case POWER_SUPPLY_PROP_SCOPE:
 		val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
@@ -489,6 +809,15 @@ static int macsmc_battery_set_property(struct power_supply *psy,
 	switch (psp) {
 	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
 		return macsmc_battery_set_charge_behaviour(power, val->intval);
+	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+		if (val->intval > 100 || val->intval < 1)
+			return -EINVAL;
+		if (power->has_chls) {
+			u16 vu16 = val->intval & 0xFF;
+
+			return apple_smc_write_u16(power->smc, SMC_KEY(CHLS), vu16);
+		}
+		return apple_smc_write_u8(power->smc, SMC_KEY(BCLM), (u8) val->intval);
 	default:
 		return -EINVAL;
 	}
@@ -500,6 +829,8 @@ static int macsmc_battery_property_is_writeable(struct power_supply *psy,
 	switch (psp) {
 	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
 		return true;
+	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+		return true;
 	default:
 		return false;
 	}
@@ -524,12 +855,47 @@ static int macsmc_ac_get_property(struct power_supply *psy,
 
 	switch (psp) {
 	case POWER_SUPPLY_PROP_ONLINE:
-		ret = apple_smc_read_u32(power->smc, SMC_KEY(CHIS), &vu32);
-		val->intval = !!vu32;
+		if (power->smc->is_acpi) {
+			if (power->has_battery)
+				val->intval = power->adapter_status;
+			else
+				val->intval = 1;
+		} else {
+			ret = apple_smc_read_u32(power->smc, SMC_KEY(CHIS), &vu32);
+			val->intval = !!vu32;
+		}
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		if (power->has_battery)
+			ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(ID0R),
+					&val->intval, 1000000);
+		else
+			ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(ID2R),
+					&val->intval, 1000000);
+		break;
+	case POWER_SUPPLY_PROP_POWER_NOW:
+		if (power->has_pd0r)
+			ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(PD0R),
+					&val->intval, 1000000);
+		else if (power->has_battery)
+			ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(PDTR),
+					&val->intval, 1000000);
+		else
+			ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(PD2R),
+					&val->intval, 1000000);
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16);
-		val->intval = vu16 * 1000;
+		if (power->smc->is_acpi) {
+			if (power->has_battery)
+				ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(VD0R),
+						&val->intval, 1000000);
+			else
+				ret = macsmc_battery_acpi_read(power->smc, SMC_KEY(VD2R),
+						&val->intval, 1000000);
+		} else {
+			ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16);
+			val->intval = vu16 * 1000;
+		}
 		break;
 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &vu16);
@@ -537,7 +903,10 @@ static int macsmc_ac_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT:
 		ret = apple_smc_read_u32(power->smc, SMC_KEY(ACPW), &vu32);
-		val->intval = vu32 * 1000;
+		if (power->smc->is_acpi)
+			val->intval = swab32(vu32) * 1000;
+		else
+			val->intval = vu32 * 1000;
 		break;
 	default:
 		return -EINVAL;
@@ -634,6 +1003,23 @@ static int macsmc_power_event(struct notifier_block *nb, unsigned long event, vo
 	return NOTIFY_DONE;
 }
 
+static void macsmc_power_acpi_notify(acpi_handle handle, u32 event, void *context)
+{
+	struct macsmc_power *power = context;
+
+	if (event == 0x80) {
+		acpi_status status = acpi_evaluate_integer(power->adapter->handle, "_PSR", NULL,
+								&power->adapter_status);
+		if (ACPI_FAILURE(status))
+			return;
+
+		power->adapter_status = !!power->adapter_status;
+		power_supply_changed(power->batt);
+		power_supply_changed(power->ac);
+	}
+}
+
+
 static int macsmc_power_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -670,15 +1056,19 @@ static int macsmc_power_probe(struct platform_device *pdev)
 	 * Check for battery presence.
 	 * B0AV is a fundamental key.
 	 */
+	if (smc->is_acpi && apple_smc_read_flag(power->smc, SMC_KEY(BBIN), &flag) == 0 && flag)
+		has_battery = true;
+
 	if (apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16) == 0 &&
 	    macsmc_battery_get_status(power) > POWER_SUPPLY_STATUS_UNKNOWN)
 		has_battery = true;
 
+	power->has_battery = has_battery;
 	/*
 	 * Check for AC adapter presence.
 	 * CHIS is a fundamental key.
 	 */
-	if (apple_smc_key_exists(smc, SMC_KEY(CHIS)))
+	if (apple_smc_key_exists(smc, SMC_KEY(CHIS)) || smc->is_acpi)
 		has_ac_adapter = true;
 
 	if (!has_battery && !has_ac_adapter)
@@ -694,118 +1084,156 @@ static int macsmc_power_probe(struct platform_device *pdev)
 
 		nprops = 0;
 
-		/* Fundamental properties */
-		props[nprops++] = POWER_SUPPLY_PROP_STATUS;
-		props[nprops++] = POWER_SUPPLY_PROP_PRESENT;
-		props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
-		props[nprops++] = POWER_SUPPLY_PROP_CURRENT_NOW;
-		props[nprops++] = POWER_SUPPLY_PROP_POWER_NOW;
-		props[nprops++] = POWER_SUPPLY_PROP_CAPACITY;
-		props[nprops++] = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
-		props[nprops++] = POWER_SUPPLY_PROP_TEMP;
-		props[nprops++] = POWER_SUPPLY_PROP_CYCLE_COUNT;
-		props[nprops++] = POWER_SUPPLY_PROP_HEALTH;
-		props[nprops++] = POWER_SUPPLY_PROP_SCOPE;
-		props[nprops++] = POWER_SUPPLY_PROP_MODEL_NAME;
-		props[nprops++] = POWER_SUPPLY_PROP_SERIAL_NUMBER;
-		props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_YEAR;
-		props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_MONTH;
-		props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_DAY;
-
-		/* Extended properties usually present */
-		props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW;
-		props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW;
-		props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
-		props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
-		props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
-		props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
-		props[nprops++] = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT;
-		props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
-		props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
-		props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
-		props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL;
-		props[nprops++] = POWER_SUPPLY_PROP_CHARGE_NOW;
-		props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
-		props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL;
-		props[nprops++] = POWER_SUPPLY_PROP_ENERGY_NOW;
-		props[nprops++] = POWER_SUPPLY_PROP_CHARGE_COUNTER;
-
-		/* Detect features based on key availability */
-		if (apple_smc_key_exists(smc, SMC_KEY(CHTE)))
-			power->has_chte = true;
-		if (apple_smc_key_exists(smc, SMC_KEY(CH0C)))
-			power->has_ch0c = true;
-		if (apple_smc_key_exists(smc, SMC_KEY(CH0I)))
-			power->has_ch0i = true;
-
-		ret = apple_smc_get_key_info(power->smc, SMC_KEY(BCF0), &info);
-		if (ret) {
-			dev_err(&pdev->dev, "Failed to determine BCF0 key size\n");
-			return ret;
-		}
-		if (info.size == 1)
-			power->fw_ge_27 = true;
-		else if (info.size == 4)
-			power->fw_ge_27 = false;
-		else {
-			dev_err(&pdev->dev, "Unexpected BCF0 key size %d\n", info.size);
-			return -EIO;
-		}
-
-		/* Reset "Optimised Battery Charging" flags to default state */
-		if (power->has_chte)
-			apple_smc_write_u32(smc, SMC_KEY(CHTE), 0);
-		else if (power->has_ch0c)
-			apple_smc_write_u8(smc, SMC_KEY(CH0C), 0);
-
-		if (power->has_ch0i)
-			apple_smc_write_u8(smc, SMC_KEY(CH0I), 0);
-
-		apple_smc_write_u8(smc, SMC_KEY(CH0K), 0);
-		apple_smc_write_u8(smc, SMC_KEY(CH0B), 0);
-
-		/* Configure charge behaviour if supported */
-		if (power->has_ch0i || power->has_ch0c || power->has_chte) {
-			props[nprops++] = POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR;
-
-			power->batt_desc.charge_behaviours =
-				BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO);
+		if (smc->is_acpi) {
+			props[nprops++] = POWER_SUPPLY_PROP_STATUS;
+			props[nprops++] = POWER_SUPPLY_PROP_PRESENT;
+			props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_CURRENT_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_CAPACITY;
+			props[nprops++] = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
+			props[nprops++] = POWER_SUPPLY_PROP_TEMP;
+			props[nprops++] = POWER_SUPPLY_PROP_CYCLE_COUNT;
+			props[nprops++] = POWER_SUPPLY_PROP_HEALTH;
+			props[nprops++] = POWER_SUPPLY_PROP_SCOPE;
+			props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
+			props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
+			props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL;
+			props[nprops++] = POWER_SUPPLY_PROP_CHARGE_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL;
+			props[nprops++] = POWER_SUPPLY_PROP_ENERGY_NOW;
+
+			if (apple_smc_key_exists(smc, SMC_KEY(CHLS)))
+				power->has_chls = true;
+			if (power->has_chls || apple_smc_key_exists(smc, SMC_KEY(BCLM)))
+				props[nprops++] = POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD;
+			if (apple_smc_key_exists(smc, SMC_KEY(B0AP)))
+				props[nprops++] = POWER_SUPPLY_PROP_POWER_NOW;
+			if (apple_smc_key_exists(smc, SMC_KEY(B0TE)))
+				props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW;
+			if (apple_smc_key_exists(smc, SMC_KEY(BITV)))
+				props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
+			if (apple_smc_key_exists(smc, SMC_KEY(BCMV)))
+				props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
+
+			if (apple_smc_key_exists(smc, SMC_KEY(BC3V)))
+				power->num_cells = 3;
+			else
+				power->num_cells = 2;
+		} else {
+			/* Fundamental properties */
+			props[nprops++] = POWER_SUPPLY_PROP_STATUS;
+			props[nprops++] = POWER_SUPPLY_PROP_PRESENT;
+			props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_CURRENT_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_POWER_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_CAPACITY;
+			props[nprops++] = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
+			props[nprops++] = POWER_SUPPLY_PROP_TEMP;
+			props[nprops++] = POWER_SUPPLY_PROP_CYCLE_COUNT;
+			props[nprops++] = POWER_SUPPLY_PROP_HEALTH;
+			props[nprops++] = POWER_SUPPLY_PROP_SCOPE;
+			props[nprops++] = POWER_SUPPLY_PROP_MODEL_NAME;
+			props[nprops++] = POWER_SUPPLY_PROP_SERIAL_NUMBER;
+			props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_YEAR;
+			props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_MONTH;
+			props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_DAY;
+
+			/* Extended properties usually present */
+			props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
+			props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
+			props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
+			props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
+			props[nprops++] = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT;
+			props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
+			props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
+			props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
+			props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL;
+			props[nprops++] = POWER_SUPPLY_PROP_CHARGE_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
+			props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL;
+			props[nprops++] = POWER_SUPPLY_PROP_ENERGY_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_CHARGE_COUNTER;
+
+			/* Detect features based on key availability */
+			if (apple_smc_key_exists(smc, SMC_KEY(CHTE)))
+				power->has_chte = true;
+			if (apple_smc_key_exists(smc, SMC_KEY(CH0C)))
+				power->has_ch0c = true;
+			if (apple_smc_key_exists(smc, SMC_KEY(CH0I)))
+				power->has_ch0i = true;
+
+			ret = apple_smc_get_key_info(power->smc, SMC_KEY(BCF0), &info);
+			if (ret) {
+				dev_err(&pdev->dev, "Failed to determine BCF0 key size\n");
+				return ret;
+			}
+			if (info.size == 1)
+				power->fw_ge_27 = true;
+			else if (info.size == 4)
+				power->fw_ge_27 = false;
+			else {
+				dev_err(&pdev->dev, "Unexpected BCF0 key size %d\n", info.size);
+				return -EIO;
+			}
+
+			/* Reset "Optimised Battery Charging" flags to default state */
+			if (power->has_chte)
+				apple_smc_write_u32(smc, SMC_KEY(CHTE), 0);
+			else if (power->has_ch0c)
+				apple_smc_write_u8(smc, SMC_KEY(CH0C), 0);
 
 			if (power->has_ch0i)
-				power->batt_desc.charge_behaviours |=
-					BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE);
-
-			if (power->has_chte || power->has_ch0c)
-				power->batt_desc.charge_behaviours |=
-					BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE);
+				apple_smc_write_u8(smc, SMC_KEY(CH0I), 0);
+
+			apple_smc_write_u8(smc, SMC_KEY(CH0K), 0);
+			apple_smc_write_u8(smc, SMC_KEY(CH0B), 0);
+
+			/* Configure charge behaviour if supported */
+			if (power->has_ch0i || power->has_ch0c || power->has_chte) {
+				props[nprops++] = POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR;
+
+				power->batt_desc.charge_behaviours =
+					BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO);
+
+				if (power->has_ch0i)
+					power->batt_desc.charge_behaviours |=
+						BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE);
+
+				if (power->has_chte || power->has_ch0c)
+					power->batt_desc.charge_behaviours |=
+						BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE);
+			}
+
+			/* Detect charge limit method (CHWA vs CHLS) */
+			if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag) == 0)
+				power->has_chwa = true;
+			else if (apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16) >= 0)
+				power->has_chls = true;
+
+			/* Fetch identity strings */
+			apple_smc_read(smc, SMC_KEY(BMDN), power->model_name,
+				       sizeof(power->model_name) - 1);
+			apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number,
+				       sizeof(power->serial_number) - 1);
+			apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date,
+				       sizeof(power->mfg_date) - 1);
+
+			apple_smc_read_u8(power->smc, SMC_KEY(BNCB), &power->num_cells);
+			power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV *
+							power->num_cells;
+
+			/* Enable critical shutdown notifications by reading status once */
+			macsmc_battery_read_bcf0(power, &val32);
 		}
-
-		/* Detect charge limit method (CHWA vs CHLS) */
-		if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag) == 0)
-			power->has_chwa = true;
-		else if (apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16) >= 0)
-			power->has_chls = true;
-
 		if (nprops > MACSMC_MAX_BATT_PROPS)
 			return -ENOMEM;
 
 		power->batt_desc.properties = props;
 		power->batt_desc.num_properties = nprops;
 
-		/* Fetch identity strings */
-		apple_smc_read(smc, SMC_KEY(BMDN), power->model_name,
-			       sizeof(power->model_name) - 1);
-		apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number,
-			       sizeof(power->serial_number) - 1);
-		apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date,
-			       sizeof(power->mfg_date) - 1);
-
-		apple_smc_read_u8(power->smc, SMC_KEY(BNCB), &power->num_cells);
-		power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells;
-
-		/* Enable critical shutdown notifications by reading status once */
-		macsmc_battery_read_bcf0(power, &val32);
-
 		psy_cfg.drv_data = power;
 		power->batt = devm_power_supply_register(dev, &power->batt_desc, &psy_cfg);
 		if (IS_ERR(power->batt)) {
@@ -829,16 +1257,27 @@ static int macsmc_power_probe(struct platform_device *pdev)
 		/* Online status is fundamental */
 		props[nprops++] = POWER_SUPPLY_PROP_ONLINE;
 
-		/* Input power limits are usually available */
-		if (apple_smc_key_exists(power->smc, SMC_KEY(ACPW)))
-			props[nprops++] = POWER_SUPPLY_PROP_INPUT_POWER_LIMIT;
-
-		/* macOS 15.4+ firmware dropped legacy AC keys (AC-n, AC-i) */
-		if (apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16) >= 0) {
-			props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
-			props[nprops++] = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
+		if (smc->is_acpi) {
+			props[nprops++] = POWER_SUPPLY_PROP_CURRENT_NOW;
+			props[nprops++] = POWER_SUPPLY_PROP_POWER_NOW;
+			if (apple_smc_key_exists(power->smc, SMC_KEY(PD0R)))
+				power->has_pd0r = true;
+			/* MacBookPro5,x and 7,x dosent have VD0R key or any equivalent */
+			if (!power->has_battery || apple_smc_key_exists(power->smc, SMC_KEY(VD0R)))
+				props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+			if (apple_smc_key_exists(power->smc, SMC_KEY(ACPW)))
+				props[nprops++] = POWER_SUPPLY_PROP_INPUT_POWER_LIMIT;
+		} else {
+			/* Input power limits are usually available */
+			if (apple_smc_key_exists(power->smc, SMC_KEY(ACPW)))
+				props[nprops++] = POWER_SUPPLY_PROP_INPUT_POWER_LIMIT;
+
+			/* macOS 15.4+ firmware dropped legacy AC keys (AC-n, AC-i) */
+			if (apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16) >= 0) {
+				props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+				props[nprops++] = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
+			}
 		}
-
 		if (nprops > MACSMC_MAX_AC_PROPS)
 			return -ENOMEM;
 
@@ -858,9 +1297,26 @@ static int macsmc_power_probe(struct platform_device *pdev)
 	if (!power->batt && !power->ac)
 		return -ENODEV;
 
-	power->nb.notifier_call = macsmc_power_event;
-	blocking_notifier_chain_register(smc->event_handlers, &power->nb);
+	if (!smc->is_acpi) {
+		power->nb.notifier_call = macsmc_power_event;
+		blocking_notifier_chain_register(smc->event_handlers, &power->nb);
+	} else if (power->has_battery) {
+		power->adapter = acpi_dev_get_first_match_dev("ACPI0003", NULL, -1);
+		if (!power->adapter)
+			return -ENODEV;
 
+		ret = acpi_dev_install_notify_handler(power->adapter, ACPI_DEVICE_NOTIFY,
+					macsmc_power_acpi_notify, power);
+		if (ret)
+			return ret;
+
+		acpi_status status = acpi_evaluate_integer(power->adapter->handle, "_PSR", NULL,
+					&power->adapter_status);
+		if (ACPI_FAILURE(status))
+			return -EIO;
+
+		power->adapter_status = !!power->adapter_status;
+	}
 	return 0;
 }
 
@@ -868,7 +1324,11 @@ static void macsmc_power_remove(struct platform_device *pdev)
 {
 	struct macsmc_power *power = dev_get_drvdata(&pdev->dev);
 
-	blocking_notifier_chain_unregister(power->smc->event_handlers, &power->nb);
+	if (!power->smc->is_acpi)
+		blocking_notifier_chain_unregister(power->smc->event_handlers, &power->nb);
+	else
+		acpi_dev_remove_notify_handler(power->adapter, ACPI_DEVICE_NOTIFY,
+				macsmc_power_acpi_notify);
 }
 
 static const struct platform_device_id macsmc_power_id[] = {
@@ -891,3 +1351,4 @@ MODULE_LICENSE("Dual MIT/GPL");
 MODULE_DESCRIPTION("Apple SMC battery and power management driver");
 MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
 MODULE_AUTHOR("Michael Reeves <michael.reeves077@gmail.com>");
+MODULE_ALIAS("platform:macsmc-power");
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 11/14] acpi: disable battery and adapter for x86 apple machines
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (9 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 10/14] power/supply: add support for x86 macs in macsmc-power Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 12/14] hwmon/macsmc: add more types Atharva Tiwari
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

When macsmc-power is enabled on an Intel Mac, skip acpi-sbs
and the ACPI battery and adapter drivers, since macsmc-power
now provides them. This avoids duplicate devices.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/acpi/sbs.c       | 17 +++++++++--------
 drivers/acpi/x86/utils.c |  4 ++++
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index f10bbf13c..b12f2acc0 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -634,6 +634,9 @@ static int acpi_sbs_probe(struct platform_device *pdev)
 	int result = 0;
 	int id;
 
+	if (IS_ENABLED(CONFIG_MACSMC_POWER) && x86_apple_machine)
+		return -ENODEV;
+
 	device = ACPI_COMPANION(&pdev->dev);
 	if (!device)
 		return -ENODEV;
@@ -655,14 +658,12 @@ static int acpi_sbs_probe(struct platform_device *pdev)
 
 	result = 0;
 
-	if (!x86_apple_machine) {
-		result = acpi_manager_get_info(sbs);
-		if (!result) {
-			sbs->manager_present = 1;
-			for (id = 0; id < MAX_SBS_BAT; ++id)
-				if ((sbs->batteries_supported & (1 << id)))
-					acpi_battery_add(sbs, id);
-		}
+	result = acpi_manager_get_info(sbs);
+	if (!result) {
+		sbs->manager_present = 1;
+		for (id = 0; id < MAX_SBS_BAT; ++id)
+			if ((sbs->batteries_supported & (1 << id)))
+				acpi_battery_add(sbs, id);
 	}
 
 	if (!sbs->manager_present)
diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c
index 418951639..9f3b9b7d9 100644
--- a/drivers/acpi/x86/utils.c
+++ b/drivers/acpi/x86/utils.c
@@ -14,6 +14,7 @@
 #include <linux/dmi.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/x86/apple.h>
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
 #include "../internal.h"
@@ -608,6 +609,9 @@ bool acpi_quirk_skip_acpi_ac_and_battery(void)
 	long quirks = 0;
 	int i;
 
+	if (IS_ENABLED(CONFIG_MACSMC_POWER) && x86_apple_machine)
+		return true;
+
 	dmi_id = dmi_first_match(acpi_quirk_skip_dmi_ids);
 	if (dmi_id)
 		quirks = (unsigned long)dmi_id->driver_data;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 12/14] hwmon/macsmc: add more types
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (10 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 11/14] acpi: disable battery and adapter for x86 apple machines Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 13/14] hwmon: add support for x86 macs in macsmc-hwmon Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 14/14] mfd/macsmc: add ioctl to access smc keys Atharva Tiwari
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

SMC keys come in many data types, and Intel Macs use a lot
that macsmc-hwmon couldn't read, such as the signed fixed
point types like sp1e.

Add support for the common integer and fixed point types,
and allow writing fan speeds.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/hwmon/macsmc-hwmon.c | 310 +++++++++++++++++++++++++++++++++++
 1 file changed, 310 insertions(+)

diff --git a/drivers/hwmon/macsmc-hwmon.c b/drivers/hwmon/macsmc-hwmon.c
index 1500ec2cc..2e74dcd66 100644
--- a/drivers/hwmon/macsmc-hwmon.c
+++ b/drivers/hwmon/macsmc-hwmon.c
@@ -221,6 +221,313 @@ static int macsmc_hwmon_read_key(struct apple_smc *smc,
 			*val = (long)ioft;
 		break;
 	}
+	/* unsigned 8-bit integer */
+	case __SMC_KEY('u', 'i', '8', ' '): {
+		u8 ui8 = 0;
+
+		ret = apple_smc_read_u8(smc, sensor->macsmc_key, &ui8);
+		if (ret)
+			return ret;
+
+		*val = ui8 * scale;
+		break;
+	}
+	/* signed 8-bit integer */
+	case __SMC_KEY('s', 'i', '8', ' '): {
+		s8 si8 = 0;
+
+		ret = apple_smc_read_s8(smc, sensor->macsmc_key, &si8);
+		if (ret)
+			return ret;
+
+		*val = si8 * scale;
+		break;
+	}
+	/* unsigned 16-bit integer */
+	case __SMC_KEY('u', 'i', '1', '6'): {
+		u16 ui16 = 0;
+
+		ret = apple_smc_read_u16(smc, sensor->macsmc_key, &ui16);
+		if (ret)
+			return ret;
+
+		*val = ui16 * scale;
+		break;
+	}
+	/* signed 16-bit integer */
+	case __SMC_KEY('s', 'i', '1', '6'): {
+		s16 si16 = 0;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &si16);
+		if (ret)
+			return ret;
+
+		*val = si16 * scale;
+		break;
+	}
+	/* unsigned 32-bit integer */
+	case __SMC_KEY('u', 'i', '3', '2'): {
+		u32 ui32 = 0;
+
+		ret = apple_smc_read_u32(smc, sensor->macsmc_key, &ui32);
+		if (ret)
+			return ret;
+
+		*val = ui32 * scale;
+		break;
+	}
+	/* signed 32-bit integer */
+	case __SMC_KEY('s', 'i', '3', '2'): {
+		s32 si32;
+
+		ret = apple_smc_read_s32(smc, sensor->macsmc_key, &si32);
+		if (ret)
+			return ret;
+
+		*val = si32 * scale;
+		break;
+	}
+	/* 2.14 unsigned fixed point decimal */
+	case __SMC_KEY('f', 'p', '2', 'e'): {
+		u16 fp2e;
+
+		ret = apple_smc_read_u16(smc, sensor->macsmc_key, &fp2e);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp2e), scale, BIT(14));
+		break;
+	}
+
+	/* 3.13 unsigned fixed point decimal */
+	case __SMC_KEY('f', 'p', '3', 'd'): {
+		u16 fp3d;
+
+		ret = apple_smc_read_u16(smc, sensor->macsmc_key, &fp3d);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp3d), scale, BIT(13));
+		break;
+	}
+
+	/* 4.12 unsigned fixed point decimal */
+	case __SMC_KEY('f', 'p', '4', 'c'): {
+		u16 fp4c;
+
+		ret = apple_smc_read_u16(smc, sensor->macsmc_key, &fp4c);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp4c), scale, BIT(12));
+		break;
+	}
+
+	/* 5.11 unsigned fixed point decimal */
+	case __SMC_KEY('f', 'p', '5', 'b'): {
+		u16 fp5b;
+
+		ret = apple_smc_read_u16(smc, sensor->macsmc_key, &fp5b);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp5b), scale, BIT(11));
+		break;
+	}
+
+	/* 6.10 unsigned fixed point decimal */
+	case __SMC_KEY('f', 'p', '6', 'a'): {
+		u16 fp6a;
+
+		ret = apple_smc_read_u16(smc, sensor->macsmc_key, &fp6a);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp6a), scale, BIT(10));
+		break;
+	}
+
+	/* 8.8 unsigned fixed point decimal */
+	case __SMC_KEY('f', 'p', '8', '8'): {
+		u16 fp88;
+
+		ret = apple_smc_read_u16(smc, sensor->macsmc_key, &fp88);
+		if (ret)
+			return ret;
+
+		*val = mult_frac(be16_to_cpu(fp88), scale, BIT(8));
+		break;
+	}
+	/* 14.2 fixed point decimal */
+	case __SMC_KEY('f', 'p', 'e', '2'): {
+		/*
+		 * fpe2 is only used for fan stats and fan control
+		 * so precision is not needed
+		 */
+		u16 fpe2;
+
+		ret = apple_smc_read_u16(smc, sensor->macsmc_key, &fpe2);
+		if (ret)
+			return ret;
+
+		*val = (be16_to_cpu(fpe2) >> 2);
+		break;
+	}
+	/* 1.14 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '1', 'e'): {
+		s16 sp1e;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp1e);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp1e), scale, BIT(14));
+		break;
+	}
+
+	/* 2.13 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '2', 'd'): {
+		s16 sp2d;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp2d);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp2d), scale, BIT(13));
+		break;
+	}
+
+	/* 3.12 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '3', 'c'): {
+		s16 sp3c;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp3c);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp3c), scale, BIT(12));
+		break;
+	}
+
+	/* 4.11 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '4', 'b'): {
+		s16 sp4b;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp4b);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp4b), scale, BIT(11));
+		break;
+	}
+
+	/* 5.10 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '5', 'a'): {
+		s16 sp5a;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp5a);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp5a), scale, BIT(10));
+		break;
+	}
+
+	/* 6.9 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '6', '9'): {
+		s16 sp69;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp69);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp69), scale, BIT(9));
+		break;
+	}
+
+	/* 7.8 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '7', '8'): {
+		s16 sp78;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp78);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp78), scale, BIT(8));
+		break;
+	}
+
+	/* 8.7 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '8', '7'): {
+		s16 sp87;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp87);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp87), scale, BIT(7));
+		break;
+	}
+
+	/* 9.6 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', '9', '6'): {
+		s16 sp96;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &sp96);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(sp96), scale, BIT(6));
+		break;
+	}
+
+	/* 10.5 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', 'a', '5'): {
+		s16 spa5;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &spa5);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(spa5), scale, BIT(5));
+		break;
+	}
+
+	/* 12.3 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', 'c', '3'): {
+		s16 spc3;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &spc3);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(spc3), scale, BIT(3));
+		break;
+	}
+
+	/* 13.2 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', 'd', '2'): {
+		s16 spd2;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &spd2);
+		if (ret)
+			return ret;
+
+		*val = mult_frac((s16)be16_to_cpu(spd2), scale, BIT(2));
+		break;
+	}
+
+	/* 15.0 signed fixed point decimal */
+	case __SMC_KEY('s', 'p', 'f', '0'): {
+		s16 spf0;
+
+		ret = apple_smc_read_s16(smc, sensor->macsmc_key, &spf0);
+		if (ret)
+			return ret;
+
+		*val = (s16)be16_to_cpu(spf0) * scale;
+		break;
+	}
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -263,6 +570,9 @@ static int macsmc_hwmon_write_key(struct apple_smc *smc,
 	/* unsigned 8-bit integer */
 	case __SMC_KEY('u', 'i', '8', ' '):
 		return apple_smc_write_u8(smc, sensor->macsmc_key, val);
+	/* 14.2 fixed point decimal */
+	case __SMC_KEY('f', 'p', 'e', '2'):
+		return apple_smc_write_u16(smc, sensor->macsmc_key, cpu_to_be16(val << 2));
 	default:
 		return -EOPNOTSUPP;
 	}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 13/14] hwmon: add support for x86 macs in macsmc-hwmon
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (11 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 12/14] hwmon/macsmc: add more types Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  2026-10-29  8:52 ` [RFC PATCH 14/14] mfd/macsmc: add ioctl to access smc keys Atharva Tiwari
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Make macsmc-hwmon work on Intel Macs, as a replacement for
the hwmon part of applesmc.

Sensors now have readable names instead of raw keys like
TA0P. It also adds power, current and voltage sensors that
applesmc doesn't provide, and supports fan control on newer
machines aswell.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/hwmon/Kconfig        |   5 +-
 drivers/hwmon/macsmc-hwmon.c | 816 ++++++++++++++++++++++++++++++++++-
 2 files changed, 800 insertions(+), 21 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 91ca7031c..4d0bb87ff 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1244,13 +1244,12 @@ config SENSORS_LTQ_CPUTEMP
 	  sensor inside your CPU.
 
 config SENSORS_MACSMC_HWMON
-	tristate "Apple SMC (Apple Silicon)"
+	tristate "Apple SMC Hardware monitoring"
 	depends on MFD_MACSMC_CORE
 	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.
+	  on Apple devices. Say Y here if you have an Apple device.
 
 	  This driver can also be built as a module. If so, the module will
 	  be called macsmc-hwmon.
diff --git a/drivers/hwmon/macsmc-hwmon.c b/drivers/hwmon/macsmc-hwmon.c
index 2e74dcd66..29e43ff4b 100644
--- a/drivers/hwmon/macsmc-hwmon.c
+++ b/drivers/hwmon/macsmc-hwmon.c
@@ -2,7 +2,7 @@
 /*
  * Apple SMC hwmon driver for Apple Silicon platforms
  *
- * The System Management Controller on Apple Silicon devices is responsible for
+ * The System Management Controller on Apple Silicon And Intel Macs 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
@@ -18,6 +18,11 @@
  * Originally based on a concept by Jean-Francois Bortolotti <jeff@borto.fr>
  *
  * Copyright The Asahi Linux Contributors
+ *
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
+ *
+ * Old macs fan control based on smcFanControl:
+ * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com>
  */
 
 #include <linux/bitfield.h>
@@ -47,7 +52,7 @@ MODULE_PARM_DESC(fan_control,
 struct macsmc_hwmon_sensor {
 	struct apple_smc_key_info info;
 	smc_key macsmc_key;
-	char label[MAX_LABEL_LENGTH];
+	char *label;
 	u32 attrs;
 };
 
@@ -71,7 +76,8 @@ struct macsmc_hwmon_sensors {
 struct macsmc_hwmon_fans {
 	struct hwmon_channel_info channel_info;
 	struct macsmc_hwmon_fan *fans;
-	u32 count;
+	u8 count;
+	bool has_old_manual;
 };
 
 struct macsmc_hwmon {
@@ -553,8 +559,8 @@ static int macsmc_hwmon_write_f32(struct apple_smc *smc, smc_key key, long value
 			val <<= 23 - exp;
 
 		fval = FIELD_PREP(FLT_SIGN_MASK, neg) |
-		       FIELD_PREP(FLT_EXP_MASK, exp + FLT_EXP_BIAS) |
-		       FIELD_PREP(FLT_MANT_MASK, val & FLT_MANT_MASK);
+			FIELD_PREP(FLT_EXP_MASK, exp + FLT_EXP_BIAS) |
+			FIELD_PREP(FLT_MANT_MASK, val & FLT_MANT_MASK);
 	}
 
 	return apple_smc_write_u32(smc, key, fval);
@@ -599,6 +605,23 @@ static int macsmc_hwmon_read_fan(struct macsmc_hwmon *hwmon, u32 attr, int chan,
 	}
 }
 
+static int macsmc_hwmon_write_old_fan_manual(struct macsmc_hwmon *hwmon, u8 val, int channel)
+{
+	int ret;
+	u16 buf;
+
+	ret = apple_smc_read_u16(hwmon->smc, __SMC_KEY('F', 'S', '!', ' '), &buf);
+	if (ret)
+		return ret;
+
+	if (val)
+		buf |= BIT(channel);
+	else
+		buf &= ~BIT(channel);
+
+	return apple_smc_write_u16(hwmon->smc, __SMC_KEY('F', 'S', '!', ' '), cpu_to_be16(buf));
+}
+
 static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel,
 				  long val)
 {
@@ -606,7 +629,8 @@ static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel,
 	long min, max;
 	int ret;
 
-	if (!fan_control || hwmon->fan.fans[channel].mode.macsmc_key == 0)
+	if (!fan_control || (hwmon->fan.fans[channel].mode.macsmc_key == 0 &&
+				!hwmon->fan.has_old_manual))
 		return -EOPNOTSUPP;
 
 	/*
@@ -625,7 +649,10 @@ static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel,
 	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,
+			if (hwmon->fan.has_old_manual)
+				ret = macsmc_hwmon_write_old_fan_manual(hwmon, 1, channel);
+			else
+				ret = macsmc_hwmon_write_key(hwmon->smc,
 						     &hwmon->fan.fans[channel].mode, 1);
 			if (ret < 0)
 				return ret;
@@ -633,10 +660,13 @@ static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel,
 			hwmon->fan.fans[channel].manual = true;
 		}
 		return macsmc_hwmon_write_key(hwmon->smc,
-					      &hwmon->fan.fans[channel].set, val);
+					      &hwmon->fan.fans[channel].set, val) > 0;
 	} else if (!val) {
 		if (hwmon->fan.fans[channel].manual) {
-			ret = macsmc_hwmon_write_key(hwmon->smc,
+			if (hwmon->fan.has_old_manual)
+				ret = macsmc_hwmon_write_old_fan_manual(hwmon, 0, channel);
+			else
+				ret = macsmc_hwmon_write_key(hwmon->smc,
 						     &hwmon->fan.fans[channel].mode, 0);
 			if (ret < 0)
 				return ret;
@@ -697,10 +727,11 @@ static int macsmc_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
 }
 
 static umode_t macsmc_hwmon_fan_is_visible(const struct macsmc_hwmon_fan *fan,
-					   u32 attr)
+					   u32 attr, bool has_old_manual)
 {
 	if (fan->attrs & BIT(attr)) {
-		if (attr == hwmon_fan_target && fan_control && fan->mode.macsmc_key)
+		if (attr == hwmon_fan_target && fan_control &&
+				(fan->mode.macsmc_key || has_old_manual))
 			return 0644;
 
 		return 0444;
@@ -710,8 +741,8 @@ static umode_t macsmc_hwmon_fan_is_visible(const struct macsmc_hwmon_fan *fan,
 }
 
 static umode_t macsmc_hwmon_is_visible(const void *data,
-				       enum hwmon_sensor_types type, u32 attr,
-				       int channel)
+					enum hwmon_sensor_types type, u32 attr,
+					int channel)
 {
 	const struct macsmc_hwmon *hwmon = data;
 	struct macsmc_hwmon_sensor *sensor;
@@ -730,7 +761,8 @@ static umode_t macsmc_hwmon_is_visible(const void *data,
 		sensor = &hwmon->temp.sensors[channel];
 		break;
 	case hwmon_fan:
-		return macsmc_hwmon_fan_is_visible(&hwmon->fan.fans[channel], attr);
+		return macsmc_hwmon_fan_is_visible(&hwmon->fan.fans[channel],
+				attr, hwmon->fan.has_old_manual);
 	default:
 		return 0;
 	}
@@ -800,6 +832,578 @@ static int macsmc_hwmon_create_sensor(struct device *dev, struct apple_smc *smc,
 	return 0;
 }
 
+struct macsmc_sensor_desc {
+	char *name;
+	smc_key key;
+};
+
+/*
+ * Key's name taken from:
+ * https://github.com/kaiT2en/KaiT2en-Fedora/blob/main/apps/t2-smc-control/src/main.rs
+ * https://github.com/acidanthera/VirtualSMC/tree/master/Docs/
+ */
+
+static const struct macsmc_sensor_desc temp_sensors[] = {
+	{ "Ambient 2 Raw Temperature (TA1p)", SMC_KEY(TA1p) },
+	{ "Ambient 3 Raw Temperature (TA2p)", SMC_KEY(TA2p) },
+	{ "Ambient Cooked Temperature (TA0P)", SMC_KEY(TA0P) },
+	{ "Ambient Cooked Temperature 1 (TA1P)", SMC_KEY(TA1P) },
+	{ "Ambient Cooked Temperature 2 (TA2P)", SMC_KEY(TA2P) },
+	{ "Ambient Raw Temperature (TA0p)", SMC_KEY(TA0p) },
+	{ "Ambient Virtual Temperature (TA0V)", SMC_KEY(TA0V) },
+	{ "Audio L Temperature (TaLC)", SMC_KEY(TaLC) },
+	{ "Audio R Temperature (TaRC)", SMC_KEY(TaRC) },
+	{ "Backlight Controller Proximity Cooked Temperature (Tb0P)", SMC_KEY(Tb0P) },
+	{ "Backlight Controller Proximity Raw Temperature (Tb0p)", SMC_KEY(Tb0p) },
+	{ "Battery Ambient Temperature (TB3T)", SMC_KEY(TB3T) },
+	{ "Battery Temperature (TB0T)", SMC_KEY(TB0T) },
+	{ "Battery Temperature, Same As TB0T (TBXT)", SMC_KEY(TBXT) },
+	{ "Battery TS1 Temperature (TB1T)", SMC_KEY(TB1T) },
+	{ "Battery TS2 Temperature (TB2T)", SMC_KEY(TB2T) },
+	{ "Charger Proximity Temperature (TCHP)", SMC_KEY(TCHP) },
+	{ "CPU 1 Heatsink Alt. Temperature (TCAH)", SMC_KEY(TCAH) },
+	{ "CPU 1 Heatsink Temperature (TC0H)", SMC_KEY(TC0H) },
+	{ "CPU 1 Package Temperature (TCAD)", SMC_KEY(TCAD) },
+	{ "CPU 2 Diode Filtered Temperature (TC1F)", SMC_KEY(TC1F) },
+	{ "CPU 2 Diode Virtual Temperature (TC1E)", SMC_KEY(TC1E) },
+	{ "CPU 2 Heatsink Alt. Temperature (TCBH)", SMC_KEY(TCBH) },
+	{ "CPU 2 Heatsink Temperature (TC1H)", SMC_KEY(TC1H) },
+	{ "CPU 2 Package Alt. Temperature (TCBD)", SMC_KEY(TCBD) },
+	{ "CPU 2 Package Temperature (TC1D)", SMC_KEY(TC1D) },
+	{ "CPU 2 Proximity Raw Temperature (TC1p)", SMC_KEY(TC1p) },
+	{ "CPU Core 0 Absolute Raw Temperature PECI (TC0c)", SMC_KEY(TC0c) },
+	{ "CPU Core 1 Absolute Cooked Temperature PECI (TC1C)", SMC_KEY(TC1C) },
+	{ "CPU Core 1 Absolute Raw Temperature PECI (TC1c)", SMC_KEY(TC1c) },
+	{ "CPU Core 2 Absolute Cooked Temperature PECI (TC2C)", SMC_KEY(TC2C) },
+	{ "CPU Core 2 Absolute Raw Temperature PECI (TC2c)", SMC_KEY(TC2c) },
+	{ "CPU Core 3 Absolute Cooked Temperature PECI (TC3C)", SMC_KEY(TC3C) },
+	{ "CPU Core 3 Absolute Raw Temperature PECI (TC3c)", SMC_KEY(TC3c) },
+	{ "CPU Core 3 Temperature PECI (TC4C)", SMC_KEY(TC4C) },
+	{ "CPU Core 4 Absolute Raw Temperature PECI (TC4c)", SMC_KEY(TC4c) },
+	{ "CPU Core 5 Absolute Raw Temperature PECI (TC5c)", SMC_KEY(TC5c) },
+	{ "CPU Core 6 Absolute Raw Temperature PECI (TC6c)", SMC_KEY(TC6c) },
+	{ "CPU Core 7 Absolute Raw Temperature PECI (TC7c)", SMC_KEY(TC7c) },
+	{ "CPU Core 8 Absolute Raw Temperature PECI (TC8c)", SMC_KEY(TC8c) },
+	{ "CPU Core 9 Absolute Raw Temperature PECI (TC9c)", SMC_KEY(TC9c) },
+	{ "CPU Core PECI Temperature (TC0C)", SMC_KEY(TC0C) },
+	{ "CPU Die Raw Temperature (TC0d)", SMC_KEY(TC0d) },
+	{ "CPU Die Temperature (TC0D)", SMC_KEY(TC0D) },
+	{ "CPU eDRAM Hot Indicator PECI (TCMc)", SMC_KEY(TCMc) },
+	{ "CPU Gfx Core Absolute Cooked Temperature PECI (TCGC)", SMC_KEY(TCGC) },
+	{ "CPU Gfx Core Absolute Raw Temperature PECI (TCGc)", SMC_KEY(TCGc) },
+	{ "CPU Heatpipe Temperature (Th0H)", SMC_KEY(Th0H) },
+	{ "CPU Max Package Core Absolute Cooked Temperature PECI (TCXC)", SMC_KEY(TCXC) },
+	{ "CPU Max Package Core Absolute Raw Temperature PECI (TCXc)", SMC_KEY(TCXc) },
+	{ "CPU Max Package Core Relative Cooked Temperature PECI (TCXR)", SMC_KEY(TCXR) },
+	{ "CPU Max Package Core Relative Raw Temperature PECI (TCXr)", SMC_KEY(TCXr) },
+	{ "CPU PECI Die Filtered And Adjusted Temperature (TC0F)", SMC_KEY(TC0F) },
+	{ "CPU PECI Die Filtered Temperature (TC0E)", SMC_KEY(TC0E) },
+	{ "CPU PECI Die Temperature Adjustment (TC0G)", SMC_KEY(TC0G) },
+	{ "CPU PECI Die Temperature Filter Coeff (TCFC)", SMC_KEY(TCFC) },
+	{ "CPU PECI Die Temperature Max Error (TC0J)", SMC_KEY(TC0J) },
+	{ "CPU PECI Die Temperature Trend (TC0T)", SMC_KEY(TC0T) },
+	{ "CPU PECI Die Temperature Trend (TCTD)", SMC_KEY(TCTD) },
+	{ "CPU Proximity Raw Temperature (TC0p)", SMC_KEY(TC0p) },
+	{ "CPU Proximity Temperature (TC0P)", SMC_KEY(TC0P) },
+	{ "CPU Relative To ProcHot 1 (TCAC)", SMC_KEY(TCAC) },
+	{ "CPU Relative To ProcHot 2 (TCBC)", SMC_KEY(TCBC) },
+	{ "CPU System Agent Core Absolute Cooked Temperature PECI (TCSC)", SMC_KEY(TCSC) },
+	{ "CPU System Agent Core Absolute Raw Temperature PECI (TCSc)", SMC_KEY(TCSc) },
+	{ "CPU System Agent Core Temperature PECI (TCSA)", SMC_KEY(TCSA) },
+	{ "CPU VR Proximity Cooked Temperature (TC1P)", SMC_KEY(TC1P) },
+	{ "DCIn Air Flow Adjusted Temperature (TaSP)", SMC_KEY(TaSP) },
+	{ "DCIn Air Flow Temperature (Ta0P)", SMC_KEY(Ta0P) },
+	{ "DIMM 0 PECI Cooked Temperature (TM0D)", SMC_KEY(TM0D) },
+	{ "DIMM 0 Proximity Raw Temperature (TM0p)", SMC_KEY(TM0p) },
+	{ "DIMM 1 PECI Cooked Temperature (TM1D)", SMC_KEY(TM1D) },
+	{ "DIMM 1 Proximity Cooked Temperature (TM1P)", SMC_KEY(TM1P) },
+	{ "DIMM 1 Proximity Raw Temperature (TM1p)", SMC_KEY(TM1p) },
+	{ "DIMM 2 PECI Cooked Temperature (TM2D)", SMC_KEY(TM2D) },
+	{ "DIMM 2 Proximity Cooked Temperature (TM2P)", SMC_KEY(TM2P) },
+	{ "DIMM 2 Proximity Raw Temperature (TM2p)", SMC_KEY(TM2p) },
+	{ "DIMM 3 PECI Cooked Temperature (TM3D)", SMC_KEY(TM3D) },
+	{ "DIMM 3 Proximity Cooked Temperature (TM3P)", SMC_KEY(TM3P) },
+	{ "DIMM 3 Proximity Raw Temperature (TM3p)", SMC_KEY(TM3p) },
+	{ "DIMM Bandwidth Estimate (TMBS)", SMC_KEY(TMBS) },
+	{ "DIMM Max Cooked Temperature (TMXD)", SMC_KEY(TMXD) },
+	{ "DIMM Proximity Max Cooked Temperature (TMXP)", SMC_KEY(TMXP) },
+	{ "DIMM Virtual Temperature (TM0V)", SMC_KEY(TM0V) },
+	{ "DIMM Virtual Temperature For 2x DIMM Slot 1+2 Or 2+3 Config (TM2b)", SMC_KEY(TM2b) },
+	{ "DIMM Virtual Temperature For 2x DIMM Slot 1+2+3+4 Config (TM1a)", SMC_KEY(TM1a) },
+	{ "DIMM Virtual Temperature For 2x DIMM Slot 1+3 Config (TM2a)", SMC_KEY(TM2a) },
+	{ "DIMM Virtual Temperature For 2x DIMM Slot 1+4 Or 2+4 Config (TM2c)", SMC_KEY(TM2c) },
+	{ "DIMM Virtual Temperature For 2x DIMM Slot 3+4 Config (TM2d)", SMC_KEY(TM2d) },
+	{ "DIMM Virtual Temperature For 3x DIMM Slot 1+2+3 Config (TM3d)", SMC_KEY(TM3d) },
+	{ "DIMM Virtual Temperature For 3x DIMM Slot 1+2+4 Config (TM3c)", SMC_KEY(TM3c) },
+	{ "DIMM Virtual Temperature For 3x DIMM Slot 1+3+4 Config (TM3b)", SMC_KEY(TM3b) },
+	{ "DIMM Virtual Temperature For 3x DIMM Slot 2+3+4 Config (TM3a)", SMC_KEY(TM3a) },
+	{ "DIMM Virtual Temperature For 4x DIMM Slot 1+2+3+4 Config (TM4a)", SMC_KEY(TM4a) },
+	{ "Drive 0 Backup Virtual DRAM Temperature (TH0V)", SMC_KEY(TH0V) },
+	{ "Drive 0 OOBv1 Mapped Temperature (TH0O)", SMC_KEY(TH0O) },
+	{ "Drive 0 OOBv1 Raw Bit-Mapped Temperature (TH0o)", SMC_KEY(TH0o) },
+	{ "Drive 0 OOBv3 Absolute Cooked Temperature A (TH0A)", SMC_KEY(TH0A) },
+	{ "Drive 0 OOBv3 Absolute Cooked Temperature B (TH0B)", SMC_KEY(TH0B) },
+	{ "Drive 0 OOBv3 Absolute Cooked Temperature C (TH0C)", SMC_KEY(TH0C) },
+	{ "Drive 0 OOBv3 Absolute Raw Temperature A (TH0a)", SMC_KEY(TH0a) },
+	{ "Drive 0 OOBv3 Absolute Raw Temperature B (TH0b)", SMC_KEY(TH0b) },
+	{ "Drive 0 OOBv3 Absolute Raw Temperature C (TH0c)", SMC_KEY(TH0c) },
+	{ "Drive 0 OOBv3 Raw Temperature Max (TH0x)", SMC_KEY(TH0x) },
+	{ "Drive 0 OOBv3 Relative Cooked Temperature Max (TH0R)", SMC_KEY(TH0R) },
+	{ "Drive 0 OOBv3 Relative Filtered Temperature Max (TH0F)", SMC_KEY(TH0F) },
+	{ "Drive 0 Temperature Sensor Existence Used For TM0V (TH0e)", SMC_KEY(TH0e) },
+	{ "Drive 1 OOBv1 Filtered Temperature (TH1P)", SMC_KEY(TH1P) },
+	{ "Drive 1 OOBv1 Mapped Temperature (TH1O)", SMC_KEY(TH1O) },
+	{ "Drive 1 OOBv1 Raw Bit-Mapped Temperature (TH1o)", SMC_KEY(TH1o) },
+	{ "Drive 1 OOBv3 Absolute Cooked Temperature A (TH1A)", SMC_KEY(TH1A) },
+	{ "Drive 1 OOBv3 Absolute Cooked Temperature B (TH1B)", SMC_KEY(TH1B) },
+	{ "Drive 1 OOBv3 Absolute Cooked Temperature C (TH1C)", SMC_KEY(TH1C) },
+	{ "Drive 1 OOBv3 Absolute Raw Temperature A (TH1a)", SMC_KEY(TH1a) },
+	{ "Drive 1 OOBv3 Absolute Raw Temperature B (TH1b)", SMC_KEY(TH1b) },
+	{ "Drive 1 OOBv3 Absolute Raw Temperature C (TH1c)", SMC_KEY(TH1c) },
+	{ "Drive 1 OOBv3 Relative Cooked Temperature Max (TH1R)", SMC_KEY(TH1R) },
+	{ "Drive 1 OOBv3 Relative Filtered Temperature Max (TH1F)", SMC_KEY(TH1F) },
+	{ "EMC1428 Internal Diode Raw Temperature (Tm0p)", SMC_KEY(Tm0p) },
+	{ "Expansion Slots Temperature (TS0C)", SMC_KEY(TS0C) },
+	{ "Fan 0 Synthetic Temperature (TF0S)", SMC_KEY(TF0S) },
+	{ "Fan Target Temperature (TTF0)", SMC_KEY(TTF0) },
+	{ "Filtered Power Supply 1 Proximity Temperature (TpFP)", SMC_KEY(TpFP) },
+	{ "Filtered Vent Proximity Temperature (TVFP)", SMC_KEY(TVFP) },
+	{ "Fin Stack Temperature (THFH)", SMC_KEY(THFH) },
+	{ "GFX Memory Temperature (TG0M)", SMC_KEY(TG0M) },
+	{ "GPU 0 Die Cooked Temperature (TG0D)", SMC_KEY(TG0D) },
+	{ "GPU 0 Die Filtered And Adjusted Temperature (TG0F)", SMC_KEY(TG0F) },
+	{ "GPU 0 Die Filtered Temperature (TG0E)", SMC_KEY(TG0E) },
+	{ "GPU 0 Die Raw Temperature (TG0d)", SMC_KEY(TG0d) },
+	{ "GPU 0 Die Temperature Max Error (TG0J)", SMC_KEY(TG0J) },
+	{ "GPU 0 Die Temperature Trend (TG0T)", SMC_KEY(TG0T) },
+	{ "GPU 0 Proximity Cooked Temperature (TG0P)", SMC_KEY(TG0P) },
+	{ "GPU 0 Proximity Raw Temperature (TG0p)", SMC_KEY(TG0p) },
+	{ "GPU AMD Die Digital Temperature (TGDD)", SMC_KEY(TGDD) },
+	{ "GPU B VR Proximity Cooked Temperature (TG5P)", SMC_KEY(TG5P) },
+	{ "GPU B VRAM Proximity Cooked Temperature (TG4P)", SMC_KEY(TG4P) },
+	{ "GPU Die Analog Temperature (TGDF)", SMC_KEY(TGDF) },
+	{ "GPU Digital Die Filtered Temperature (TG1F)", SMC_KEY(TG1F) },
+	{ "GPU Digital Die Raw Temperature Without Offset (TG1d)", SMC_KEY(TG1d) },
+	{ "GPU Digital Die Temperature (TG1D)", SMC_KEY(TG1D) },
+	{ "GPU FB VR Proximity 3 Cooked Temperature (TG3P)", SMC_KEY(TG3P) },
+	{ "GPU FB VR Proximity 3 Raw Temperature (TG3p)", SMC_KEY(TG3p) },
+	{ "GPU GDDR5 Proximity 1 Cooked Temperature (TG1P)", SMC_KEY(TG1P) },
+	{ "GPU GDDR5 Proximity 1 Raw Temperature (TG1p)", SMC_KEY(TG1p) },
+	{ "GPU VR Proximity 2 Cooked Temperature (TG2P)", SMC_KEY(TG2P) },
+	{ "GPU VR Proximity 2 Raw Temperature (TG2p)", SMC_KEY(TG2p) },
+	{ "GPU VR Temperature (TGVP)", SMC_KEY(TGVP) },
+	{ "HDD Bay 3 Temperature (TH2P)", SMC_KEY(TH2P) },
+	{ "HDD Bay 4 Temperature (TH3P)", SMC_KEY(TH3P) },
+	{ "HDD Proximity Temperature (TH0P)", SMC_KEY(TH0P) },
+	{ "Heat Pipe Temperature (TG0H)", SMC_KEY(TG0H) },
+	{ "Heat Pipe Temperature 2 (TG1H)", SMC_KEY(TG1H) },
+	{ "Heat Spreader Proximity Temperature (Th0P)", SMC_KEY(Th0P) },
+	{ "High Speed I/O Proximity 0 Temperature (TI0P)", SMC_KEY(TI0P) },
+	{ "High Speed I/O Proximity 1 Temperature (TI1P)", SMC_KEY(TI1P) },
+	{ "LCD Filtered Center Front-Of-Screen Temperature Feeding TL1V (TL1v)", SMC_KEY(TL1v) },
+	{ "LCD Filtered Center Front-Of-Screen Temperature For Color Compensation (TL1V)", SMC_KEY(TL1V) },
+	{ "LCD Filtered Front-Of-Screen Hotspot Virtual Temperature (TL0V)", SMC_KEY(TL0V) },
+	{ "LCD IR Target Hotspot Calculated Temperature (TL2p)", SMC_KEY(TL2p) },
+	{ "LCD IR Temperature Sensor Die Raw Temperature (TL3p)", SMC_KEY(TL3p) },
+	{ "LCD TCON Proximity Cooked Temperature (TL1P)", SMC_KEY(TL1P) },
+	{ "LCD TCON Proximity Raw Temperature (TL1p)", SMC_KEY(TL1p) },
+	{ "LCD Unfiltered Front-Of-Screen Cooked Temperature (TL0P)", SMC_KEY(TL0P) },
+	{ "LCD Unfiltered Front-Of-Screen Raw Temperature (TL0p)", SMC_KEY(TL0p) },
+	{ "Left Fin Stack Temperature (Th2H)", SMC_KEY(Th2H) },
+	{ "Max PECI Reported Temperature (TCMX)", SMC_KEY(TCMX) },
+	{ "MCH Diode Temperature (TN0C)", SMC_KEY(TN0C) },
+	{ "MCH Heatsink Temperature (TN0H)", SMC_KEY(TN0H) },
+	{ "MCP Die 2 Temperature (TN1D)", SMC_KEY(TN1D) },
+	{ "MCP Die Temperature (TN0D)", SMC_KEY(TN0D) },
+	{ "MCP Proximity Temperature (TN0P)", SMC_KEY(TN0P) },
+	{ "Mem Bank B1 Temperature (TM8P)", SMC_KEY(TM8P) },
+	{ "Mem Bank B2 Temperature (TM9P)", SMC_KEY(TM9P) },
+	{ "Mem Module A2 Temperature (TM1S)", SMC_KEY(TM1S) },
+	{ "Mem Module A3 Temperature (TM2S)", SMC_KEY(TM2S) },
+	{ "Mem Module A4 Temperature (TM3S)", SMC_KEY(TM3S) },
+	{ "Mem Module B1 Temperature (TM8S)", SMC_KEY(TM8S) },
+	{ "Mem Module B2 Temperature (TM9S)", SMC_KEY(TM9S) },
+	{ "Memory Controller/Integrated Graphics PECI Temperature (TMCD)", SMC_KEY(TMCD) },
+	{ "Memory Proximity Temperature (TM0P)", SMC_KEY(TM0P) },
+	{ "MLB Bottom Side Temperature (Tm1P)", SMC_KEY(Tm1P) },
+	{ "MLB Proximity 0 Cooked Temperature (Tm0P)", SMC_KEY(Tm0P) },
+	{ "MLB Proximity 1 Raw Temperature (Tm1p)", SMC_KEY(Tm1p) },
+	{ "MLB Proximity 2 Cooked Temperature (Tm2P)", SMC_KEY(Tm2P) },
+	{ "MLB Proximity 2 Raw Temperature (Tm2p)", SMC_KEY(Tm2p) },
+	{ "MLB Proximity Temperature (TMLB)", SMC_KEY(TMLB) },
+	{ "MLB Temperature (Tp0P)", SMC_KEY(Tp0P) },
+	{ "NAND Temperature (Th0N)", SMC_KEY(Th0N) },
+	{ "Northbridge 2 Temperature (TN1P)", SMC_KEY(TN1P) },
+	{ "Optical Drive Temperature (TO0P)", SMC_KEY(TO0P) },
+	{ "Palm Rest Temperature (Ts0P)", SMC_KEY(Ts0P) },
+	{ "Palm Rest Temperature 2 (Ts1P)", SMC_KEY(Ts1P) },
+	{ "PCH Die Cooked Temperature (TPCD)", SMC_KEY(TPCD) },
+	{ "PCH Digital Die Raw Temperature (TPCd)", SMC_KEY(TPCd) },
+	{ "PCH Diode Temperature (TP0D)", SMC_KEY(TP0D) },
+	{ "PCH Proximity Temperature (TP0P)", SMC_KEY(TP0P) },
+	{ "PCI Slot 1 Pos 1 Temperature (TA0S)", SMC_KEY(TA0S) },
+	{ "PCI Slot 1 Pos 2 Temperature (TA1S)", SMC_KEY(TA1S) },
+	{ "PCI Slot 2 Pos 1 Temperature (TA2S)", SMC_KEY(TA2S) },
+	{ "PCI Slot 2 Pos 2 Temperature (TA3S)", SMC_KEY(TA3S) },
+	{ "Power Supply 1 Alt. Temperature (Tp0C)", SMC_KEY(Tp0C) },
+	{ "Power Supply 2 Alt. Temperature (Tp1C)", SMC_KEY(Tp1C) },
+	{ "Power Supply 3 Temperature (Tp2P)", SMC_KEY(Tp2P) },
+	{ "Power Supply 4 Temperature (Tp3P)", SMC_KEY(Tp3P) },
+	{ "Power Supply 5 Temperature (Tp4P)", SMC_KEY(Tp4P) },
+	{ "Power Supply 6 Temperature (Tp5P)", SMC_KEY(Tp5P) },
+	{ "Power Supply T2 Secondary Heatsink Cooked Temperature (Tp2H)", SMC_KEY(Tp2H) },
+	{ "Power Supply T2 Secondary Heatsink Filtered Temperature (Tp2F)", SMC_KEY(Tp2F) },
+	{ "Power Supply T2 Secondary Heatsink Raw Temperature (Tp2h)", SMC_KEY(Tp2h) },
+	{ "PSU Secondary Heatsink Diode Cooked Temperature (Tp0T)", SMC_KEY(Tp0T) },
+	{ "Right Fin Stack Temperature (Th1H)", SMC_KEY(Th1H) },
+	{ "RIO Temperature (Th0R)", SMC_KEY(Th0R) },
+	{ "S2 Camera Proximity Temperature (TS2P)", SMC_KEY(TS2P) },
+	{ "Secondary Power Supply Proximity Temperature (Tp1P)", SMC_KEY(Tp1P) },
+	{ "Skin 0 Raw Temperature (TS0p)", SMC_KEY(TS0p) },
+	{ "Skin 0 Virtual Temperature (TS0V)", SMC_KEY(TS0V) },
+	{ "Speaker Proximity 2 Temperature (TS1P)", SMC_KEY(TS1P) },
+	{ "Speaker Proximity Temperature (TS0P)", SMC_KEY(TS0P) },
+	{ "Speaker Remote 2 Temperature (TS1D)", SMC_KEY(TS1D) },
+	{ "Speaker Remote Temperature (TS0D)", SMC_KEY(TS0D) },
+	{ "SSD 0 OOBv3 Cooked Temperature Max (TH0X)", SMC_KEY(TH0X) },
+	{ "SSD 1 OOBv3 Cooked Temperature Max (TH1X)", SMC_KEY(TH1X) },
+	{ "Synthetic Bottom Skin Temperature (Ts0S)", SMC_KEY(Ts0S) },
+	{ "Synthetic DIMM Estimate (TM0S)", SMC_KEY(TM0S) },
+	{ "Synthetic Top Skin Temperature (Ts1S)", SMC_KEY(Ts1S) },
+	{ "TBT Proximity Raw Temperature (TI0p)", SMC_KEY(TI0p) },
+	{ "TBT Thermal Diode Raw Temperature (Te0t)", SMC_KEY(Te0t) },
+	{ "TBT Thermal Diode Temperature (Te0T)", SMC_KEY(Te0T) },
+	{ "Thermal Trap Hysteresis (TsTH)", SMC_KEY(TsTH) },
+	{ "Thermal Trap Threshold (TsTP)", SMC_KEY(TsTP) },
+	{ "Thermal Warning Hysteresis (TsHS)", SMC_KEY(TsHS) },
+	{ "Thermal Warning Threshold (TsWS)", SMC_KEY(TsWS) },
+	{ "Thunderbolt Die Temperature (THSP)", SMC_KEY(THSP) },
+	{ "Thunderbolt L Temperature (TTLD)", SMC_KEY(TTLD) },
+	{ "Thunderbolt Proximity Cooked Temperature (TI0T)", SMC_KEY(TI0T) },
+	{ "Thunderbolt R Temperature (TTRD)", SMC_KEY(TTRD) },
+	{ "Touchpad Temperature (Ts2S)", SMC_KEY(Ts2S) },
+	{ "Vent Proximity Temperature (TV0P)", SMC_KEY(TV0P) },
+	{ "WiFi Proximity Raw Temperature (TW0p)", SMC_KEY(TW0p) },
+	{ "Wireless Proximity Temperature (TW0P)", SMC_KEY(TW0P) },
+};
+
+static const struct macsmc_sensor_desc power_sensors[] = {
+	{ "12 V Rail (Pp0R)", SMC_KEY(Pp0R) },
+	{ "2nd GPU Plimit (MSTi)", SMC_KEY(MSTi) },
+	{ "3.3V In S0 Power (PR3C)", SMC_KEY(PR3C) },
+	{ "3.3V S0 Power (PR0C)", SMC_KEY(PR0C) },
+	{ "3.3V S3 Power (PR1C)", SMC_KEY(PR1C) },
+	{ "3.3V S5 Power (PR5C)", SMC_KEY(PR5C) },
+	{ "5V Lowside Power (P50R)", SMC_KEY(P50R) },
+	{ "A8 Load-Side Rail (PA8C)", SMC_KEY(PA8C) },
+	{ "Audio ScratchPad Register (MST1)", SMC_KEY(MST1) },
+	{ "Audio ScratchPad2 Register (MST2)", SMC_KEY(MST2) },
+	{ "Average Main Chipset Power (Pc0R)", SMC_KEY(Pc0R) },
+	{ "Battery Rail (PB0R)", SMC_KEY(PB0R) },
+	{ "Battery Rail (PBLC)", SMC_KEY(PBLC) },
+	{ "BL Rail 12 V (PBLR)", SMC_KEY(PBLR) },
+	{ "Chipset IMon Power (PCS1)", SMC_KEY(PCS1) },
+	{ "Chipset Lowside Power (PCS0)", SMC_KEY(PCS0) },
+	{ "CPU 1V5 Power (PD1R)", SMC_KEY(PD1R) },
+	{ "CPU Core (PCAC)", SMC_KEY(PCAC) },
+	{ "CPU Core 1 (PC0C)", SMC_KEY(PC0C) },
+	{ "CPU Core 2 (PC1C)", SMC_KEY(PC1C) },
+	{ "CPU Core 3 (PC2C)", SMC_KEY(PC2C) },
+	{ "CPU Core 4 (PC3C)", SMC_KEY(PC3C) },
+	{ "CPU Core 5 (PC4C)", SMC_KEY(PC4C) },
+	{ "CPU Core 6 (PC5C)", SMC_KEY(PC5C) },
+	{ "CPU Core 7 (PC6C)", SMC_KEY(PC6C) },
+	{ "CPU Core 8 (PC7C)", SMC_KEY(PC7C) },
+	{ "CPU Core IMON (PCAM)", SMC_KEY(PCAM) },
+	{ "CPU Cores (PCPC)", SMC_KEY(PCPC) },
+	{ "CPU DRAM (PCPD)", SMC_KEY(PCPD) },
+	{ "CPU GFX (PCPG)", SMC_KEY(PCPG) },
+	{ "CPU High-Side Average (PC0R)", SMC_KEY(PC0R) },
+	{ "CPU I/O High-Side (PC0I)", SMC_KEY(PC0I) },
+	{ "CPU I/O High-Side 2 (PC0M)", SMC_KEY(PC0M) },
+	{ "CPU I/O Load-Side Rail (PCIC)", SMC_KEY(PCIC) },
+	{ "CPU Integrated GPU (PC0G)", SMC_KEY(PC0G) },
+	{ "CPU Package Total PECI (PCPT)", SMC_KEY(PCPT) },
+	{ "CPU Package Total Power SMC (PCPR)", SMC_KEY(PCPR) },
+	{ "CPU Package Total Power SMC Load Sensors (PCLT)", SMC_KEY(PCLT) },
+	{ "CPU Plimit (MSTc)", SMC_KEY(MSTc) },
+	{ "CPU Rail (PC1R)", SMC_KEY(PC1R) },
+	{ "CPU Raw Package (PC0c)", SMC_KEY(PC0c) },
+	{ "CPU S0 Rail (PC5R)", SMC_KEY(PC5R) },
+	{ "CPU System Agent (PC0S)", SMC_KEY(PC0S) },
+	{ "CPU Total (PCPL)", SMC_KEY(PCPL) },
+	{ "CPU Total (PCTR)", SMC_KEY(PCTR) },
+	{ "CPU VccEDRAM (PCEC)", SMC_KEY(PCEC) },
+	{ "CPU VCCSA (PCSC)", SMC_KEY(PCSC) },
+	{ "CT Load-Side Rail (PCTC)", SMC_KEY(PCTC) },
+	{ "DC-In MLB S5 Rail (PD5R)", SMC_KEY(PD5R) },
+	{ "DC-In MLB Total (PDMR)", SMC_KEY(PDMR) },
+	{ "DDR (PM1C)", SMC_KEY(PM1C) },
+	{ "DDR Power 1.8V CPU (PM2C)", SMC_KEY(PM2C) },
+	{ "DDR S3 Power (PM3C)", SMC_KEY(PM3C) },
+	{ "DIMM Load-Side Power Per Fan RPM Squared (PM0f)", SMC_KEY(PM0f) },
+	{ "Discrete BMON On Battery Power (PB1R)", SMC_KEY(PB1R) },
+	{ "Drive 0 (PH0R)", SMC_KEY(PH0R) },
+	{ "Drive 0 Filtered Total Power (PH0F)", SMC_KEY(PH0F) },
+	{ "Drive 1 (PH1R)", SMC_KEY(PH1R) },
+	{ "Drive 1 Filtered Power (PH1F)", SMC_KEY(PH1F) },
+	{ "Drive 2 Power (PH2R)", SMC_KEY(PH2R) },
+	{ "External GPU 1.05 V (PG2C)", SMC_KEY(PG2C) },
+	{ "External GPU 1.35 V (PG3C)", SMC_KEY(PG3C) },
+	{ "External GPU 1.8 V (PG1C)", SMC_KEY(PG1C) },
+	{ "External GPU Rail 4 (PG4C)", SMC_KEY(PG4C) },
+	{ "F3 Load-Side Rail (PF3C)", SMC_KEY(PF3C) },
+	{ "Forced Idle Limit (MSTf)", SMC_KEY(MSTf) },
+	{ "GFX B Total Power (PG1R)", SMC_KEY(PG1R) },
+	{ "GPU (PG0C)", SMC_KEY(PG0C) },
+	{ "GPU 0 Rail (PG0R)", SMC_KEY(PG0R) },
+	{ "GPU A VDDCI Low Side Power (PG0S)", SMC_KEY(PG0S) },
+	{ "GPU Aux Input-Side Power (PG1A)", SMC_KEY(PG1A) },
+	{ "GPU Aux Load-Side Power (PG0A)", SMC_KEY(PG0A) },
+	{ "GPU B VDDCI Low Side Power (PG1S)", SMC_KEY(PG1S) },
+	{ "GPU Frame Buffer Input-Side Power (PG0F)", SMC_KEY(PG0F) },
+	{ "GPU Frame Buffer Input-Side Power (PG1F)", SMC_KEY(PG1F) },
+	{ "GPU Mem Ctl Load-Side Power (PG0M)", SMC_KEY(PG0M) },
+	{ "GPU Package Total Power SMC (PGPR)", SMC_KEY(PGPR) },
+	{ "GPU Plimit (MSTg)", SMC_KEY(MSTg) },
+	{ "GPU Total (PGTR)", SMC_KEY(PGTR) },
+	{ "GPU Uncore High-Side Power (PG0U)", SMC_KEY(PG0U) },
+	{ "GPU VDDCI Load-Side Power (PG0I)", SMC_KEY(PG0I) },
+	{ "GPU VRAM Load-Side Power (PG0V)", SMC_KEY(PG0V) },
+	{ "Heatpipe (PHPC)", SMC_KEY(PHPC) },
+	{ "HP Power Target (PTHC)", SMC_KEY(PTHC) },
+	{ "HP Power Throttle (MSLT)", SMC_KEY(MSLT) },
+	{ "I/O Board Total Power (PI0R)", SMC_KEY(PI0R) },
+	{ "Intel GPU IMON (PCGC)", SMC_KEY(PCGC) },
+	{ "Intel GPU IMON 2 (PCGM)", SMC_KEY(PCGM) },
+	{ "LCD Panel (PLDC)", SMC_KEY(PLDC) },
+	{ "Left Thunderbolt/I/O Rail (POLR)", SMC_KEY(POLR) },
+	{ "Left USB-C Load-Side Rail (PULC)", SMC_KEY(PULC) },
+	{ "Main 3.3 V Rail (PH02)", SMC_KEY(PH02) },
+	{ "Main 5 V Rail (PH05)", SMC_KEY(PH05) },
+	{ "MC Load-Side Rail (PMCC)", SMC_KEY(PMCC) },
+	{ "MCH (PN0C)", SMC_KEY(PN0C) },
+	{ "MCP Memory Power (PN1C)", SMC_KEY(PN1C) },
+	{ "MCP VCore Power (PN0R)", SMC_KEY(PN0R) },
+	{ "MEM Plimit (MSTm)", SMC_KEY(MSTm) },
+	{ "Memory Average (PM0C)", SMC_KEY(PM0C) },
+	{ "Memory Power (PZDM)", SMC_KEY(PZDM) },
+	{ "Memory Rail (PM0R)", SMC_KEY(PM0R) },
+	{ "Memory Total Power (PMTR)", SMC_KEY(PMTR) },
+	{ "Misc. Rail (PO0R)", SMC_KEY(PO0R) },
+	{ "ODD Power (PZOD)", SMC_KEY(PZOD) },
+	{ "Other 3.3V High Side Power (PO3R)", SMC_KEY(PO3R) },
+	{ "Other 5 V High-Side (PO5R)", SMC_KEY(PO5R) },
+	{ "PBus (PP0R)", SMC_KEY(PP0R) },
+	{ "PBus Battery Discharge (PPBR)", SMC_KEY(PPBR) },
+	{ "PCH Core Power (PSBC)", SMC_KEY(PSBC) },
+	{ "PCH Rail (PN1R)", SMC_KEY(PN1R) },
+	{ "Power Supply Filter Coefficient (PD0K)", SMC_KEY(PD0K) },
+	{ "Power Supply Filtered Power (PD0E)", SMC_KEY(PD0E) },
+	{ "Power Supply Max Error Filtered Output (PD0J)", SMC_KEY(PD0J) },
+	{ "Power Supply Power Filtered Then Adjusted (PD0F)", SMC_KEY(PD0F) },
+	{ "Power Supply Power Trend (PD0D)", SMC_KEY(PD0D) },
+	{ "Power Zone AP (PZAP)", SMC_KEY(PZAP) },
+	{ "Power Zone Backlight (PZBL)", SMC_KEY(PZBL) },
+	{ "Power Zone Storage (PZHD)", SMC_KEY(PZHD) },
+	{ "Right Thunderbolt/I/O Rail (PORR)", SMC_KEY(PORR) },
+	{ "Right USB-C Load-Side Rail (PURC)", SMC_KEY(PURC) },
+	{ "S0 5.0V Power (PR0R)", SMC_KEY(PR0R) },
+	{ "S1 Load-Side Rail (PS1C)", SMC_KEY(PS1C) },
+	{ "S2 Camera Power (PCMC)", SMC_KEY(PCMC) },
+	{ "S2 Camera Power (PS2C)", SMC_KEY(PS2C) },
+	{ "S3 3.30V Power (PR1R)", SMC_KEY(PR1R) },
+	{ "S3 5.0V Power (PR2R)", SMC_KEY(PR2R) },
+	{ "S5 3.30V Power (PR3R)", SMC_KEY(PR3R) },
+	{ "SL Rail 3.3 V (PSLC)", SMC_KEY(PSLC) },
+	{ "SSD 3.3 V Load-Side Rail 0 (PH0C)", SMC_KEY(PH0C) },
+	{ "SSD 3.3 V Load-Side Rail 1 (PH1C)", SMC_KEY(PH1C) },
+	{ "SSD Power (PHDC)", SMC_KEY(PHDC) },
+	{ "SSD Power (PSDC)", SMC_KEY(PSDC) },
+	{ "System Total, 1 s Delayed (PSTR)", SMC_KEY(PSTR) },
+	{ "T101 Accuator Power (PTPC)", SMC_KEY(PTPC) },
+	{ "T25 Track Pad Power (PTPR)", SMC_KEY(PTPR) },
+	{ "Thunderbolt Power (PHSC)", SMC_KEY(PHSC) },
+	{ "Thunderbolt Power (PHSP)", SMC_KEY(PHSP) },
+	{ "WiFi (PAPC)", SMC_KEY(PAPC) },
+	{ "Wifi Load-Side Power (PW0R)", SMC_KEY(PW0R) },
+	{ "Heatpipe Average (PZ0G)", SMC_KEY(PZ0G) },
+	{ "Heatpipe Average Target (PZ0E)", SMC_KEY(PZ0E) },
+	{ "Heatpipe Filtered (PZ0F)", SMC_KEY(PZ0F) },
+	{ "Heatpipe Throttle (PZ0T)", SMC_KEY(PZ0T) },
+	{ "CPU Average (PZ1G)", SMC_KEY(PZ1G) },
+	{ "CPU Filtered (PZ1F)", SMC_KEY(PZ1F) },
+	{ "CPU Target (PZ1E)", SMC_KEY(PZ1E) },
+	{ "CPU Throttle (PZ1T)", SMC_KEY(PZ1T) },
+	{ "dGPU Average (PZ2G)", SMC_KEY(PZ2G) },
+	{ "dGPU Filtered (PZ2F)", SMC_KEY(PZ2F) },
+	{ "dGPU Target (PZ2E)", SMC_KEY(PZ2E) },
+	{ "dGPU Throttle (PZ2T)", SMC_KEY(PZ2T) },
+	{ "Batt aux Average (PZ3G)", SMC_KEY(PZ3G) },
+	{ "Batt aux Filtered (PZ3F)", SMC_KEY(PZ3F) },
+	{ "Batt aux Target (PZ3E)", SMC_KEY(PZ3E) },
+	{ "Batt aux Throttle (PZ3T)", SMC_KEY(PZ3T) },
+	{ "Battery Average (PZ4G)", SMC_KEY(PZ4G) },
+	{ "Battery Filtered (PZ4F)", SMC_KEY(PZ4F) },
+	{ "Battery Target (PZ4E)", SMC_KEY(PZ4E) },
+	{ "Battery Throttle (PZ4T)", SMC_KEY(PZ4T) },
+	{ "Zone 5 Abstract Throttle (PZ5T)", SMC_KEY(PZ5T) },
+	{ "Zone 5 Average Power (PZ5G)", SMC_KEY(PZ5G) },
+	{ "Zone 5 Filtered Power (PZ5F)", SMC_KEY(PZ5F) },
+	{ "Zone 5 Target Power (PZ5E)", SMC_KEY(PZ5E) },
+};
+
+static const struct macsmc_sensor_desc current_sensors[] = {
+	{ "3.3V S0 Current (IR0C)", SMC_KEY(IR0C) },
+	{ "3.3V S0 Current (IR3C)", SMC_KEY(IR3C) },
+	{ "5.0V S0 Current (IR5C)", SMC_KEY(IR5C) },
+	{ "5V Lowside Current (I50R)", SMC_KEY(I50R) },
+	{ "Airport Current (IZOP)", SMC_KEY(IZOP) },
+	{ "Backlight Current (IBLC)", SMC_KEY(IBLC) },
+	{ "Battery Current (IBAC)", SMC_KEY(IBAC) },
+	{ "Battery Current Discrete (IP0R)", SMC_KEY(IP0R) },
+	{ "Battery Discharge Current MLB (IB0R)", SMC_KEY(IB0R) },
+	{ "Camera S2 Current (ICMC)", SMC_KEY(ICMC) },
+	{ "CPU 1V5 Current (ID1R)", SMC_KEY(ID1R) },
+	{ "CPU And MEM Current (IM0C)", SMC_KEY(IM0C) },
+	{ "CPU AXG Low-Side Current (IC0G)", SMC_KEY(IC0G) },
+	{ "CPU Core Current (IC0C)", SMC_KEY(IC0C) },
+	{ "CPU Core Current (ICS0)", SMC_KEY(ICS0) },
+	{ "CPU Current From CPU IMON (ICS1)", SMC_KEY(ICS1) },
+	{ "CPU DDR Current (IC3C)", SMC_KEY(IC3C) },
+	{ "CPU DDR Current (IM1C)", SMC_KEY(IM1C) },
+	{ "CPU High Side Current (IC0R)", SMC_KEY(IC0R) },
+	{ "CPU High Side Current From EMC1704 (IC1R)", SMC_KEY(IC1R) },
+	{ "CPU I/O High-Side Current (IC0I)", SMC_KEY(IC0I) },
+	{ "CPU Mem Low-Side Current (IC0M)", SMC_KEY(IC0M) },
+	{ "CPU Riser 12V High Side Current (ICTR)", SMC_KEY(ICTR) },
+	{ "CPU VCCIO Current (IC1C)", SMC_KEY(IC1C) },
+	{ "CPU VCCSA Current (IC2C)", SMC_KEY(IC2C) },
+	{ "CPU VSA Low Side Current (IC0S)", SMC_KEY(IC0S) },
+	{ "CSreg Current (Ic0R)", SMC_KEY(Ic0R) },
+	{ "DDR S3 Current (IM3C)", SMC_KEY(IM3C) },
+	{ "DIMM Current (IZDM)", SMC_KEY(IZDM) },
+	{ "DIMM Load-Side Current (IM0R)", SMC_KEY(IM0R) },
+	{ "Discrete Battery Current (IPB1)", SMC_KEY(IPB1) },
+	{ "Drive 0 12V Load-Side Current (IH02)", SMC_KEY(IH02) },
+	{ "Drive 0 5V Load-Side Current (IH05)", SMC_KEY(IH05) },
+	{ "Drive 1 3.3V Load-Side Current (IH1R)", SMC_KEY(IH1R) },
+	{ "Ext GPU 1.05V Current (IG2C)", SMC_KEY(IG2C) },
+	{ "Ext GPU 1.35V Current (IG3C)", SMC_KEY(IG3C) },
+	{ "Ext GPU High Side Current (IG0R)", SMC_KEY(IG0R) },
+	{ "GFX B Riser 12V High Side Current (IG1R)", SMC_KEY(IG1R) },
+	{ "GFX Vcore Current (IN0R)", SMC_KEY(IN0R) },
+	{ "GPU A VDDCI Low Side Current (IG0S)", SMC_KEY(IG0S) },
+	{ "GPU Aux Input-Side Current (IG1A)", SMC_KEY(IG1A) },
+	{ "GPU B VDDCI Low Side Current (IG1S)", SMC_KEY(IG1S) },
+	{ "GPU Core Current (IG0C)", SMC_KEY(IG0C) },
+	{ "GPU Core Input-Side Current (IG1C)", SMC_KEY(IG1C) },
+	{ "GPU FB High-Side Current (IG0F)", SMC_KEY(IG0F) },
+	{ "GPU FB Input-Side Current (IG1F)", SMC_KEY(IG1F) },
+	{ "GPU Uncore High-Side Current (IG0U)", SMC_KEY(IG0U) },
+	{ "GPU VDDCI Load-Side Current (IG0I)", SMC_KEY(IG0I) },
+	{ "HDD Current (IZHD)", SMC_KEY(IZHD) },
+	{ "I/O Board 12V High Side Current (II0R)", SMC_KEY(II0R) },
+	{ "Internal GFX Core Current (IN0C)", SMC_KEY(IN0C) },
+	{ "LCD Backlight Current (IZBL)", SMC_KEY(IZBL) },
+	{ "LCD Panel Current (ILDC)", SMC_KEY(ILDC) },
+	{ "LPDDR Current 1.8V (IM2C)", SMC_KEY(IM2C) },
+	{ "MCP DDR Current (IN1C)", SMC_KEY(IN1C) },
+	{ "Memory 1V5 High Side Current (IMTR)", SMC_KEY(IMTR) },
+	{ "ODD Current (IODC)", SMC_KEY(IODC) },
+	{ "ODD Current (IZOD)", SMC_KEY(IZOD) },
+	{ "Other 3.3V High Side Current (IO3R)", SMC_KEY(IO3R) },
+	{ "Other 5V High Side Current (IO5R)", SMC_KEY(IO5R) },
+	{ "Other High Side Current (IO0R)", SMC_KEY(IO0R) },
+	{ "Other High Side Current (IR1C)", SMC_KEY(IR1C) },
+	{ "PBus On Battery Current (IPBR)", SMC_KEY(IPBR) },
+	{ "PCH 1V05 Current (IN1R)", SMC_KEY(IN1R) },
+	{ "PCH Core Current (ISBC)", SMC_KEY(ISBC) },
+	{ "PCH/GPU/TBT 1.05V High-Side Current (IR1R)", SMC_KEY(IR1R) },
+	{ "Power Supply 12V Current Dynamic Filter Coeff (ID2L)", SMC_KEY(ID2L) },
+	{ "Power Supply 12V Current Filter Input (ID2I)", SMC_KEY(ID2I) },
+	{ "Power Supply 12V Current Filtered (ID2E)", SMC_KEY(ID2E) },
+	{ "Power Supply 12V Current Filtered And Adjusted (ID2F)", SMC_KEY(ID2F) },
+	{ "Power Supply 12V Current Max Error (ID2J)", SMC_KEY(ID2J) },
+	{ "Power Supply 12V Current Trend (ID2T)", SMC_KEY(ID2T) },
+	{ "S2 Camera Current (IS2C)", SMC_KEY(IS2C) },
+	{ "S3 5.0V Current (IR2C)", SMC_KEY(IR2C) },
+	{ "SSD 3V3 Low Side Current (IH0R)", SMC_KEY(IH0R) },
+	{ "SSD Current (IHDC)", SMC_KEY(IHDC) },
+	{ "SSD Current (ISDC)", SMC_KEY(ISDC) },
+	{ "Thunderbolt Current (IHSC)", SMC_KEY(IHSC) },
+	{ "Thunderbolt Current (IHSP)", SMC_KEY(IHSP) },
+	{ "Trackpad Actuator Current (ITPC)", SMC_KEY(ITPC) },
+	{ "Wifi Load-Side Current (IW0R)", SMC_KEY(IW0R) },
+	{ "WLAN Current (IZAP)", SMC_KEY(IZAP) },
+	{ "WLAN Lowside Current (IAPC)", SMC_KEY(IAPC) },
+};
+
+static const struct macsmc_sensor_desc voltage_sensors[] = {
+	{ "1.05V To CPU/PCH Lowside Voltage (VC1C)", SMC_KEY(VC1C) },
+	{ "1.2V To CPU/MEM Lowside Voltage (VM0C)", SMC_KEY(VM0C) },
+	{ "1.8V S3 Lowside Voltage (VM1C)", SMC_KEY(VM1C) },
+	{ "3.3V Load-Side Voltage (VR3R)", SMC_KEY(VR3R) },
+	{ "5V Lowside Voltage (V50R)", SMC_KEY(V50R) },
+	{ "ACC Voltage (VACC)", SMC_KEY(VACC) },
+	{ "ACDC Cooked Voltage (Vp0C)", SMC_KEY(Vp0C) },
+	{ "Backlight Output Voltage (VBLC)", SMC_KEY(VBLC) },
+	{ "Backlight Voltage (VZBL)", SMC_KEY(VZBL) },
+	{ "CPU AXG Low-Side Voltage (VC0G)", SMC_KEY(VC0G) },
+	{ "CPU Core Voltage (VC0C)", SMC_KEY(VC0C) },
+	{ "CPU Core Voltage (VCS0)", SMC_KEY(VCS0) },
+	{ "CPU High Side Voltage From EMC1704 (VC1R)", SMC_KEY(VC1R) },
+	{ "CPU Mem Load-Side Voltage (VC0M)", SMC_KEY(VC0M) },
+	{ "CPU Ripple Voltage (VCRC)", SMC_KEY(VCRC) },
+	{ "CPU Ripple Voltage (VCRP)", SMC_KEY(VCRP) },
+	{ "CPU VCCSA Voltage (VC2C)", SMC_KEY(VC2C) },
+	{ "CPU VSA Low Side Voltage (VC0S)", SMC_KEY(VC0S) },
+	{ "DIMM Load-Side Voltage (VM0R)", SMC_KEY(VM0R) },
+	{ "Drive 0 5V Load-Side Voltage (VH05)", SMC_KEY(VH05) },
+	{ "FIVR CPU Supply Voltage (VCFR)", SMC_KEY(VCFR) },
+	{ "GFX Vcore Voltage (VN0R)", SMC_KEY(VN0R) },
+	{ "GPU A VDDCI Low Side Voltage (VG0S)", SMC_KEY(VG0S) },
+	{ "GPU B VDDCI Low Side Voltage (VG1S)", SMC_KEY(VG1S) },
+	{ "GPU Core Input-Side Voltage (VG1C)", SMC_KEY(VG1C) },
+	{ "GPU Core Voltage (VG0C)", SMC_KEY(VG0C) },
+	{ "GPU Frame Buffer Input-Side Voltage (VG0F)", SMC_KEY(VG0F) },
+	{ "GPU Frame Buffer Input-Side Voltage (VG1F)", SMC_KEY(VG1F) },
+	{ "GPU Uncore High-Side Voltage (VG0U)", SMC_KEY(VG0U) },
+	{ "GPU VDDCI Load-Side Voltage (VG0I)", SMC_KEY(VG0I) },
+	{ "LCD IR Temp Sensor Voltage Raw Register (VL3t)", SMC_KEY(VL3t) },
+	{ "MCP Core Voltage (VN0C)", SMC_KEY(VN0C) },
+	{ "MCP Memory Voltage (VN1C)", SMC_KEY(VN1C) },
+	{ "Memory Voltage (VZDM)", SMC_KEY(VZDM) },
+	{ "ODD Voltage (VODC)", SMC_KEY(VODC) },
+	{ "ODD Voltage (VZOD)", SMC_KEY(VZOD) },
+	{ "PBus Rail Voltage (VP0R)", SMC_KEY(VP0R) },
+	{ "PCH Low-Side Voltage (VN1R)", SMC_KEY(VN1R) },
+	{ "PCH/GPU/TBT Load-Side Voltage (VR1R)", SMC_KEY(VR1R) },
+	{ "PSU Cooked Temperature As Voltage (VP0T)", SMC_KEY(VP0T) },
+	{ "SD Card Voltage (VSDC)", SMC_KEY(VSDC) },
+	{ "SSD 3.3V Low-Side Voltage (VH1R)", SMC_KEY(VH1R) },
+	{ "SSD 3V3 Low Side Voltage (VH0R)", SMC_KEY(VH0R) },
+	{ "SSD Voltage (VZHD)", SMC_KEY(VZHD) },
+	{ "T101 Accuator Voltage (VTPC)", SMC_KEY(VTPC) },
+	{ "WIFI Voltage (VZAP)", SMC_KEY(VZAP) },
+	{ "Wireless Voltage (VAPC)", SMC_KEY(VAPC) },
+};
+
+static int macsmc_hwmon_create_sensor_acpi(struct apple_smc *smc,
+		struct macsmc_hwmon_sensor *sensor, smc_key key, char *name, u32 attrs)
+{
+	int ret;
+
+	ret = apple_smc_get_key_info(smc, key, &sensor->info);
+	if (ret)
+		return ret;
+
+	sensor->macsmc_key = key;
+	sensor->label = name;
+	sensor->attrs = attrs;
+
+	return 0;
+}
 /*
  * Fan data is exposed by the SMC as multiple sensors.
  *
@@ -871,6 +1475,57 @@ static int macsmc_hwmon_create_fan(struct device *dev, struct apple_smc *smc,
 	return 0;
 }
 
+static int macsmc_hwmon_create_fan_acpi(struct apple_smc *smc,
+					struct macsmc_hwmon_fan *fan, int num)
+{
+	const char num_char = num + '0';
+	int ret;
+
+	ret = macsmc_hwmon_create_sensor_acpi(smc, &fan->now,
+			__SMC_KEY('F', num_char, 'A', 'c'), NULL, 0);
+	if (ret)
+		return ret;
+
+	if (apple_smc_key_exists(smc, __SMC_KEY('F', num_char, 'I', 'D'))) {
+		char buf[17];
+
+		ret = apple_smc_read(smc, __SMC_KEY('F', num_char, 'I', 'D'), buf, 16);
+		if (ret)
+			return ret;
+
+		buf[16] = '\0';
+		snprintf(fan->label, sizeof(fan->label), "%s", buf + 4);
+	} else
+		snprintf(fan->label, sizeof(fan->label), "Fan %d", num);
+
+	ret = macsmc_hwmon_create_sensor_acpi(smc, &fan->min,
+			__SMC_KEY('F', num_char, 'M', 'n'), NULL, 0);
+	if (ret)
+		return ret;
+
+	ret = macsmc_hwmon_create_sensor_acpi(smc, &fan->max,
+			__SMC_KEY('F', num_char, 'M', 'x'), NULL, 0);
+	if (ret)
+		return ret;
+
+	ret = macsmc_hwmon_create_sensor_acpi(smc, &fan->set,
+			__SMC_KEY('F', num_char, 'T', 'g'), NULL, 0);
+	if (ret)
+		return ret;
+
+	if (apple_smc_key_exists(smc, __SMC_KEY('F', num_char, 'M', 'd'))) {
+		ret = macsmc_hwmon_create_sensor_acpi(smc, &fan->mode,
+				__SMC_KEY('F', num_char, 'M', 'd'), NULL, 0);
+		if (ret)
+			return ret;
+	}
+
+	fan->attrs = HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_TARGET;
+	fan->manual = false;
+
+	return 0;
+}
+
 static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon,
 					 struct device_node *hwmon_node)
 {
@@ -907,7 +1562,7 @@ static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon,
 
 	if (n_fan) {
 		hwmon->fan.fans = devm_kcalloc(hwmon->dev, n_fan,
-					       sizeof(struct macsmc_hwmon_fan), GFP_KERNEL);
+						sizeof(struct macsmc_hwmon_fan), GFP_KERNEL);
 		if (!hwmon->fan.fans)
 			return -ENOMEM;
 
@@ -990,6 +1645,101 @@ static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon,
 	return 0;
 }
 
+static int macsmc_hwmon_register_sensors_acpi(
+	struct macsmc_hwmon *hwmon,
+	struct macsmc_hwmon_sensors *sensors,
+	const struct macsmc_sensor_desc *descs,
+	size_t num_descs,
+	u32 attrs)
+{
+	size_t count = 0;
+	size_t i;
+	int ret;
+
+	for (i = 0; i < num_descs; i++) {
+		if (apple_smc_key_exists(hwmon->smc, descs[i].key))
+			count++;
+	}
+
+	if (!count)
+		return 0;
+
+	sensors->sensors = devm_kcalloc(hwmon->dev, count,
+					sizeof(*sensors->sensors),
+					GFP_KERNEL);
+	if (!sensors->sensors)
+		return -ENOMEM;
+
+	sensors->count = 0;
+
+	for (i = 0; i < num_descs; i++) {
+		if (!apple_smc_key_exists(hwmon->smc, descs[i].key))
+			continue;
+
+		ret = macsmc_hwmon_create_sensor_acpi(hwmon->smc,
+						&sensors->sensors[sensors->count],
+						descs[i].key,
+						descs[i].name,
+						attrs);
+		if (ret)
+			return ret;
+
+		sensors->count++;
+	}
+
+	return 0;
+}
+
+static int macsmc_hwmon_populate_sensors_acpi(struct macsmc_hwmon *hwmon)
+{
+	int ret, i;
+
+	ret = apple_smc_read_u8(hwmon->smc, SMC_KEY(FNum), &hwmon->fan.count);
+	if (hwmon->fan.count && !ret) {
+		hwmon->fan.fans = devm_kcalloc(hwmon->dev, hwmon->fan.count,
+				sizeof(struct macsmc_hwmon_fan), GFP_KERNEL);
+		if (!hwmon->fan.fans)
+			return -ENOMEM;
+
+		hwmon->fan.has_old_manual = apple_smc_key_exists(hwmon->smc,
+				__SMC_KEY('F', 'S', '!', ' '));
+
+		for (i = 0; i < hwmon->fan.count; i++) {
+			macsmc_hwmon_create_fan_acpi(hwmon->smc, &hwmon->fan.fans[i], i);
+			if (ret)
+				return ret;
+		}
+	}
+	ret = macsmc_hwmon_register_sensors_acpi(hwmon, &hwmon->temp,
+						temp_sensors,
+						ARRAY_SIZE(temp_sensors),
+						HWMON_T_INPUT | HWMON_T_LABEL);
+	if (ret)
+		return ret;
+
+	ret = macsmc_hwmon_register_sensors_acpi(hwmon, &hwmon->power,
+						power_sensors,
+						ARRAY_SIZE(power_sensors),
+						HWMON_P_INPUT | HWMON_P_LABEL);
+	if (ret)
+		return ret;
+
+	ret = macsmc_hwmon_register_sensors_acpi(hwmon, &hwmon->curr,
+						current_sensors,
+						ARRAY_SIZE(current_sensors),
+						HWMON_C_INPUT | HWMON_C_LABEL);
+	if (ret)
+		return ret;
+
+	ret = macsmc_hwmon_register_sensors_acpi(hwmon, &hwmon->volt,
+						voltage_sensors,
+						ARRAY_SIZE(voltage_sensors),
+						HWMON_I_INPUT | HWMON_I_LABEL);
+	if (ret)
+		return ret;
+
+	return 0;
+}
 /* Create NULL-terminated config arrays */
 static void macsmc_hwmon_populate_configs(u32 *configs, const struct macsmc_hwmon_sensors *sensors)
 {
@@ -1090,9 +1840,9 @@ static int macsmc_hwmon_probe(struct platform_device *pdev)
 	/*
 	 * 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.
+	 * a hwmon node or the machine is a intel mac.
 	 */
-	if (!pdev->dev.of_node)
+	if (!pdev->dev.of_node && !smc->is_acpi)
 		return -ENODEV;
 
 	hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon),
@@ -1102,8 +1852,12 @@ static int macsmc_hwmon_probe(struct platform_device *pdev)
 
 	hwmon->dev = &pdev->dev;
 	hwmon->smc = smc;
+	platform_set_drvdata(pdev, hwmon);
 
-	ret = macsmc_hwmon_populate_sensors(hwmon, hwmon->dev->of_node);
+	if (hwmon->smc->is_acpi)
+		ret = macsmc_hwmon_populate_sensors_acpi(hwmon);
+	else
+		ret = macsmc_hwmon_populate_sensors(hwmon, hwmon->dev->of_node);
 	if (ret) {
 		dev_err(hwmon->dev, "Could not parse sensors\n");
 		return ret;
@@ -1142,6 +1896,30 @@ static int macsmc_hwmon_probe(struct platform_device *pdev)
 	return 0;
 }
 
+static int macsmc_hwmon_resume(struct platform_device *pdev)
+{
+	struct macsmc_hwmon *hwmon = dev_get_drvdata(&pdev->dev);
+
+	if (hwmon->smc->is_acpi && !hwmon->fan.has_old_manual) {
+		int idx;
+		int ret;
+
+		for (idx = 0; idx < hwmon->fan.count; idx++)
+			if (hwmon->fan.fans[idx].manual) {
+				ret = macsmc_hwmon_write_key(hwmon->smc,
+						&hwmon->fan.fans[idx].mode, 1);
+				if (ret < 0) {
+					dev_warn(&pdev->dev,
+						"Unable to reenable fan manual, disabling\n");
+					hwmon->fan.fans[idx].manual = false;
+				}
+			}
+
+	}
+
+	return 0;
+}
+
 static const struct of_device_id macsmc_hwmon_of_table[] = {
 	{ .compatible = "apple,smc-hwmon" },
 	{}
@@ -1150,6 +1928,7 @@ MODULE_DEVICE_TABLE(of, macsmc_hwmon_of_table);
 
 static struct platform_driver macsmc_hwmon_driver = {
 	.probe = macsmc_hwmon_probe,
+	.resume = macsmc_hwmon_resume,
 	.driver = {
 		.name = "macsmc-hwmon",
 		.of_match_table = macsmc_hwmon_of_table,
@@ -1160,3 +1939,4 @@ 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");
+MODULE_ALIAS("platform:macsmc-hwmon");
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 14/14] mfd/macsmc: add ioctl to access smc keys
  2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
                   ` (12 preceding siblings ...)
  2026-10-29  8:52 ` [RFC PATCH 13/14] hwmon: add support for x86 macs in macsmc-hwmon Atharva Tiwari
@ 2026-10-29  8:52 ` Atharva Tiwari
  13 siblings, 0 replies; 15+ messages in thread
From: Atharva Tiwari @ 2026-10-29  8:52 UTC (permalink / raw)
  Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Rafael J. Wysocki, Len Brown, Linus Walleij, Bartosz Golaszewski,
	Sven Peter, Janne Grunau, Neal Gompa, Guenter Roeck,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Lee Jones, Pavel Machek, Griffin Kroah-Hartman, Hans de Goede,
	Ilpo Järvinen, Sebastian Reichel, Alexandre Belloni,
	Wim Van Sebroeck, Jarkko Sakkinen, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, linux-kernel, linux-pci, linux-acpi, linux-gpio,
	asahi, linux-arm-kernel, linux-hwmon, linux-iio, linux-leds, mfd,
	platform-driver-x86, linux-pm, linux-rtc, linux-watchdog,
	linux-integrity, keyrings, linux-security-module

Add a /dev/smc device so userspace can talk to the SMC
directly. Some keys that need to be written aren't handled by
any driver, or affect several drivers at once, so they
can't be exposed cleanly through the kernel interfaces.

Keys already managed by kernel drivers can't be written this
way, to avoid conflicts.

Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
 drivers/mfd/macsmc-core.c  | 155 +++++++++++++++++++++++++++++++++++++
 include/linux/mfd/macsmc.h |  27 +++++++
 2 files changed, 182 insertions(+)

diff --git a/drivers/mfd/macsmc-core.c b/drivers/mfd/macsmc-core.c
index bc4200f57..a30f7386e 100644
--- a/drivers/mfd/macsmc-core.c
+++ b/drivers/mfd/macsmc-core.c
@@ -6,10 +6,13 @@
 
 #include <linux/bitfield.h>
 #include <linux/device.h>
+#include <linux/fs.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/macsmc.h>
+#include <linux/miscdevice.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
+#include <linux/uaccess.h>
 
 static const struct mfd_cell apple_smc_rtkit_devs[] = {
 	MFD_CELL_NAME("macsmc-input"),
@@ -112,6 +115,144 @@ static void apple_smc_disable_notifications(void *data)
 	smc->be->disable_notifications(smc->be_cookie);
 }
 
+static long apple_smc_fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct apple_smc *smc = file->private_data;
+	void __user *argp = (void __user *)arg;
+	int ret;
+
+	switch (cmd) {
+	case IOCTL_SMC_READ: {
+		struct ioctl_smc_rw_payload payload;
+		struct apple_smc_key_info info;
+
+		if (copy_from_user(&payload, argp, sizeof(payload)))
+			return -EFAULT;
+
+		ret = apple_smc_get_key_info(smc, payload.key, &info);
+		if (ret < 0)
+			return ret;
+
+		if (payload.size != info.size)
+			return -EINVAL;
+
+		if (payload.size > 32)
+			return -EINVAL;
+
+		ret = apple_smc_read(smc, payload.key, payload.buf, payload.size);
+		if (ret < 0)
+			return ret;
+
+		if (copy_to_user(argp, &payload, sizeof(payload)))
+			return -EFAULT;
+		return 0;
+	}
+	case IOCTL_SMC_WRITE: {
+		struct ioctl_smc_rw_payload payload;
+		struct apple_smc_key_info info;
+
+		if (copy_from_user(&payload, argp, sizeof(payload)))
+			return -EFAULT;
+
+		ret = apple_smc_get_key_info(smc, payload.key, &info);
+		if (ret < 0)
+			return ret;
+
+		if (payload.size != info.size)
+			return -EINVAL;
+
+		if (payload.size > 32)
+			return -EINVAL;
+
+		switch (payload.key) {
+		case __SMC_KEY('C', 'H', '0', 'I'):
+		case __SMC_KEY('C', 'H', '0', 'C'):
+		case __SMC_KEY('C', 'H', '0', 'K'):
+		case __SMC_KEY('C', 'H', '0', 'B'):
+		case __SMC_KEY('C', 'H', 'T', 'E'):
+		case __SMC_KEY('C', 'H', 'L', 'S'):
+		case __SMC_KEY('B', 'C', 'L', 'M'):
+		case __SMC_KEY('M', 'B', 'S', 'E'):
+		case __SMC_KEY('C', 'L', 'K', 'L'):
+		case __SMC_KEY('C', 'L', 'K', 'O'):
+		case __SMC_KEY('O', 'S', 'W', 'D'):
+		case __SMC_KEY('N', 'A', 'T', 'J'):
+		case __SMC_KEY('N', 'A', 'T', 'i'):
+		case __SMC_KEY('N', 'E', 'S', 'N'):
+		case __SMC_KEY('M', 'O', 'C', 'N'):
+		case __SMC_KEY('L', 'K', 'S', 'B'):
+		case __SMC_KEY('N', 'T', 'O', 'K'):
+		case __SMC_KEY('N', 'T', 'A', 'P'):
+		case __SMC_KEY('F', '0', 'T', 'g'):
+		case __SMC_KEY('F', '1', 'T', 'g'):
+		case __SMC_KEY('F', '0', 'M', 'd'):
+		case __SMC_KEY('F', '1', 'M', 'd'):
+		case __SMC_KEY('F', 'S', '!', ' '):
+			return -EPERM;
+		}
+
+		ret = apple_smc_write(smc, payload.key, payload.buf, payload.size);
+		if (ret < 0)
+			return ret;
+
+		return 0;
+	}
+	case IOCTL_SMC_GET_KEY_INDEX: {
+		struct ioctl_smc_index_payload payload;
+
+		if (copy_from_user(&payload, argp, sizeof(payload)))
+			return -EFAULT;
+
+		ret = apple_smc_get_key_by_index(smc, payload.index, &payload.key);
+		if (ret < 0)
+			return ret;
+
+		if (copy_to_user(argp, &payload, sizeof(payload)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case IOCTL_SMC_GET_KEY_INFO: {
+		struct ioctl_smc_info_payload payload;
+
+		if (copy_from_user(&payload, argp, sizeof(payload)))
+			return -EFAULT;
+
+		ret = apple_smc_get_key_info(smc, payload.key, &payload.info);
+		if (ret < 0)
+			return ret;
+
+		if (copy_to_user(argp, &payload, sizeof(payload)))
+			return -EFAULT;
+
+		return 0;
+	}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int apple_smc_fops_open(struct inode *inode, struct file *file)
+{
+	struct miscdevice *misc = file->private_data;
+
+	file->private_data = container_of(misc, struct apple_smc, miscdev);
+	return 0;
+}
+
+static const struct file_operations apple_smc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= apple_smc_fops_open,
+	.unlocked_ioctl	= apple_smc_fops_ioctl,
+	.compat_ioctl	= compat_ptr_ioctl,
+	.llseek		= noop_llseek,
+};
+
+static void apple_smc_misc_unregister(void *data)
+{
+	misc_deregister(data);
+}
+
 int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
 		void *cookie, struct blocking_notifier_head *event_handlers, bool is_acpi)
 {
@@ -150,6 +291,20 @@ int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
 	if (ret)
 		return ret;
 
+	smc->miscdev.minor = MISC_DYNAMIC_MINOR;
+	smc->miscdev.name = "smc";
+	smc->miscdev.fops = &apple_smc_fops;
+	smc->miscdev.parent = dev;
+	smc->miscdev.mode = 0600;
+
+	ret = misc_register(&smc->miscdev);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, apple_smc_misc_unregister, &smc->miscdev);
+	if (ret)
+		return ret;
+
 	if (smc->is_acpi) {
 		ret = devm_mfd_add_devices(dev, -1, apple_smc_acpi_common,
 			ARRAY_SIZE(apple_smc_acpi_common), NULL, 0, NULL);
diff --git a/include/linux/mfd/macsmc.h b/include/linux/mfd/macsmc.h
index 4b4368b99..29fd7b20c 100644
--- a/include/linux/mfd/macsmc.h
+++ b/include/linux/mfd/macsmc.h
@@ -8,6 +8,8 @@
 #ifndef _LINUX_MFD_MACSMC_H
 #define _LINUX_MFD_MACSMC_H
 
+#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
 #include <linux/soc/apple/rtkit.h>
 
 /**
@@ -125,6 +127,7 @@ struct apple_smc_backend_ops {
  * @is_acpi: Is the backend ACPI based
  * @mutex: Mutex for non-atomic mode
  * @lock: Spinlock for atomic mode
+ * @miscdev: Misc Device struct for ioctl ops
  */
 struct apple_smc {
 	struct device *dev;
@@ -141,8 +144,32 @@ struct apple_smc {
 
 	struct mutex mutex;
 	spinlock_t lock;
+
+	struct miscdevice miscdev;
+};
+
+struct ioctl_smc_rw_payload {
+	smc_key key;
+	size_t size;
+	u8 buf[32];
+};
+
+struct ioctl_smc_index_payload {
+	int index;
+	smc_key key;
+};
+
+struct ioctl_smc_info_payload {
+	smc_key key;
+	struct apple_smc_key_info info;
 };
 
+#define APPLE_SMC_MAGIC 'S'
+#define IOCTL_SMC_READ           _IOWR(APPLE_SMC_MAGIC, 1, struct ioctl_smc_rw_payload)
+#define IOCTL_SMC_WRITE          _IOW(APPLE_SMC_MAGIC, 2, struct ioctl_smc_rw_payload)
+#define IOCTL_SMC_GET_KEY_INDEX  _IOWR(APPLE_SMC_MAGIC, 3, struct ioctl_smc_index_payload)
+#define IOCTL_SMC_GET_KEY_INFO   _IOWR(APPLE_SMC_MAGIC, 4, struct ioctl_smc_info_payload)
+
 /**
  * apple_smc_read - Read size bytes from given SMC key into buf
  * @smc: Pointer to apple_smc struct
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-10-29  9:02 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29  8:51 [RFC PATCH 00/14] Add macsmc support for Intel Macs, replacing applesmc Atharva Tiwari
2026-10-29  8:51 ` [RFC PATCH 01/14] treewide: Add backend for macsmc Atharva Tiwari
2026-10-29  8:51 ` [RFC PATCH 02/14] platform/x86: add backend for macsmc in intel macs Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 03/14] watchdog: add driver for apple smc Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 04/14] iio/light: add new driver for macsmc Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 05/14] iio/accel: add driver for apple SMS Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 06/14] leds: add support for macsmc kbl Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 07/14] rtc: add support for x86 macs in macsmc-rtc Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 08/14] treewide: Add a flag to detect the Apple T2 chip Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 09/14] treewide: disable other rtc's in t2 macs Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 10/14] power/supply: add support for x86 macs in macsmc-power Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 11/14] acpi: disable battery and adapter for x86 apple machines Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 12/14] hwmon/macsmc: add more types Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 13/14] hwmon: add support for x86 macs in macsmc-hwmon Atharva Tiwari
2026-10-29  8:52 ` [RFC PATCH 14/14] mfd/macsmc: add ioctl to access smc keys Atharva Tiwari

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