Skip to content

Commit 1d51544

Browse files
marcanjannau
authored andcommitted
platform/apple: Add new Apple Mac SMC driver
This driver implements support for the SMC (System Management Controller) in Apple Macs. In contrast to the existing applesmc driver, it uses pluggable backends that allow it to support different SMC implementations, and uses the MFD subsystem to expose the core SMC functionality so that specific features (gpio, hwmon, battery, etc.) can be implemented by separate drivers in their respective downstream subsystems. The initial RTKit backend adds support for Apple Silicon Macs (M1 et al). We hope a backend for T2 Macs will be written in the future (since those are not supported by applesmc), and eventually an x86 backend would allow us to fully deprecate applesmc in favor of this driver. Signed-off-by: Hector Martin <marcan@marcan.st>
1 parent a67d73b commit 1d51544

8 files changed

Lines changed: 880 additions & 0 deletions

File tree

drivers/platform/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ source "drivers/platform/surface/Kconfig"
1717

1818
source "drivers/platform/x86/Kconfig"
1919

20+
source "drivers/platform/apple/Kconfig"
21+
2022
source "drivers/platform/arm64/Kconfig"

drivers/platform/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ obj-$(CONFIG_GOLDFISH) += goldfish/
1212
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
1313
obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
1414
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
15+
obj-$(CONFIG_APPLE_PLATFORMS) += apple/
1516
obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/

drivers/platform/apple/Kconfig

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
#
3+
# Apple Platform-Specific Drivers
4+
#
5+
6+
menuconfig APPLE_PLATFORMS
7+
bool "Apple Mac Platform-Specific Device Drivers"
8+
default y
9+
help
10+
Say Y here to get to see options for platform-specific device drivers
11+
for Apple devices. This option alone does not add any kernel code.
12+
13+
If you say N, all options in this submenu will be skipped and disabled.
14+
15+
if APPLE_PLATFORMS
16+
17+
config APPLE_SMC
18+
tristate "Apple SMC Driver"
19+
depends on ARCH_APPLE || (COMPILE_TEST && 64BIT)
20+
default ARCH_APPLE
21+
select MFD_CORE
22+
help
23+
Build support for the Apple System Management Controller present in
24+
Apple Macs. This driver currently supports the SMC in Apple Silicon
25+
Macs. For x86 Macs, see the applesmc driver (SENSORS_APPLESMC).
26+
27+
Say Y here if you have an Apple Silicon Mac.
28+
29+
To compile this driver as a module, choose M here: the module will
30+
be called macsmc.
31+
32+
if APPLE_SMC
33+
34+
config APPLE_SMC_RTKIT
35+
tristate "RTKit (Apple Silicon) backend"
36+
depends on ARCH_APPLE || (COMPILE_TEST && 64BIT)
37+
depends on APPLE_RTKIT
38+
default ARCH_APPLE
39+
help
40+
Build support for SMC communications via the RTKit backend. This is
41+
required for Apple Silicon Macs.
42+
43+
Say Y here if you have an Apple Silicon Mac.
44+
45+
To compile this driver as a module, choose M here: the module will
46+
be called macsmc-rtkit.
47+
48+
endif
49+
endif

drivers/platform/apple/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
#
3+
# Makefile for linux/drivers/platform/apple
4+
# Apple Platform-Specific Drivers
5+
#
6+
7+
macsmc-y += smc_core.o
8+
macsmc-rtkit-y += smc_rtkit.o
9+
10+
obj-$(CONFIG_APPLE_SMC) += macsmc.o
11+
obj-$(CONFIG_APPLE_SMC_RTKIT) += macsmc-rtkit.o

drivers/platform/apple/smc.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/*
3+
* Apple SMC internal core definitions
4+
* Copyright (C) The Asahi Linux Contributors
5+
*/
6+
7+
#ifndef _SMC_H
8+
#define _SMC_H
9+
10+
#include <linux/mfd/macsmc.h>
11+
12+
struct apple_smc_backend_ops {
13+
int (*read_key)(void *cookie, smc_key key, void *buf, size_t size);
14+
int (*write_key)(void *cookie, smc_key key, void *buf, size_t size);
15+
int (*write_key_atomic)(void *cookie, smc_key key, void *buf, size_t size);
16+
int (*rw_key)(void *cookie, smc_key key, void *wbuf, size_t wsize,
17+
void *rbuf, size_t rsize);
18+
int (*get_key_by_index)(void *cookie, int index, smc_key *key);
19+
int (*get_key_info)(void *cookie, smc_key key, struct apple_smc_key_info *info);
20+
};
21+
22+
struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops,
23+
void *cookie);
24+
void *apple_smc_get_cookie(struct apple_smc *smc);
25+
int apple_smc_remove(struct apple_smc *smc);
26+
void apple_smc_event_received(struct apple_smc *smc, uint32_t event);
27+
28+
#endif

drivers/platform/apple/smc_core.c

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/*
3+
* Apple SMC core framework
4+
* Copyright The Asahi Linux Contributors
5+
*/
6+
7+
#include <linux/device.h>
8+
#include <linux/mfd/core.h>
9+
#include <linux/mutex.h>
10+
#include <linux/notifier.h>
11+
#include "smc.h"
12+
13+
struct apple_smc {
14+
struct device *dev;
15+
16+
void *be_cookie;
17+
const struct apple_smc_backend_ops *be;
18+
19+
struct mutex mutex;
20+
21+
u32 key_count;
22+
smc_key first_key;
23+
smc_key last_key;
24+
25+
struct blocking_notifier_head event_handlers;
26+
};
27+
28+
static const struct mfd_cell apple_smc_devs[] = {
29+
{
30+
.name = "macsmc-gpio",
31+
},
32+
{
33+
.name = "macsmc-hid",
34+
},
35+
{
36+
.name = "macsmc-power",
37+
},
38+
{
39+
.name = "macsmc-reboot",
40+
},
41+
{
42+
.name = "macsmc-rtc",
43+
},
44+
};
45+
46+
int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size)
47+
{
48+
int ret;
49+
50+
mutex_lock(&smc->mutex);
51+
ret = smc->be->read_key(smc->be_cookie, key, buf, size);
52+
mutex_unlock(&smc->mutex);
53+
54+
return ret;
55+
}
56+
EXPORT_SYMBOL(apple_smc_read);
57+
58+
int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size)
59+
{
60+
int ret;
61+
62+
mutex_lock(&smc->mutex);
63+
ret = smc->be->write_key(smc->be_cookie, key, buf, size);
64+
mutex_unlock(&smc->mutex);
65+
66+
return ret;
67+
}
68+
EXPORT_SYMBOL(apple_smc_write);
69+
70+
int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size)
71+
{
72+
int ret;
73+
74+
/*
75+
* Will fail if SMC is busy. This is only used by SMC reboot/poweroff
76+
* final calls, so it doesn't really matter at that point.
77+
*/
78+
if (!mutex_trylock(&smc->mutex))
79+
return -EBUSY;
80+
81+
ret = smc->be->write_key_atomic(smc->be_cookie, key, buf, size);
82+
mutex_unlock(&smc->mutex);
83+
84+
return ret;
85+
}
86+
EXPORT_SYMBOL(apple_smc_write_atomic);
87+
88+
int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize,
89+
void *rbuf, size_t rsize)
90+
{
91+
int ret;
92+
93+
mutex_lock(&smc->mutex);
94+
ret = smc->be->rw_key(smc->be_cookie, key, wbuf, wsize, rbuf, rsize);
95+
mutex_unlock(&smc->mutex);
96+
97+
return ret;
98+
}
99+
EXPORT_SYMBOL(apple_smc_rw);
100+
101+
int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key)
102+
{
103+
int ret;
104+
105+
mutex_lock(&smc->mutex);
106+
ret = smc->be->get_key_by_index(smc->be_cookie, index, key);
107+
mutex_unlock(&smc->mutex);
108+
109+
return ret;
110+
}
111+
EXPORT_SYMBOL(apple_smc_get_key_by_index);
112+
113+
int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info)
114+
{
115+
int ret;
116+
117+
mutex_lock(&smc->mutex);
118+
ret = smc->be->get_key_info(smc->be_cookie, key, info);
119+
mutex_unlock(&smc->mutex);
120+
121+
return ret;
122+
}
123+
EXPORT_SYMBOL(apple_smc_get_key_info);
124+
125+
int apple_smc_find_first_key_index(struct apple_smc *smc, smc_key key)
126+
{
127+
int start = 0, count = smc->key_count;
128+
int ret;
129+
130+
if (key <= smc->first_key)
131+
return 0;
132+
if (key > smc->last_key)
133+
return smc->key_count;
134+
135+
while (count > 1) {
136+
int pivot = start + ((count - 1) >> 1);
137+
smc_key pkey;
138+
139+
ret = apple_smc_get_key_by_index(smc, pivot, &pkey);
140+
if (ret < 0)
141+
return ret;
142+
143+
if (pkey == key)
144+
return pivot;
145+
146+
pivot++;
147+
148+
if (pkey < key) {
149+
count -= pivot - start;
150+
start = pivot;
151+
} else {
152+
count = pivot - start;
153+
}
154+
}
155+
156+
return start;
157+
}
158+
EXPORT_SYMBOL(apple_smc_find_first_key_index);
159+
160+
int apple_smc_get_key_count(struct apple_smc *smc)
161+
{
162+
return smc->key_count;
163+
}
164+
EXPORT_SYMBOL(apple_smc_get_key_count);
165+
166+
void apple_smc_event_received(struct apple_smc *smc, uint32_t event)
167+
{
168+
dev_dbg(smc->dev, "Event: 0x%08x\n", event);
169+
blocking_notifier_call_chain(&smc->event_handlers, event, NULL);
170+
}
171+
EXPORT_SYMBOL(apple_smc_event_received);
172+
173+
int apple_smc_register_notifier(struct apple_smc *smc, struct notifier_block *n)
174+
{
175+
return blocking_notifier_chain_register(&smc->event_handlers, n);
176+
}
177+
EXPORT_SYMBOL(apple_smc_register_notifier);
178+
179+
int apple_smc_unregister_notifier(struct apple_smc *smc, struct notifier_block *n)
180+
{
181+
return blocking_notifier_chain_unregister(&smc->event_handlers, n);
182+
}
183+
EXPORT_SYMBOL(apple_smc_unregister_notifier);
184+
185+
void *apple_smc_get_cookie(struct apple_smc *smc)
186+
{
187+
return smc->be_cookie;
188+
}
189+
EXPORT_SYMBOL(apple_smc_get_cookie);
190+
191+
struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops, void *cookie)
192+
{
193+
struct apple_smc *smc;
194+
u32 count;
195+
int ret;
196+
197+
smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL);
198+
if (!smc)
199+
return ERR_PTR(-ENOMEM);
200+
201+
smc->dev = dev;
202+
smc->be_cookie = cookie;
203+
smc->be = ops;
204+
mutex_init(&smc->mutex);
205+
BLOCKING_INIT_NOTIFIER_HEAD(&smc->event_handlers);
206+
207+
ret = apple_smc_read_u32(smc, SMC_KEY(#KEY), &count);
208+
if (ret)
209+
return ERR_PTR(dev_err_probe(dev, ret, "Failed to get key count"));
210+
smc->key_count = be32_to_cpu(count);
211+
212+
ret = apple_smc_get_key_by_index(smc, 0, &smc->first_key);
213+
if (ret)
214+
return ERR_PTR(dev_err_probe(dev, ret, "Failed to get first key"));
215+
216+
ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &smc->last_key);
217+
if (ret)
218+
return ERR_PTR(dev_err_probe(dev, ret, "Failed to get last key"));
219+
220+
/* Enable notifications */
221+
apple_smc_write_flag(smc, SMC_KEY(NTAP), 1);
222+
223+
dev_info(dev, "Initialized (%d keys %p4ch..%p4ch)\n",
224+
smc->key_count, &smc->first_key, &smc->last_key);
225+
226+
dev_set_drvdata(dev, smc);
227+
228+
ret = mfd_add_devices(dev, -1, apple_smc_devs, ARRAY_SIZE(apple_smc_devs), NULL, 0, NULL);
229+
if (ret)
230+
return ERR_PTR(dev_err_probe(dev, ret, "Subdevice initialization failed"));
231+
232+
return smc;
233+
}
234+
EXPORT_SYMBOL(apple_smc_probe);
235+
236+
int apple_smc_remove(struct apple_smc *smc)
237+
{
238+
mfd_remove_devices(smc->dev);
239+
240+
/* Disable notifications */
241+
apple_smc_write_flag(smc, SMC_KEY(NTAP), 1);
242+
243+
return 0;
244+
}
245+
EXPORT_SYMBOL(apple_smc_remove);
246+
247+
MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
248+
MODULE_LICENSE("Dual MIT/GPL");
249+
MODULE_DESCRIPTION("Apple SMC core");

0 commit comments

Comments
 (0)