[PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1
From: Shih-Yuan Lee
Date: Sat Jul 11 2026 - 05:22:25 EST
On MacBook8,1 (early 2015 12" MacBook), the LPSS SPI controller's DMA
handshake and interrupt routing frequently fail or time out on boot,
causing the keyboard and trackpad to become unresponsive.
Address this by introducing a DMI quirk and a `force_pio` module
parameter. If either is true, override the SPI controller's `can_dma`
callback to a helper that always returns false. This forces all SPI
transfers to fall back to the reliable PIO mode.
To ensure correct timing and architectural safety:
1. Perform the override at the very beginning of applespi_probe() so
that all early initialization transfers use PIO.
2. In case of probe failures or module unload (remove), unconditionally
restore the original `can_dma` callback (even if it was NULL) to
prevent any execute-after-free or dangling pointer vulnerability in
the shared host controller.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=108331
Signed-off-by: Shih-Yuan Lee <fourdollars@xxxxxxxxxx>
---
drivers/input/keyboard/applespi.c | 66 +++++++++++++++++++++++++------
1 file changed, 54 insertions(+), 12 deletions(-)
diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
index b5ff71cd5a70..79e5cb5001c7 100644
--- a/drivers/input/keyboard/applespi.c
+++ b/drivers/input/keyboard/applespi.c
@@ -46,6 +46,7 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/efi.h>
+#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/ktime.h>
@@ -110,6 +111,10 @@ module_param_string(touchpad_dimensions, touchpad_dimensions,
sizeof(touchpad_dimensions), 0444);
MODULE_PARM_DESC(touchpad_dimensions, "The pixel dimensions of the touchpad, as XxY+W+H .");
+static bool applespi_force_pio;
+module_param_named(force_pio, applespi_force_pio, bool, 0444);
+MODULE_PARM_DESC(force_pio, "Force PIO mode (disables DMA) for SPI transfers. ([0] = disabled, 1 = enabled)");
+
/**
* struct keyboard_protocol - keyboard message.
* message.type = 0x0110, message.length = 0x000a
@@ -431,6 +436,10 @@ struct applespi_data {
int tp_dim_max_x;
int tp_dim_min_y;
int tp_dim_max_y;
+
+ bool (*original_can_dma)(struct spi_controller *controller,
+ struct spi_device *spi,
+ struct spi_transfer *xfer);
};
static const unsigned char applespi_scancodes[] = {
@@ -1605,6 +1614,13 @@ static void applespi_save_bl_level(struct applespi_data *applespi,
"Error saving backlight level to EFI vars: 0x%lx\n", sts);
}
+static bool applespi_can_not_dma(struct spi_controller *controller,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ return false;
+}
+
static int applespi_probe(struct spi_device *spi)
{
struct applespi_data *applespi;
@@ -1612,6 +1628,7 @@ static int applespi_probe(struct spi_device *spi)
acpi_status acpi_sts;
int sts, i;
unsigned long long gpe, usb_status;
+ bool override_dma = applespi_force_pio || dmi_match(DMI_PRODUCT_NAME, "MacBook8,1");
/* check if the USB interface is present and enabled already */
acpi_sts = acpi_evaluate_integer(spi_handle, "UIST", NULL, &usb_status);
@@ -1628,6 +1645,13 @@ static int applespi_probe(struct spi_device *spi)
applespi->spi = spi;
+ /* Save original can_dma and override if requested or needed */
+ applespi->original_can_dma = spi->controller->can_dma;
+ if (override_dma) {
+ dev_info(&spi->dev, "Disabling DMA for SPI to force PIO mode\n");
+ spi->controller->can_dma = applespi_can_not_dma;
+ }
+
INIT_WORK(&applespi->work, applespi_worker);
/* store the driver data */
@@ -1645,8 +1669,10 @@ static int applespi_probe(struct spi_device *spi)
GFP_KERNEL);
if (!applespi->tx_buffer || !applespi->tx_status ||
- !applespi->rx_buffer || !applespi->msg_buf)
- return -ENOMEM;
+ !applespi->rx_buffer || !applespi->msg_buf) {
+ sts = -ENOMEM;
+ goto err_restore_dma;
+ }
/* set up our spi messages */
applespi_setup_read_txfrs(applespi);
@@ -1658,7 +1684,8 @@ static int applespi_probe(struct spi_device *spi)
dev_err(&applespi->spi->dev,
"Failed to get SIEN ACPI method handle: %s\n",
acpi_format_exception(acpi_sts));
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
acpi_sts = acpi_get_handle(spi_handle, "SIST", &applespi->sist);
@@ -1666,23 +1693,26 @@ static int applespi_probe(struct spi_device *spi)
dev_err(&applespi->spi->dev,
"Failed to get SIST ACPI method handle: %s\n",
acpi_format_exception(acpi_sts));
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
/* switch on the SPI interface */
sts = applespi_setup_spi(applespi);
if (sts)
- return sts;
+ goto err_restore_dma;
sts = applespi_enable_spi(applespi);
if (sts)
- return sts;
+ goto err_restore_dma;
/* setup the keyboard input dev */
applespi->keyboard_input_dev = devm_input_allocate_device(&spi->dev);
- if (!applespi->keyboard_input_dev)
- return -ENOMEM;
+ if (!applespi->keyboard_input_dev) {
+ sts = -ENOMEM;
+ goto err_restore_dma;
+ }
applespi->keyboard_input_dev->name = "Apple SPI Keyboard";
applespi->keyboard_input_dev->phys = "applespi/input0";
@@ -1717,7 +1747,7 @@ static int applespi_probe(struct spi_device *spi)
if (sts) {
dev_err(&applespi->spi->dev,
"Unable to register keyboard input device (%d)\n", sts);
- return -ENODEV;
+ goto err_restore_dma;
}
/*
@@ -1729,7 +1759,8 @@ static int applespi_probe(struct spi_device *spi)
dev_err(&applespi->spi->dev,
"Failed to obtain GPE for SPI slave device: %s\n",
acpi_format_exception(acpi_sts));
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
applespi->gpe = (int)gpe;
@@ -1740,7 +1771,8 @@ static int applespi_probe(struct spi_device *spi)
dev_err(&applespi->spi->dev,
"Failed to install GPE handler for GPE %d: %s\n",
applespi->gpe, acpi_format_exception(acpi_sts));
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
applespi->suspended = false;
@@ -1751,7 +1783,8 @@ static int applespi_probe(struct spi_device *spi)
"Failed to enable GPE handler for GPE %d: %s\n",
applespi->gpe, acpi_format_exception(acpi_sts));
acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify);
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
/* trigger touchpad setup */
@@ -1789,6 +1822,11 @@ static int applespi_probe(struct spi_device *spi)
&applespi_tp_dim_fops);
return 0;
+
+err_restore_dma:
+ if (override_dma)
+ spi->controller->can_dma = applespi->original_can_dma;
+ return sts;
}
static void applespi_drain_writes(struct applespi_data *applespi)
@@ -1813,6 +1851,7 @@ static void applespi_drain_reads(struct applespi_data *applespi)
static void applespi_remove(struct spi_device *spi)
{
struct applespi_data *applespi = spi_get_drvdata(spi);
+ bool override_dma = applespi_force_pio || dmi_match(DMI_PRODUCT_NAME, "MacBook8,1");
applespi_drain_writes(applespi);
@@ -1822,6 +1861,9 @@ static void applespi_remove(struct spi_device *spi)
applespi_drain_reads(applespi);
+ if (override_dma)
+ spi->controller->can_dma = applespi->original_can_dma;
+
debugfs_remove_recursive(applespi->debugfs_root);
}
--
2.39.5