Skip to content

Commit fc622b3

Browse files
qzedjwrdegoede
authored andcommitted
platform/surface: Set up Surface Aggregator device registry
The Surface System Aggregator Module (SSAM) subsystem provides various functionalities, which are separated by spreading them across multiple devices and corresponding drivers. Parts of that functionality / some of those devices, however, can (as far as we currently know) not be auto-detected by conventional means. While older (specifically 5th- and 6th-)generation models do advertise most of their functionality via standard platform devices in ACPI, newer generations do not. As we are currently also not aware of any feasible way to query said functionalities dynamically, this poses a problem. There is, however, a device in ACPI that seems to be used by Windows for identifying different Surface models: The Windows Surface Integration Device (WSID). This device seems to have a HID corresponding to the overall set of functionalities SSAM provides for the associated model. This commit introduces a registry providing non-detectable device information via software nodes. In addition, a SSAM platform hub driver is introduced, which takes care of creating and managing the SSAM devices specified in this registry. This approach allows for a hierarchical setup akin to ACPI and is easily extendable, e.g. via firmware node properties. Note that this commit only provides the basis for the platform hub and registry, and does not add any content to it. The registry will be expanded in subsequent commits. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com> Link: https://lore.kernel.org/r/20210212115439.1525216-2-luzmaximilian@gmail.com Signed-off-by: Hans de Goede <hdegoede@redhat.com>
1 parent a38fd87 commit fc622b3

4 files changed

Lines changed: 313 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11897,6 +11897,7 @@ F: Documentation/driver-api/surface_aggregator/
1189711897
F: drivers/platform/surface/aggregator/
1189811898
F: drivers/platform/surface/surface_acpi_notify.c
1189911899
F: drivers/platform/surface/surface_aggregator_cdev.c
11900+
F: drivers/platform/surface/surface_aggregator_registry.c
1190011901
F: include/linux/surface_acpi_notify.h
1190111902
F: include/linux/surface_aggregator/
1190211903
F: include/uapi/linux/surface_aggregator/

drivers/platform/surface/Kconfig

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,33 @@ config SURFACE_AGGREGATOR_CDEV
7777
The provided interface is intended for debugging and development only,
7878
and should not be used otherwise.
7979

80+
config SURFACE_AGGREGATOR_REGISTRY
81+
tristate "Surface System Aggregator Module Device Registry"
82+
depends on SURFACE_AGGREGATOR
83+
depends on SURFACE_AGGREGATOR_BUS
84+
help
85+
Device-registry and device-hubs for Surface System Aggregator Module
86+
(SSAM) devices.
87+
88+
Provides a module and driver which act as a device-registry for SSAM
89+
client devices that cannot be detected automatically, e.g. via ACPI.
90+
Such devices are instead provided via this registry and attached via
91+
device hubs, also provided in this module.
92+
93+
Devices provided via this registry are:
94+
- Platform profile (performance-/cooling-mode) device (5th- and later
95+
generations).
96+
- Battery/AC devices (7th-generation).
97+
- HID input devices (7th-generation).
98+
99+
Select M (recommended) or Y here if you want support for the above
100+
mentioned devices on the corresponding Surface models. Without this
101+
module, the respective devices will not be instantiated and thus any
102+
functionality provided by them will be missing, even when drivers for
103+
these devices are present. In other words, this module only provides
104+
the respective client devices. Drivers for these devices still need to
105+
be selected via the other options.
106+
80107
config SURFACE_GPE
81108
tristate "Surface GPE/Lid Support Driver"
82109
depends on DMI

drivers/platform/surface/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o
1010
obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o
1111
obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/
1212
obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o
13+
obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o
1314
obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
1415
obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o
1516
obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Surface System Aggregator Module (SSAM) client device registry.
4+
*
5+
* Registry for non-platform/non-ACPI SSAM client devices, i.e. devices that
6+
* cannot be auto-detected. Provides device-hubs and performs instantiation
7+
* for these devices.
8+
*
9+
* Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
10+
*/
11+
12+
#include <linux/acpi.h>
13+
#include <linux/kernel.h>
14+
#include <linux/module.h>
15+
#include <linux/platform_device.h>
16+
#include <linux/property.h>
17+
18+
#include <linux/surface_aggregator/controller.h>
19+
#include <linux/surface_aggregator/device.h>
20+
21+
22+
/* -- Device registry. ------------------------------------------------------ */
23+
24+
/*
25+
* SSAM device names follow the SSAM module alias, meaning they are prefixed
26+
* with 'ssam:', followed by domain, category, target ID, instance ID, and
27+
* function, each encoded as two-digit hexadecimal, separated by ':'. In other
28+
* words, it follows the scheme
29+
*
30+
* ssam:dd:cc:tt:ii:ff
31+
*
32+
* Where, 'dd', 'cc', 'tt', 'ii', and 'ff' are the two-digit hexadecimal
33+
* values mentioned above, respectively.
34+
*/
35+
36+
/* Root node. */
37+
static const struct software_node ssam_node_root = {
38+
.name = "ssam_platform_hub",
39+
};
40+
41+
/* Devices for Surface Book 2. */
42+
static const struct software_node *ssam_node_group_sb2[] = {
43+
&ssam_node_root,
44+
NULL,
45+
};
46+
47+
/* Devices for Surface Book 3. */
48+
static const struct software_node *ssam_node_group_sb3[] = {
49+
&ssam_node_root,
50+
NULL,
51+
};
52+
53+
/* Devices for Surface Laptop 1. */
54+
static const struct software_node *ssam_node_group_sl1[] = {
55+
&ssam_node_root,
56+
NULL,
57+
};
58+
59+
/* Devices for Surface Laptop 2. */
60+
static const struct software_node *ssam_node_group_sl2[] = {
61+
&ssam_node_root,
62+
NULL,
63+
};
64+
65+
/* Devices for Surface Laptop 3. */
66+
static const struct software_node *ssam_node_group_sl3[] = {
67+
&ssam_node_root,
68+
NULL,
69+
};
70+
71+
/* Devices for Surface Laptop Go. */
72+
static const struct software_node *ssam_node_group_slg1[] = {
73+
&ssam_node_root,
74+
NULL,
75+
};
76+
77+
/* Devices for Surface Pro 5. */
78+
static const struct software_node *ssam_node_group_sp5[] = {
79+
&ssam_node_root,
80+
NULL,
81+
};
82+
83+
/* Devices for Surface Pro 6. */
84+
static const struct software_node *ssam_node_group_sp6[] = {
85+
&ssam_node_root,
86+
NULL,
87+
};
88+
89+
/* Devices for Surface Pro 7. */
90+
static const struct software_node *ssam_node_group_sp7[] = {
91+
&ssam_node_root,
92+
NULL,
93+
};
94+
95+
96+
/* -- Device registry helper functions. ------------------------------------- */
97+
98+
static int ssam_uid_from_string(const char *str, struct ssam_device_uid *uid)
99+
{
100+
u8 d, tc, tid, iid, fn;
101+
int n;
102+
103+
n = sscanf(str, "ssam:%hhx:%hhx:%hhx:%hhx:%hhx", &d, &tc, &tid, &iid, &fn);
104+
if (n != 5)
105+
return -EINVAL;
106+
107+
uid->domain = d;
108+
uid->category = tc;
109+
uid->target = tid;
110+
uid->instance = iid;
111+
uid->function = fn;
112+
113+
return 0;
114+
}
115+
116+
static int ssam_hub_remove_devices_fn(struct device *dev, void *data)
117+
{
118+
if (!is_ssam_device(dev))
119+
return 0;
120+
121+
ssam_device_remove(to_ssam_device(dev));
122+
return 0;
123+
}
124+
125+
static void ssam_hub_remove_devices(struct device *parent)
126+
{
127+
device_for_each_child_reverse(parent, NULL, ssam_hub_remove_devices_fn);
128+
}
129+
130+
static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ctrl,
131+
struct fwnode_handle *node)
132+
{
133+
struct ssam_device_uid uid;
134+
struct ssam_device *sdev;
135+
int status;
136+
137+
status = ssam_uid_from_string(fwnode_get_name(node), &uid);
138+
if (status)
139+
return status;
140+
141+
sdev = ssam_device_alloc(ctrl, uid);
142+
if (!sdev)
143+
return -ENOMEM;
144+
145+
sdev->dev.parent = parent;
146+
sdev->dev.fwnode = node;
147+
148+
status = ssam_device_add(sdev);
149+
if (status)
150+
ssam_device_put(sdev);
151+
152+
return status;
153+
}
154+
155+
static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *ctrl,
156+
struct fwnode_handle *node)
157+
{
158+
struct fwnode_handle *child;
159+
int status;
160+
161+
fwnode_for_each_child_node(node, child) {
162+
/*
163+
* Try to add the device specified in the firmware node. If
164+
* this fails with -EINVAL, the node does not specify any SSAM
165+
* device, so ignore it and continue with the next one.
166+
*/
167+
168+
status = ssam_hub_add_device(parent, ctrl, child);
169+
if (status && status != -EINVAL)
170+
goto err;
171+
}
172+
173+
return 0;
174+
err:
175+
ssam_hub_remove_devices(parent);
176+
return status;
177+
}
178+
179+
180+
/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
181+
182+
static const struct acpi_device_id ssam_platform_hub_match[] = {
183+
/* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
184+
{ "MSHW0081", (unsigned long)ssam_node_group_sp5 },
185+
186+
/* Surface Pro 6 (OMBR >= 0x10) */
187+
{ "MSHW0111", (unsigned long)ssam_node_group_sp6 },
188+
189+
/* Surface Pro 7 */
190+
{ "MSHW0116", (unsigned long)ssam_node_group_sp7 },
191+
192+
/* Surface Book 2 */
193+
{ "MSHW0107", (unsigned long)ssam_node_group_sb2 },
194+
195+
/* Surface Book 3 */
196+
{ "MSHW0117", (unsigned long)ssam_node_group_sb3 },
197+
198+
/* Surface Laptop 1 */
199+
{ "MSHW0086", (unsigned long)ssam_node_group_sl1 },
200+
201+
/* Surface Laptop 2 */
202+
{ "MSHW0112", (unsigned long)ssam_node_group_sl2 },
203+
204+
/* Surface Laptop 3 (13", Intel) */
205+
{ "MSHW0114", (unsigned long)ssam_node_group_sl3 },
206+
207+
/* Surface Laptop 3 (15", AMD) */
208+
{ "MSHW0110", (unsigned long)ssam_node_group_sl3 },
209+
210+
/* Surface Laptop Go 1 */
211+
{ "MSHW0118", (unsigned long)ssam_node_group_slg1 },
212+
213+
{ },
214+
};
215+
MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_match);
216+
217+
static int ssam_platform_hub_probe(struct platform_device *pdev)
218+
{
219+
const struct software_node **nodes;
220+
struct ssam_controller *ctrl;
221+
struct fwnode_handle *root;
222+
int status;
223+
224+
nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev);
225+
if (!nodes)
226+
return -ENODEV;
227+
228+
/*
229+
* As we're adding the SSAM client devices as children under this device
230+
* and not the SSAM controller, we need to add a device link to the
231+
* controller to ensure that we remove all of our devices before the
232+
* controller is removed. This also guarantees proper ordering for
233+
* suspend/resume of the devices on this hub.
234+
*/
235+
ctrl = ssam_client_bind(&pdev->dev);
236+
if (IS_ERR(ctrl))
237+
return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
238+
239+
status = software_node_register_node_group(nodes);
240+
if (status)
241+
return status;
242+
243+
root = software_node_fwnode(&ssam_node_root);
244+
if (!root) {
245+
software_node_unregister_node_group(nodes);
246+
return -ENOENT;
247+
}
248+
249+
set_secondary_fwnode(&pdev->dev, root);
250+
251+
status = ssam_hub_add_devices(&pdev->dev, ctrl, root);
252+
if (status) {
253+
set_secondary_fwnode(&pdev->dev, NULL);
254+
software_node_unregister_node_group(nodes);
255+
}
256+
257+
platform_set_drvdata(pdev, nodes);
258+
return status;
259+
}
260+
261+
static int ssam_platform_hub_remove(struct platform_device *pdev)
262+
{
263+
const struct software_node **nodes = platform_get_drvdata(pdev);
264+
265+
ssam_hub_remove_devices(&pdev->dev);
266+
set_secondary_fwnode(&pdev->dev, NULL);
267+
software_node_unregister_node_group(nodes);
268+
return 0;
269+
}
270+
271+
static struct platform_driver ssam_platform_hub_driver = {
272+
.probe = ssam_platform_hub_probe,
273+
.remove = ssam_platform_hub_remove,
274+
.driver = {
275+
.name = "surface_aggregator_platform_hub",
276+
.acpi_match_table = ssam_platform_hub_match,
277+
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
278+
},
279+
};
280+
module_platform_driver(ssam_platform_hub_driver);
281+
282+
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
283+
MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module");
284+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)