Skip to content

Commit 36eff9f

Browse files
committed
macsmc-input: poll MSLD for lid state, fix spurious s2idle wake
SMC lid events (0x7203) show up in the SMC firmware syslog but never get delivered to Linux via RTKit notifications, so lid open/close is invisible to userspace. Switch to polling the MSLD key every second with a delayed_work and reporting SW_LID when it changes. 2-poll debounce because MSLD bounces during DP disconnect events. Also fixes spurious wake: the SMC fires a fake BTN_TOUCHID press+release within ~1ms of entering s2idle. Skip the first 2 button events after pm_prepare, real presses after that go through normally. Other fixes: - Add remove callback (old driver had none, leaked notifier + work) - pm_complete cancels and reschedules lid_work so polling survives suspend/resume cycles - Expose MSLD as sysfs attribute (msld_state) for debugging - Init pending_lid_state from MSLD at probe Tested on M1 MacBook Air (J313), fairydust 6.18.10. Signed-off-by: areofyl <areofyl@users.noreply.github.com>
1 parent 77e0fe0 commit 36eff9f

1 file changed

Lines changed: 109 additions & 7 deletions

File tree

drivers/input/misc/macsmc-input.c

Lines changed: 109 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ struct macsmc_input {
2828
struct input_dev *input;
2929
struct notifier_block nb;
3030
bool wakeup_mode;
31+
int ignore_btn_count;
32+
struct delayed_work lid_work;
33+
u8 pending_lid_state;
34+
u8 debounce_lid_state;
35+
int debounce_count;
36+
bool have_lid;
3137
};
3238

3339
#define SMC_EV_BTN 0x7201
@@ -46,11 +52,21 @@ static void macsmc_input_event_button(struct macsmc_input *smcin, unsigned long
4652
switch (button) {
4753
case BTN_POWER:
4854
case BTN_TOUCHID:
55+
/*
56+
* The SMC fires a spurious BTN_TOUCHID press+release ~1ms
57+
* after entering s2idle. Skip those first 2 events, then
58+
* let real presses through normally.
59+
*/
60+
if (smcin->wakeup_mode && smcin->ignore_btn_count > 0) {
61+
smcin->ignore_btn_count--;
62+
return;
63+
}
64+
4965
pm_wakeup_dev_event(smcin->dev, 0, (smcin->wakeup_mode && state));
5066
/*
5167
* Suppress KEY_POWER reports when suspended to avoid powering down
5268
* immediately after waking from s2idle.
53-
* */
69+
*/
5470
if (smcin->wakeup_mode)
5571
return;
5672

@@ -79,14 +95,75 @@ static void macsmc_input_event_button(struct macsmc_input *smcin, unsigned long
7995
}
8096
}
8197

98+
static void macsmc_input_lid_work(struct work_struct *work)
99+
{
100+
struct macsmc_input *smcin = container_of(work, struct macsmc_input, lid_work.work);
101+
u8 val;
102+
int ret;
103+
104+
ret = apple_smc_read_u8(smcin->smc, SMC_KEY(MSLD), &val);
105+
if (ret) {
106+
dev_warn_once(smcin->dev, "MSLD read failed: %d\n", ret);
107+
goto resched;
108+
}
109+
110+
/*
111+
* Debounce: DP disconnect can cause MSLD to bounce briefly.
112+
* Require 2 consecutive polls with the same changed value
113+
* before reporting a lid state change.
114+
*/
115+
if (val != smcin->debounce_lid_state) {
116+
smcin->debounce_lid_state = val;
117+
smcin->debounce_count = 1;
118+
} else {
119+
smcin->debounce_count++;
120+
}
121+
122+
if (smcin->debounce_lid_state != smcin->pending_lid_state &&
123+
smcin->debounce_count >= 2) {
124+
dev_info(smcin->dev, "lid state changed: %d -> %d\n",
125+
smcin->pending_lid_state, smcin->debounce_lid_state);
126+
smcin->pending_lid_state = smcin->debounce_lid_state;
127+
input_report_switch(smcin->input, SW_LID, smcin->pending_lid_state);
128+
input_sync(smcin->input);
129+
}
130+
131+
resched:
132+
schedule_delayed_work(&smcin->lid_work, msecs_to_jiffies(1000));
133+
}
134+
82135
static void macsmc_input_event_lid(struct macsmc_input *smcin, unsigned long event)
83136
{
84-
u8 lid_state = !!((event >> 8) & 0xff);
137+
/*
138+
* SMC lid events (0x7203) are not reliably delivered via RTKit
139+
* notifications on all machines. Lid state is polled via the
140+
* MSLD key in lid_work instead.
141+
*/
142+
}
143+
144+
static ssize_t msld_state_show(struct device *dev,
145+
struct device_attribute *attr, char *buf)
146+
{
147+
struct macsmc_input *smcin = dev_get_drvdata(dev);
148+
u8 val;
149+
int ret;
85150

86-
pm_wakeup_dev_event(smcin->dev, 0, (smcin->wakeup_mode && !lid_state));
87-
input_report_switch(smcin->input, SW_LID, lid_state);
88-
input_sync(smcin->input);
151+
if (!smcin->have_lid)
152+
return sysfs_emit(buf, "unsupported\n");
153+
154+
ret = apple_smc_read_u8(smcin->smc, SMC_KEY(MSLD), &val);
155+
if (ret)
156+
return sysfs_emit(buf, "error %d\n", ret);
157+
158+
return sysfs_emit(buf, "%d\n", val);
89159
}
160+
static DEVICE_ATTR_RO(msld_state);
161+
162+
static struct attribute *macsmc_input_attrs[] = {
163+
&dev_attr_msld_state.attr,
164+
NULL,
165+
};
166+
ATTRIBUTE_GROUPS(macsmc_input);
90167

91168
static int macsmc_input_event(struct notifier_block *nb, unsigned long event, void *data)
92169
{
@@ -125,7 +202,9 @@ static int macsmc_input_probe(struct platform_device *pdev)
125202

126203
smcin->dev = &pdev->dev;
127204
smcin->smc = smc;
205+
smcin->have_lid = have_lid;
128206
platform_set_drvdata(pdev, smcin);
207+
INIT_DELAYED_WORK(&smcin->lid_work, macsmc_input_lid_work);
129208

130209
smcin->input = devm_input_allocate_device(&pdev->dev);
131210
if (!smcin->input)
@@ -143,10 +222,14 @@ static int macsmc_input_probe(struct platform_device *pdev)
143222
u8 val;
144223

145224
error = apple_smc_read_u8(smc, SMC_KEY(MSLD), &val);
146-
if (error < 0)
225+
if (error < 0) {
147226
dev_warn(&pdev->dev, "Failed to read initial lid state\n");
148-
else
227+
} else {
228+
smcin->pending_lid_state = val;
229+
smcin->debounce_lid_state = val;
230+
smcin->debounce_count = 2;
149231
input_report_switch(smcin->input, SW_LID, val);
232+
}
150233
}
151234

152235
if (have_power) {
@@ -172,22 +255,39 @@ static int macsmc_input_probe(struct platform_device *pdev)
172255

173256
device_init_wakeup(&pdev->dev, true);
174257

258+
if (have_lid)
259+
schedule_delayed_work(&smcin->lid_work, msecs_to_jiffies(1000));
260+
175261
return 0;
176262
}
177263

264+
static void macsmc_input_remove(struct platform_device *pdev)
265+
{
266+
struct macsmc_input *smcin = platform_get_drvdata(pdev);
267+
struct apple_smc *smc = smcin->smc;
268+
269+
cancel_delayed_work_sync(&smcin->lid_work);
270+
blocking_notifier_chain_unregister(&smc->event_handlers, &smcin->nb);
271+
}
272+
178273
static int macsmc_input_pm_prepare(struct device *dev)
179274
{
180275
struct macsmc_input *smcin = dev_get_drvdata(dev);
181276

182277
smcin->wakeup_mode = true;
278+
smcin->ignore_btn_count = 2;
183279
return 0;
184280
}
185281

186282
static void macsmc_input_pm_complete(struct device *dev)
187283
{
188284
struct macsmc_input *smcin = dev_get_drvdata(dev);
189285

286+
cancel_delayed_work_sync(&smcin->lid_work);
190287
smcin->wakeup_mode = false;
288+
smcin->ignore_btn_count = 0;
289+
if (smcin->have_lid)
290+
schedule_delayed_work(&smcin->lid_work, msecs_to_jiffies(1000));
191291
}
192292

193293
static const struct dev_pm_ops macsmc_input_pm_ops = {
@@ -199,8 +299,10 @@ static struct platform_driver macsmc_input_driver = {
199299
.driver = {
200300
.name = "macsmc-input",
201301
.pm = &macsmc_input_pm_ops,
302+
.dev_groups = macsmc_input_groups,
202303
},
203304
.probe = macsmc_input_probe,
305+
.remove = macsmc_input_remove,
204306
};
205307
module_platform_driver(macsmc_input_driver);
206308

0 commit comments

Comments
 (0)