Skip to content

Commit 2d4aa58

Browse files
committed
Merge branch 'bits/110-smc' into asahi-wip
2 parents b69fe19 + a57409a commit 2d4aa58

11 files changed

Lines changed: 1419 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2+
%YAML 1.2
3+
---
4+
$id: http://devicetree.org/schemas/hwmon/apple,smc-hwmon.yaml#
5+
$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+
title: Apple SMC Hardware Monitoring
8+
9+
description:
10+
Apple's System Management Controller (SMC) exposes a vast array of
11+
hardware monitoring sensors, including temperature probes, current and
12+
voltage sense, power meters, and fan speeds. It also provides endpoints
13+
to manually control the speed of each fan individually. Each Apple
14+
Silicon device exposes a different set of endpoints via SMC keys. This
15+
is true even when two machines share an SoC. The CPU core temperature
16+
sensor keys on an M1 Mac mini are different to those on an M1 MacBook
17+
Pro, for example.
18+
19+
maintainers:
20+
- James Calligeros <jcalligeros99@gmail.com>
21+
22+
$defs:
23+
sensor:
24+
type: object
25+
26+
properties:
27+
apple,key-id:
28+
$ref: /schemas/types.yaml#/definitions/string
29+
pattern: "^[A-Za-z0-9]{4}$"
30+
description: The SMC FourCC key of the desired sensor.
31+
Must match the node's suffix.
32+
33+
label:
34+
description: Human-readable name for the sensor
35+
36+
required:
37+
- apple,key-id
38+
39+
properties:
40+
compatible:
41+
const: apple,smc-hwmon
42+
43+
patternProperties:
44+
"^current-[A-Za-z0-9]{4}$":
45+
$ref: "#/$defs/sensor"
46+
unevaluatedProperties: false
47+
48+
"^fan-[A-Za-z0-9]{4}$":
49+
$ref: "#/$defs/sensor"
50+
unevaluatedProperties: false
51+
52+
properties:
53+
apple,fan-minimum:
54+
$ref: /schemas/types.yaml#/definitions/string
55+
pattern: "^[A-Za-z0-9]{4}$"
56+
description: SMC key containing the fan's minimum speed
57+
58+
apple,fan-maximum:
59+
$ref: /schemas/types.yaml#/definitions/string
60+
pattern: "^[A-Za-z0-9]{4}$"
61+
description: SMC key containing the fan's maximum speed
62+
63+
apple,fan-target:
64+
$ref: /schemas/types.yaml#/definitions/string
65+
pattern: "^[A-Za-z0-9]{4}$"
66+
description: Writeable endpoint for setting desired fan speed
67+
68+
apple,fan-mode:
69+
$ref: /schemas/types.yaml#/definitions/string
70+
pattern: "^[A-Za-z0-9]{4}$"
71+
description: Writeable key to enable/disable manual fan control
72+
73+
74+
"^power-[A-Za-z0-9]{4}$":
75+
$ref: "#/$defs/sensor"
76+
unevaluatedProperties: false
77+
78+
"^temperature-[A-Za-z0-9]{4}$":
79+
$ref: "#/$defs/sensor"
80+
unevaluatedProperties: false
81+
82+
"^voltage-[A-Za-z0-9]{4}$":
83+
$ref: "#/$defs/sensor"
84+
unevaluatedProperties: false
85+
86+
additionalProperties: false

Documentation/devicetree/bindings/mfd/apple,smc.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ properties:
4949
rtc:
5050
$ref: /schemas/rtc/apple,smc-rtc.yaml
5151

52+
hwmon:
53+
$ref: /schemas/hwmon/apple,smc-hwmon.yaml
54+
5255
additionalProperties: false
5356

5457
required:
@@ -89,5 +92,38 @@ examples:
8992
nvmem-cells = <&rtc_offset>;
9093
nvmem-cell-names = "rtc_offset";
9194
};
95+
96+
hwmon {
97+
compatible = "apple,smc-hwmon";
98+
99+
current-ID0R {
100+
apple,key-id = "ID0R";
101+
label = "AC Input Current";
102+
};
103+
104+
fan-F0Ac {
105+
apple,key-id = "F0Ac";
106+
apple,fan-minimum = "F0Mn";
107+
apple,fan-maximum = "F0Mx";
108+
apple,fan-target = "F0Tg";
109+
apple,fan-mode = "F0Md";
110+
label = "Fan 1";
111+
};
112+
113+
power-PSTR {
114+
apple,key-id = "PSTR";
115+
label = "Total System Power";
116+
};
117+
118+
temperature-TW0P {
119+
apple,key-id = "TW0P";
120+
label = "WiFi/BT Module Temperature";
121+
};
122+
123+
voltage-VD0R {
124+
apple,key-id = "VD0R";
125+
label = "AC Input Voltage";
126+
};
127+
};
92128
};
93129
};

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,6 +2460,7 @@ F: Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml
24602460
F: Documentation/devicetree/bindings/dma/apple,admac.yaml
24612461
F: Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml
24622462
F: Documentation/devicetree/bindings/gpu/apple,agx.yaml
2463+
F: Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml
24632464
F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
24642465
F: Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml
24652466
F: Documentation/devicetree/bindings/interrupt-controller/apple,*
@@ -2493,6 +2494,7 @@ F: drivers/hwmon/macsmc-hwmon.c
24932494
F: drivers/pmdomain/apple/
24942495
F: drivers/i2c/busses/i2c-pasemi-core.c
24952496
F: drivers/i2c/busses/i2c-pasemi-platform.c
2497+
F: drivers/input/misc/macsmc-input.c
24962498
F: drivers/input/touchscreen/apple_z2.c
24972499
F: drivers/iommu/apple-dart.c
24982500
F: drivers/iommu/io-pgtable-dart.c

drivers/input/misc/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,4 +1003,15 @@ config INPUT_STPMIC1_ONKEY
10031003
To compile this driver as a module, choose M here: the
10041004
module will be called stpmic1_onkey.
10051005

1006+
config INPUT_MACSMC_INPUT
1007+
tristate "Apple Mac SMC lid/buttons"
1008+
depends on MFD_MACSMC
1009+
help
1010+
Say Y here if you want to use the input events delivered via the
1011+
SMC controller on Apple Mac machines using the macsmc driver.
1012+
This includes lid open/close and the power button.
1013+
1014+
To compile this driver as a module, choose M here: the
1015+
module will be called macsmc-input.
1016+
10061017
endif

drivers/input/misc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o
5252
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
5353
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
5454
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
55+
obj-$(CONFIG_INPUT_MACSMC_INPUT) += macsmc-input.o
5556
obj-$(CONFIG_INPUT_MAX7360_ROTARY) += max7360-rotary.o
5657
obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o
5758
obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o

drivers/input/misc/macsmc-input.c

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/*
3+
* Apple SMC input event driver
4+
* Copyright The Asahi Linux Contributors
5+
*
6+
* This driver exposes HID events from the SMC as an input device.
7+
* This includes the lid open/close and power button notifications.
8+
*/
9+
10+
#include <linux/device.h>
11+
#include <linux/input.h>
12+
#include <linux/mfd/core.h>
13+
#include <linux/mfd/macsmc.h>
14+
#include <linux/module.h>
15+
#include <linux/reboot.h>
16+
17+
/**
18+
* struct macsmc_input
19+
* @dev: Underlying struct device for the input sub-device
20+
* @smc: Pointer to apple_smc struct of the mfd parent
21+
* @input: Allocated input_dev; devres managed
22+
* @nb: Notifier block used for incoming events from SMC (e.g. button pressed down)
23+
* @wakeup_mode: Set to true when system is suspended and power button events should wake it
24+
*/
25+
struct macsmc_input {
26+
struct device *dev;
27+
struct apple_smc *smc;
28+
struct input_dev *input;
29+
struct notifier_block nb;
30+
bool wakeup_mode;
31+
};
32+
33+
#define SMC_EV_BTN 0x7201
34+
#define SMC_EV_LID 0x7203
35+
36+
#define BTN_POWER 0x01 /* power button on e.g. Mac Mini chasis pressed */
37+
#define BTN_TOUCHID 0x06 /* combined TouchID / power button on MacBooks pressed */
38+
#define BTN_POWER_HELD_SHORT 0xfe /* power button briefly held down */
39+
#define BTN_POWER_HELD_LONG 0x00 /* power button held down; sent just before forced poweroff */
40+
41+
static void macsmc_input_event_button(struct macsmc_input *smcin, unsigned long event)
42+
{
43+
u8 button = (event >> 8) & 0xff;
44+
u8 state = !!(event & 0xff);
45+
46+
switch (button) {
47+
case BTN_POWER:
48+
case BTN_TOUCHID:
49+
pm_wakeup_dev_event(smcin->dev, 0, (smcin->wakeup_mode && state));
50+
/*
51+
* Suppress KEY_POWER reports when suspended to avoid powering down
52+
* immediately after waking from s2idle.
53+
* */
54+
if (smcin->wakeup_mode)
55+
return;
56+
57+
input_report_key(smcin->input, KEY_POWER, state);
58+
input_sync(smcin->input);
59+
break;
60+
case BTN_POWER_HELD_SHORT: /* power button held down; ignore */
61+
break;
62+
case BTN_POWER_HELD_LONG:
63+
/*
64+
* If we get here the power button has been held down for a while and
65+
* we have about 4 seconds before forced power-off is triggered by SMC.
66+
* Try to do an emergency shutdown to make sure the NVMe cache is
67+
* flushed. macOS actually does this by panicing (!)...
68+
*/
69+
if (state) {
70+
dev_crit(smcin->dev, "Triggering forced shutdown!\n");
71+
if (kernel_can_power_off())
72+
kernel_power_off();
73+
else /* Missing macsmc-reboot driver? */
74+
kernel_restart("SMC power button triggered restart");
75+
}
76+
break;
77+
default:
78+
dev_warn(smcin->dev, "Unknown SMC button event: %04lx\n", event & 0xffff);
79+
}
80+
}
81+
82+
static void macsmc_input_event_lid(struct macsmc_input *smcin, unsigned long event)
83+
{
84+
u8 lid_state = !!((event >> 8) & 0xff);
85+
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);
89+
}
90+
91+
static int macsmc_input_event(struct notifier_block *nb, unsigned long event, void *data)
92+
{
93+
struct macsmc_input *smcin = container_of(nb, struct macsmc_input, nb);
94+
u16 type = event >> 16;
95+
96+
switch (type) {
97+
case SMC_EV_BTN:
98+
macsmc_input_event_button(smcin, event);
99+
return NOTIFY_OK;
100+
case SMC_EV_LID:
101+
macsmc_input_event_lid(smcin, event);
102+
return NOTIFY_OK;
103+
default:
104+
/* SMC event meant for another driver */
105+
return NOTIFY_DONE;
106+
}
107+
}
108+
109+
static int macsmc_input_probe(struct platform_device *pdev)
110+
{
111+
struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
112+
struct macsmc_input *smcin;
113+
bool have_lid, have_power;
114+
int error;
115+
116+
/* Bail early if this SMC neither supports power button nor lid events */
117+
have_lid = apple_smc_key_exists(smc, SMC_KEY(MSLD));
118+
have_power = apple_smc_key_exists(smc, SMC_KEY(bHLD));
119+
if (!have_lid && !have_power)
120+
return -ENODEV;
121+
122+
smcin = devm_kzalloc(&pdev->dev, sizeof(*smcin), GFP_KERNEL);
123+
if (!smcin)
124+
return -ENOMEM;
125+
126+
smcin->dev = &pdev->dev;
127+
smcin->smc = smc;
128+
platform_set_drvdata(pdev, smcin);
129+
130+
smcin->input = devm_input_allocate_device(&pdev->dev);
131+
if (!smcin->input)
132+
return -ENOMEM;
133+
134+
smcin->input->phys = "macsmc-input (0)";
135+
smcin->input->name = "Apple SMC power/lid events";
136+
137+
if (have_lid)
138+
input_set_capability(smcin->input, EV_SW, SW_LID);
139+
if (have_power)
140+
input_set_capability(smcin->input, EV_KEY, KEY_POWER);
141+
142+
if (have_lid) {
143+
u8 val;
144+
145+
error = apple_smc_read_u8(smc, SMC_KEY(MSLD), &val);
146+
if (error < 0)
147+
dev_warn(&pdev->dev, "Failed to read initial lid state\n");
148+
else
149+
input_report_switch(smcin->input, SW_LID, val);
150+
}
151+
152+
if (have_power) {
153+
u32 val;
154+
155+
error = apple_smc_read_u32(smc, SMC_KEY(bHLD), &val);
156+
if (error < 0)
157+
dev_warn(&pdev->dev, "Failed to read initial power button state\n");
158+
else
159+
input_report_key(smcin->input, KEY_POWER, val & 1);
160+
}
161+
162+
error = input_register_device(smcin->input);
163+
if (error) {
164+
dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
165+
return error;
166+
}
167+
168+
input_sync(smcin->input);
169+
170+
smcin->nb.notifier_call = macsmc_input_event;
171+
blocking_notifier_chain_register(&smc->event_handlers, &smcin->nb);
172+
173+
device_init_wakeup(&pdev->dev, true);
174+
175+
return 0;
176+
}
177+
178+
static int macsmc_input_pm_prepare(struct device *dev)
179+
{
180+
struct macsmc_input *smcin = dev_get_drvdata(dev);
181+
182+
smcin->wakeup_mode = true;
183+
return 0;
184+
}
185+
186+
static void macsmc_input_pm_complete(struct device *dev)
187+
{
188+
struct macsmc_input *smcin = dev_get_drvdata(dev);
189+
190+
smcin->wakeup_mode = false;
191+
}
192+
193+
static const struct dev_pm_ops macsmc_input_pm_ops = {
194+
.prepare = macsmc_input_pm_prepare,
195+
.complete = macsmc_input_pm_complete,
196+
};
197+
198+
static struct platform_driver macsmc_input_driver = {
199+
.driver = {
200+
.name = "macsmc-input",
201+
.pm = &macsmc_input_pm_ops,
202+
},
203+
.probe = macsmc_input_probe,
204+
};
205+
module_platform_driver(macsmc_input_driver);
206+
207+
MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
208+
MODULE_LICENSE("Dual MIT/GPL");
209+
MODULE_DESCRIPTION("Apple SMC input driver");
210+
MODULE_ALIAS("platform:macsmc-input");

drivers/mfd/macsmc.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,12 @@
4545
#define SMC_TIMEOUT_MS 500
4646

4747
static const struct mfd_cell apple_smc_devs[] = {
48+
MFD_CELL_NAME("macsmc-input"),
49+
MFD_CELL_NAME("macsmc-power"),
4850
MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"),
51+
MFD_CELL_OF("macsmc-hwmon", NULL, NULL, 0, 0, "apple,smc-hwmon"),
4952
MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"),
53+
MFD_CELL_OF("macsmc-rtc", NULL, NULL, 0, 0, "apple,smc-rtc"),
5054
};
5155

5256
static int apple_smc_cmd_locked(struct apple_smc *smc, u64 cmd, u64 arg,

0 commit comments

Comments
 (0)