Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/surface: aggregator: Move subsystem hub drivers to their own module

Split out subsystem device hub drivers into their own module. This
allows us to load the hub drivers separately from the registry, which
will help future DT/OF support.

While doing so, also remove a small bit of code duplication.

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
Link: https://lore.kernel.org/r/20220624205800.1355621-3-luzmaximilian@gmail.com
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Maximilian Luz and committed by
Hans de Goede
993a9e2a 4a4ab610

+410 -374
+6
MAINTAINERS
··· 13253 13253 F: include/linux/surface_aggregator/ 13254 13254 F: include/uapi/linux/surface_aggregator/ 13255 13255 13256 + MICROSOFT SURFACE SYSTEM AGGREGATOR HUB DRIVER 13257 + M: Maximilian Luz <luzmaximilian@gmail.com> 13258 + L: platform-driver-x86@vger.kernel.org 13259 + S: Maintained 13260 + F: drivers/platform/surface/surface_aggregator_hub.c 13261 + 13256 13262 MICROTEK X6 SCANNER 13257 13263 M: Oliver Neukum <oliver@neukum.org> 13258 13264 S: Maintained
+31 -4
drivers/platform/surface/Kconfig
··· 72 72 The provided interface is intended for debugging and development only, 73 73 and should not be used otherwise. 74 74 75 + config SURFACE_AGGREGATOR_HUB 76 + tristate "Surface System Aggregator Module Subsystem Device Hubs" 77 + depends on SURFACE_AGGREGATOR 78 + depends on SURFACE_AGGREGATOR_BUS 79 + help 80 + Device-hub drivers for Surface System Aggregator Module (SSAM) subsystem 81 + devices. 82 + 83 + Provides subsystem hub drivers which manage client devices on various 84 + SSAM subsystems. In some subsystems, notably the BAS subsystem managing 85 + devices contained in the base of the Surface Book 3 and the KIP subsystem 86 + managing type-cover devices in the Surface Pro 8 and Surface Pro X, 87 + devices can be (hot-)removed. Hub devices and drivers are required to 88 + manage these subdevices. 89 + 90 + Devices managed via these hubs are: 91 + - Battery/AC devices (Surface Book 3). 92 + - HID input devices (7th-generation and later models with detachable 93 + input devices). 94 + 95 + Select M (recommended) or Y here if you want support for the above 96 + mentioned devices on the corresponding Surface models. Without this 97 + module, the respective devices mentioned above will not be instantiated 98 + and thus any functionality provided by them will be missing, even when 99 + drivers for these devices are present. This module only provides the 100 + respective subsystem hubs. Both drivers and device specification (e.g. 101 + via the Surface Aggregator Registry) for these devices still need to be 102 + selected via other options. 103 + 75 104 config SURFACE_AGGREGATOR_REGISTRY 76 105 tristate "Surface System Aggregator Module Device Registry" 77 106 depends on SURFACE_AGGREGATOR 78 107 depends on SURFACE_AGGREGATOR_BUS 79 108 help 80 - Device-registry and device-hubs for Surface System Aggregator Module 81 - (SSAM) devices. 109 + Device-registry for Surface System Aggregator Module (SSAM) devices. 82 110 83 111 Provides a module and driver which act as a device-registry for SSAM 84 112 client devices that cannot be detected automatically, e.g. via ACPI. 85 - Such devices are instead provided via this registry and attached via 86 - device hubs, also provided in this module. 113 + Such devices are instead provided and managed via this registry. 87 114 88 115 Devices provided via this registry are: 89 116 - Platform profile (performance-/cooling-mode) device (5th- and later
+1
drivers/platform/surface/Makefile
··· 9 9 obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o 10 10 obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/ 11 11 obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o 12 + obj-$(CONFIG_SURFACE_AGGREGATOR_HUB) += surface_aggregator_hub.o 12 13 obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o 13 14 obj-$(CONFIG_SURFACE_AGGREGATOR_TABLET_SWITCH) += surface_aggregator_tabletsw.o 14 15 obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o
+371
drivers/platform/surface/surface_aggregator_hub.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Driver for Surface System Aggregator Module (SSAM) subsystem device hubs. 4 + * 5 + * Provides a driver for SSAM subsystems device hubs. This driver performs 6 + * instantiation of the devices managed by said hubs and takes care of 7 + * (hot-)removal. 8 + * 9 + * Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com> 10 + */ 11 + 12 + #include <linux/kernel.h> 13 + #include <linux/limits.h> 14 + #include <linux/module.h> 15 + #include <linux/types.h> 16 + #include <linux/workqueue.h> 17 + 18 + #include <linux/surface_aggregator/device.h> 19 + 20 + 21 + /* -- SSAM generic subsystem hub driver framework. -------------------------- */ 22 + 23 + enum ssam_hub_state { 24 + SSAM_HUB_UNINITIALIZED, /* Only set during initialization. */ 25 + SSAM_HUB_CONNECTED, 26 + SSAM_HUB_DISCONNECTED, 27 + }; 28 + 29 + enum ssam_hub_flags { 30 + SSAM_HUB_HOT_REMOVED, 31 + }; 32 + 33 + struct ssam_hub; 34 + 35 + struct ssam_hub_ops { 36 + int (*get_state)(struct ssam_hub *hub, enum ssam_hub_state *state); 37 + }; 38 + 39 + struct ssam_hub { 40 + struct ssam_device *sdev; 41 + 42 + enum ssam_hub_state state; 43 + unsigned long flags; 44 + 45 + struct delayed_work update_work; 46 + unsigned long connect_delay; 47 + 48 + struct ssam_event_notifier notif; 49 + struct ssam_hub_ops ops; 50 + }; 51 + 52 + struct ssam_hub_desc { 53 + struct { 54 + struct ssam_event_registry reg; 55 + struct ssam_event_id id; 56 + enum ssam_event_mask mask; 57 + } event; 58 + 59 + struct { 60 + u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event); 61 + int (*get_state)(struct ssam_hub *hub, enum ssam_hub_state *state); 62 + } ops; 63 + 64 + unsigned long connect_delay_ms; 65 + }; 66 + 67 + static void ssam_hub_update_workfn(struct work_struct *work) 68 + { 69 + struct ssam_hub *hub = container_of(work, struct ssam_hub, update_work.work); 70 + enum ssam_hub_state state; 71 + int status = 0; 72 + 73 + status = hub->ops.get_state(hub, &state); 74 + if (status) 75 + return; 76 + 77 + /* 78 + * There is a small possibility that hub devices were hot-removed and 79 + * re-added before we were able to remove them here. In that case, both 80 + * the state returned by get_state() and the state of the hub will 81 + * equal SSAM_HUB_CONNECTED and we would bail early below, which would 82 + * leave child devices without proper (re-)initialization and the 83 + * hot-remove flag set. 84 + * 85 + * Therefore, we check whether devices have been hot-removed via an 86 + * additional flag on the hub and, in this case, override the returned 87 + * hub state. In case of a missed disconnect (i.e. get_state returned 88 + * "connected"), we further need to re-schedule this work (with the 89 + * appropriate delay) as the actual connect work submission might have 90 + * been merged with this one. 91 + * 92 + * This then leads to one of two cases: Either we submit an unnecessary 93 + * work item (which will get ignored via either the queue or the state 94 + * checks) or, in the unlikely case that the work is actually required, 95 + * double the normal connect delay. 96 + */ 97 + if (test_and_clear_bit(SSAM_HUB_HOT_REMOVED, &hub->flags)) { 98 + if (state == SSAM_HUB_CONNECTED) 99 + schedule_delayed_work(&hub->update_work, hub->connect_delay); 100 + 101 + state = SSAM_HUB_DISCONNECTED; 102 + } 103 + 104 + if (hub->state == state) 105 + return; 106 + hub->state = state; 107 + 108 + if (hub->state == SSAM_HUB_CONNECTED) 109 + status = ssam_device_register_clients(hub->sdev); 110 + else 111 + ssam_remove_clients(&hub->sdev->dev); 112 + 113 + if (status) 114 + dev_err(&hub->sdev->dev, "failed to update hub child devices: %d\n", status); 115 + } 116 + 117 + static int ssam_hub_mark_hot_removed(struct device *dev, void *_data) 118 + { 119 + struct ssam_device *sdev = to_ssam_device(dev); 120 + 121 + if (is_ssam_device(dev)) 122 + ssam_device_mark_hot_removed(sdev); 123 + 124 + return 0; 125 + } 126 + 127 + static void ssam_hub_update(struct ssam_hub *hub, bool connected) 128 + { 129 + unsigned long delay; 130 + 131 + /* Mark devices as hot-removed before we remove any. */ 132 + if (!connected) { 133 + set_bit(SSAM_HUB_HOT_REMOVED, &hub->flags); 134 + device_for_each_child_reverse(&hub->sdev->dev, NULL, ssam_hub_mark_hot_removed); 135 + } 136 + 137 + /* 138 + * Delay update when the base/keyboard cover is being connected to give 139 + * devices/EC some time to set up. 140 + */ 141 + delay = connected ? hub->connect_delay : 0; 142 + 143 + schedule_delayed_work(&hub->update_work, delay); 144 + } 145 + 146 + static int __maybe_unused ssam_hub_resume(struct device *dev) 147 + { 148 + struct ssam_hub *hub = dev_get_drvdata(dev); 149 + 150 + schedule_delayed_work(&hub->update_work, 0); 151 + return 0; 152 + } 153 + static SIMPLE_DEV_PM_OPS(ssam_hub_pm_ops, NULL, ssam_hub_resume); 154 + 155 + static int ssam_hub_probe(struct ssam_device *sdev) 156 + { 157 + const struct ssam_hub_desc *desc; 158 + struct ssam_hub *hub; 159 + int status; 160 + 161 + desc = ssam_device_get_match_data(sdev); 162 + if (!desc) { 163 + WARN(1, "no driver match data specified"); 164 + return -EINVAL; 165 + } 166 + 167 + hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL); 168 + if (!hub) 169 + return -ENOMEM; 170 + 171 + hub->sdev = sdev; 172 + hub->state = SSAM_HUB_UNINITIALIZED; 173 + 174 + hub->notif.base.priority = INT_MAX; /* This notifier should run first. */ 175 + hub->notif.base.fn = desc->ops.notify; 176 + hub->notif.event.reg = desc->event.reg; 177 + hub->notif.event.id = desc->event.id; 178 + hub->notif.event.mask = desc->event.mask; 179 + hub->notif.event.flags = SSAM_EVENT_SEQUENCED; 180 + 181 + hub->connect_delay = msecs_to_jiffies(desc->connect_delay_ms); 182 + hub->ops.get_state = desc->ops.get_state; 183 + 184 + INIT_DELAYED_WORK(&hub->update_work, ssam_hub_update_workfn); 185 + 186 + ssam_device_set_drvdata(sdev, hub); 187 + 188 + status = ssam_device_notifier_register(sdev, &hub->notif); 189 + if (status) 190 + return status; 191 + 192 + schedule_delayed_work(&hub->update_work, 0); 193 + return 0; 194 + } 195 + 196 + static void ssam_hub_remove(struct ssam_device *sdev) 197 + { 198 + struct ssam_hub *hub = ssam_device_get_drvdata(sdev); 199 + 200 + ssam_device_notifier_unregister(sdev, &hub->notif); 201 + cancel_delayed_work_sync(&hub->update_work); 202 + ssam_remove_clients(&sdev->dev); 203 + } 204 + 205 + 206 + /* -- SSAM base-subsystem hub driver. --------------------------------------- */ 207 + 208 + /* 209 + * Some devices (especially battery) may need a bit of time to be fully usable 210 + * after being (re-)connected. This delay has been determined via 211 + * experimentation. 212 + */ 213 + #define SSAM_BASE_UPDATE_CONNECT_DELAY 2500 214 + 215 + SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, { 216 + .target_category = SSAM_SSH_TC_BAS, 217 + .target_id = 0x01, 218 + .command_id = 0x0d, 219 + .instance_id = 0x00, 220 + }); 221 + 222 + #define SSAM_BAS_OPMODE_TABLET 0x00 223 + #define SSAM_EVENT_BAS_CID_CONNECTION 0x0c 224 + 225 + static int ssam_base_hub_query_state(struct ssam_hub *hub, enum ssam_hub_state *state) 226 + { 227 + u8 opmode; 228 + int status; 229 + 230 + status = ssam_retry(ssam_bas_query_opmode, hub->sdev->ctrl, &opmode); 231 + if (status < 0) { 232 + dev_err(&hub->sdev->dev, "failed to query base state: %d\n", status); 233 + return status; 234 + } 235 + 236 + if (opmode != SSAM_BAS_OPMODE_TABLET) 237 + *state = SSAM_HUB_CONNECTED; 238 + else 239 + *state = SSAM_HUB_DISCONNECTED; 240 + 241 + return 0; 242 + } 243 + 244 + static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) 245 + { 246 + struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif); 247 + 248 + if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION) 249 + return 0; 250 + 251 + if (event->length < 1) { 252 + dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); 253 + return 0; 254 + } 255 + 256 + ssam_hub_update(hub, event->data[0]); 257 + 258 + /* 259 + * Do not return SSAM_NOTIF_HANDLED: The event should be picked up and 260 + * consumed by the detachment system driver. We're just a (more or less) 261 + * silent observer. 262 + */ 263 + return 0; 264 + } 265 + 266 + static const struct ssam_hub_desc base_hub = { 267 + .event = { 268 + .reg = SSAM_EVENT_REGISTRY_SAM, 269 + .id = { 270 + .target_category = SSAM_SSH_TC_BAS, 271 + .instance = 0, 272 + }, 273 + .mask = SSAM_EVENT_MASK_NONE, 274 + }, 275 + .ops = { 276 + .notify = ssam_base_hub_notif, 277 + .get_state = ssam_base_hub_query_state, 278 + }, 279 + .connect_delay_ms = SSAM_BASE_UPDATE_CONNECT_DELAY, 280 + }; 281 + 282 + 283 + /* -- SSAM KIP-subsystem hub driver. ---------------------------------------- */ 284 + 285 + /* 286 + * Some devices may need a bit of time to be fully usable after being 287 + * (re-)connected. This delay has been determined via experimentation. 288 + */ 289 + #define SSAM_KIP_UPDATE_CONNECT_DELAY 250 290 + 291 + #define SSAM_EVENT_KIP_CID_CONNECTION 0x2c 292 + 293 + SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_query_state, u8, { 294 + .target_category = SSAM_SSH_TC_KIP, 295 + .target_id = 0x01, 296 + .command_id = 0x2c, 297 + .instance_id = 0x00, 298 + }); 299 + 300 + static int ssam_kip_hub_query_state(struct ssam_hub *hub, enum ssam_hub_state *state) 301 + { 302 + int status; 303 + u8 connected; 304 + 305 + status = ssam_retry(__ssam_kip_query_state, hub->sdev->ctrl, &connected); 306 + if (status < 0) { 307 + dev_err(&hub->sdev->dev, "failed to query KIP connection state: %d\n", status); 308 + return status; 309 + } 310 + 311 + *state = connected ? SSAM_HUB_CONNECTED : SSAM_HUB_DISCONNECTED; 312 + return 0; 313 + } 314 + 315 + static u32 ssam_kip_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) 316 + { 317 + struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif); 318 + 319 + if (event->command_id != SSAM_EVENT_KIP_CID_CONNECTION) 320 + return 0; /* Return "unhandled". */ 321 + 322 + if (event->length < 1) { 323 + dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); 324 + return 0; 325 + } 326 + 327 + ssam_hub_update(hub, event->data[0]); 328 + return SSAM_NOTIF_HANDLED; 329 + } 330 + 331 + static const struct ssam_hub_desc kip_hub = { 332 + .event = { 333 + .reg = SSAM_EVENT_REGISTRY_SAM, 334 + .id = { 335 + .target_category = SSAM_SSH_TC_KIP, 336 + .instance = 0, 337 + }, 338 + .mask = SSAM_EVENT_MASK_TARGET, 339 + }, 340 + .ops = { 341 + .notify = ssam_kip_hub_notif, 342 + .get_state = ssam_kip_hub_query_state, 343 + }, 344 + .connect_delay_ms = SSAM_KIP_UPDATE_CONNECT_DELAY, 345 + }; 346 + 347 + 348 + /* -- Driver registration. -------------------------------------------------- */ 349 + 350 + static const struct ssam_device_id ssam_hub_match[] = { 351 + { SSAM_VDEV(HUB, 0x01, SSAM_SSH_TC_KIP, 0x00), (unsigned long)&kip_hub }, 352 + { SSAM_VDEV(HUB, 0x02, SSAM_SSH_TC_BAS, 0x00), (unsigned long)&base_hub }, 353 + { } 354 + }; 355 + MODULE_DEVICE_TABLE(ssam, ssam_hub_match); 356 + 357 + static struct ssam_device_driver ssam_subsystem_hub_driver = { 358 + .probe = ssam_hub_probe, 359 + .remove = ssam_hub_remove, 360 + .match_table = ssam_hub_match, 361 + .driver = { 362 + .name = "surface_aggregator_subsystem_hub", 363 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 364 + .pm = &ssam_hub_pm_ops, 365 + }, 366 + }; 367 + module_ssam_device_driver(ssam_subsystem_hub_driver); 368 + 369 + MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 370 + MODULE_DESCRIPTION("Subsystem device hub driver for Surface System Aggregator Module"); 371 + MODULE_LICENSE("GPL");
+1 -370
drivers/platform/surface/surface_aggregator_registry.c
··· 11 11 12 12 #include <linux/acpi.h> 13 13 #include <linux/kernel.h> 14 - #include <linux/limits.h> 15 14 #include <linux/module.h> 16 15 #include <linux/platform_device.h> 17 16 #include <linux/property.h> 18 17 #include <linux/types.h> 19 - #include <linux/workqueue.h> 20 18 21 - #include <linux/surface_aggregator/controller.h> 22 19 #include <linux/surface_aggregator/device.h> 23 20 24 21 ··· 283 286 }; 284 287 285 288 286 - /* -- SSAM generic subsystem hub driver framework. -------------------------- */ 287 - 288 - enum ssam_hub_state { 289 - SSAM_HUB_UNINITIALIZED, /* Only set during initialization. */ 290 - SSAM_HUB_CONNECTED, 291 - SSAM_HUB_DISCONNECTED, 292 - }; 293 - 294 - enum ssam_hub_flags { 295 - SSAM_HUB_HOT_REMOVED, 296 - }; 297 - 298 - struct ssam_hub { 299 - struct ssam_device *sdev; 300 - 301 - enum ssam_hub_state state; 302 - unsigned long flags; 303 - 304 - struct delayed_work update_work; 305 - unsigned long connect_delay; 306 - 307 - struct ssam_event_notifier notif; 308 - 309 - int (*get_state)(struct ssam_hub *hub, enum ssam_hub_state *state); 310 - }; 311 - 312 - static void ssam_hub_update_workfn(struct work_struct *work) 313 - { 314 - struct ssam_hub *hub = container_of(work, struct ssam_hub, update_work.work); 315 - enum ssam_hub_state state; 316 - int status = 0; 317 - 318 - status = hub->get_state(hub, &state); 319 - if (status) 320 - return; 321 - 322 - /* 323 - * There is a small possibility that hub devices were hot-removed and 324 - * re-added before we were able to remove them here. In that case, both 325 - * the state returned by get_state() and the state of the hub will 326 - * equal SSAM_HUB_CONNECTED and we would bail early below, which would 327 - * leave child devices without proper (re-)initialization and the 328 - * hot-remove flag set. 329 - * 330 - * Therefore, we check whether devices have been hot-removed via an 331 - * additional flag on the hub and, in this case, override the returned 332 - * hub state. In case of a missed disconnect (i.e. get_state returned 333 - * "connected"), we further need to re-schedule this work (with the 334 - * appropriate delay) as the actual connect work submission might have 335 - * been merged with this one. 336 - * 337 - * This then leads to one of two cases: Either we submit an unnecessary 338 - * work item (which will get ignored via either the queue or the state 339 - * checks) or, in the unlikely case that the work is actually required, 340 - * double the normal connect delay. 341 - */ 342 - if (test_and_clear_bit(SSAM_HUB_HOT_REMOVED, &hub->flags)) { 343 - if (state == SSAM_HUB_CONNECTED) 344 - schedule_delayed_work(&hub->update_work, hub->connect_delay); 345 - 346 - state = SSAM_HUB_DISCONNECTED; 347 - } 348 - 349 - if (hub->state == state) 350 - return; 351 - hub->state = state; 352 - 353 - if (hub->state == SSAM_HUB_CONNECTED) 354 - status = ssam_device_register_clients(hub->sdev); 355 - else 356 - ssam_remove_clients(&hub->sdev->dev); 357 - 358 - if (status) 359 - dev_err(&hub->sdev->dev, "failed to update hub child devices: %d\n", status); 360 - } 361 - 362 - static int ssam_hub_mark_hot_removed(struct device *dev, void *_data) 363 - { 364 - struct ssam_device *sdev = to_ssam_device(dev); 365 - 366 - if (is_ssam_device(dev)) 367 - ssam_device_mark_hot_removed(sdev); 368 - 369 - return 0; 370 - } 371 - 372 - static void ssam_hub_update(struct ssam_hub *hub, bool connected) 373 - { 374 - unsigned long delay; 375 - 376 - /* Mark devices as hot-removed before we remove any. */ 377 - if (!connected) { 378 - set_bit(SSAM_HUB_HOT_REMOVED, &hub->flags); 379 - device_for_each_child_reverse(&hub->sdev->dev, NULL, ssam_hub_mark_hot_removed); 380 - } 381 - 382 - /* 383 - * Delay update when the base/keyboard cover is being connected to give 384 - * devices/EC some time to set up. 385 - */ 386 - delay = connected ? hub->connect_delay : 0; 387 - 388 - schedule_delayed_work(&hub->update_work, delay); 389 - } 390 - 391 - static int __maybe_unused ssam_hub_resume(struct device *dev) 392 - { 393 - struct ssam_hub *hub = dev_get_drvdata(dev); 394 - 395 - schedule_delayed_work(&hub->update_work, 0); 396 - return 0; 397 - } 398 - static SIMPLE_DEV_PM_OPS(ssam_hub_pm_ops, NULL, ssam_hub_resume); 399 - 400 - static int ssam_hub_setup(struct ssam_device *sdev, struct ssam_hub *hub) 401 - { 402 - int status; 403 - 404 - hub->sdev = sdev; 405 - hub->state = SSAM_HUB_UNINITIALIZED; 406 - 407 - INIT_DELAYED_WORK(&hub->update_work, ssam_hub_update_workfn); 408 - 409 - ssam_device_set_drvdata(sdev, hub); 410 - 411 - status = ssam_device_notifier_register(sdev, &hub->notif); 412 - if (status) 413 - return status; 414 - 415 - schedule_delayed_work(&hub->update_work, 0); 416 - return 0; 417 - } 418 - 419 - static void ssam_hub_remove(struct ssam_device *sdev) 420 - { 421 - struct ssam_hub *hub = ssam_device_get_drvdata(sdev); 422 - 423 - ssam_device_notifier_unregister(sdev, &hub->notif); 424 - cancel_delayed_work_sync(&hub->update_work); 425 - ssam_remove_clients(&sdev->dev); 426 - } 427 - 428 - 429 - /* -- SSAM base-hub driver. ------------------------------------------------- */ 430 - 431 - /* 432 - * Some devices (especially battery) may need a bit of time to be fully usable 433 - * after being (re-)connected. This delay has been determined via 434 - * experimentation. 435 - */ 436 - #define SSAM_BASE_UPDATE_CONNECT_DELAY msecs_to_jiffies(2500) 437 - 438 - SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, { 439 - .target_category = SSAM_SSH_TC_BAS, 440 - .target_id = 0x01, 441 - .command_id = 0x0d, 442 - .instance_id = 0x00, 443 - }); 444 - 445 - #define SSAM_BAS_OPMODE_TABLET 0x00 446 - #define SSAM_EVENT_BAS_CID_CONNECTION 0x0c 447 - 448 - static int ssam_base_hub_query_state(struct ssam_hub *hub, enum ssam_hub_state *state) 449 - { 450 - u8 opmode; 451 - int status; 452 - 453 - status = ssam_retry(ssam_bas_query_opmode, hub->sdev->ctrl, &opmode); 454 - if (status < 0) { 455 - dev_err(&hub->sdev->dev, "failed to query base state: %d\n", status); 456 - return status; 457 - } 458 - 459 - if (opmode != SSAM_BAS_OPMODE_TABLET) 460 - *state = SSAM_HUB_CONNECTED; 461 - else 462 - *state = SSAM_HUB_DISCONNECTED; 463 - 464 - return 0; 465 - } 466 - 467 - static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) 468 - { 469 - struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif); 470 - 471 - if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION) 472 - return 0; 473 - 474 - if (event->length < 1) { 475 - dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); 476 - return 0; 477 - } 478 - 479 - ssam_hub_update(hub, event->data[0]); 480 - 481 - /* 482 - * Do not return SSAM_NOTIF_HANDLED: The event should be picked up and 483 - * consumed by the detachment system driver. We're just a (more or less) 484 - * silent observer. 485 - */ 486 - return 0; 487 - } 488 - 489 - static int ssam_base_hub_probe(struct ssam_device *sdev) 490 - { 491 - struct ssam_hub *hub; 492 - 493 - hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL); 494 - if (!hub) 495 - return -ENOMEM; 496 - 497 - hub->notif.base.priority = INT_MAX; /* This notifier should run first. */ 498 - hub->notif.base.fn = ssam_base_hub_notif; 499 - hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; 500 - hub->notif.event.id.target_category = SSAM_SSH_TC_BAS, 501 - hub->notif.event.id.instance = 0, 502 - hub->notif.event.mask = SSAM_EVENT_MASK_NONE; 503 - hub->notif.event.flags = SSAM_EVENT_SEQUENCED; 504 - 505 - hub->connect_delay = SSAM_BASE_UPDATE_CONNECT_DELAY; 506 - hub->get_state = ssam_base_hub_query_state; 507 - 508 - return ssam_hub_setup(sdev, hub); 509 - } 510 - 511 - static const struct ssam_device_id ssam_base_hub_match[] = { 512 - { SSAM_VDEV(HUB, 0x02, SSAM_SSH_TC_BAS, 0x00) }, 513 - { }, 514 - }; 515 - 516 - static struct ssam_device_driver ssam_base_hub_driver = { 517 - .probe = ssam_base_hub_probe, 518 - .remove = ssam_hub_remove, 519 - .match_table = ssam_base_hub_match, 520 - .driver = { 521 - .name = "surface_aggregator_base_hub", 522 - .probe_type = PROBE_PREFER_ASYNCHRONOUS, 523 - .pm = &ssam_hub_pm_ops, 524 - }, 525 - }; 526 - 527 - 528 - /* -- SSAM KIP-subsystem hub driver. ---------------------------------------- */ 529 - 530 - /* 531 - * Some devices may need a bit of time to be fully usable after being 532 - * (re-)connected. This delay has been determined via experimentation. 533 - */ 534 - #define SSAM_KIP_UPDATE_CONNECT_DELAY msecs_to_jiffies(250) 535 - 536 - #define SSAM_EVENT_KIP_CID_CONNECTION 0x2c 537 - 538 - SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_connection_state, u8, { 539 - .target_category = SSAM_SSH_TC_KIP, 540 - .target_id = 0x01, 541 - .command_id = 0x2c, 542 - .instance_id = 0x00, 543 - }); 544 - 545 - static int ssam_kip_get_connection_state(struct ssam_hub *hub, enum ssam_hub_state *state) 546 - { 547 - int status; 548 - u8 connected; 549 - 550 - status = ssam_retry(__ssam_kip_get_connection_state, hub->sdev->ctrl, &connected); 551 - if (status < 0) { 552 - dev_err(&hub->sdev->dev, "failed to query KIP connection state: %d\n", status); 553 - return status; 554 - } 555 - 556 - *state = connected ? SSAM_HUB_CONNECTED : SSAM_HUB_DISCONNECTED; 557 - return 0; 558 - } 559 - 560 - static u32 ssam_kip_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) 561 - { 562 - struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif); 563 - 564 - if (event->command_id != SSAM_EVENT_KIP_CID_CONNECTION) 565 - return 0; /* Return "unhandled". */ 566 - 567 - if (event->length < 1) { 568 - dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); 569 - return 0; 570 - } 571 - 572 - ssam_hub_update(hub, event->data[0]); 573 - return SSAM_NOTIF_HANDLED; 574 - } 575 - 576 - static int ssam_kip_hub_probe(struct ssam_device *sdev) 577 - { 578 - struct ssam_hub *hub; 579 - 580 - hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL); 581 - if (!hub) 582 - return -ENOMEM; 583 - 584 - hub->notif.base.priority = INT_MAX; /* This notifier should run first. */ 585 - hub->notif.base.fn = ssam_kip_hub_notif; 586 - hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; 587 - hub->notif.event.id.target_category = SSAM_SSH_TC_KIP, 588 - hub->notif.event.id.instance = 0, 589 - hub->notif.event.mask = SSAM_EVENT_MASK_TARGET; 590 - hub->notif.event.flags = SSAM_EVENT_SEQUENCED; 591 - 592 - hub->connect_delay = SSAM_KIP_UPDATE_CONNECT_DELAY; 593 - hub->get_state = ssam_kip_get_connection_state; 594 - 595 - return ssam_hub_setup(sdev, hub); 596 - } 597 - 598 - static const struct ssam_device_id ssam_kip_hub_match[] = { 599 - { SSAM_VDEV(HUB, 0x01, SSAM_SSH_TC_KIP, 0x00) }, 600 - { }, 601 - }; 602 - 603 - static struct ssam_device_driver ssam_kip_hub_driver = { 604 - .probe = ssam_kip_hub_probe, 605 - .remove = ssam_hub_remove, 606 - .match_table = ssam_kip_hub_match, 607 - .driver = { 608 - .name = "surface_kip_hub", 609 - .probe_type = PROBE_PREFER_ASYNCHRONOUS, 610 - .pm = &ssam_hub_pm_ops, 611 - }, 612 - }; 613 - 614 - 615 289 /* -- SSAM platform/meta-hub driver. ---------------------------------------- */ 616 290 617 291 static const struct acpi_device_id ssam_platform_hub_match[] = { ··· 395 727 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 396 728 }, 397 729 }; 398 - 399 - 400 - /* -- Module initialization. ------------------------------------------------ */ 401 - 402 - static int __init ssam_device_hub_init(void) 403 - { 404 - int status; 405 - 406 - status = platform_driver_register(&ssam_platform_hub_driver); 407 - if (status) 408 - goto err_platform; 409 - 410 - status = ssam_device_driver_register(&ssam_base_hub_driver); 411 - if (status) 412 - goto err_base; 413 - 414 - status = ssam_device_driver_register(&ssam_kip_hub_driver); 415 - if (status) 416 - goto err_kip; 417 - 418 - return 0; 419 - 420 - err_kip: 421 - ssam_device_driver_unregister(&ssam_base_hub_driver); 422 - err_base: 423 - platform_driver_unregister(&ssam_platform_hub_driver); 424 - err_platform: 425 - return status; 426 - } 427 - module_init(ssam_device_hub_init); 428 - 429 - static void __exit ssam_device_hub_exit(void) 430 - { 431 - ssam_device_driver_unregister(&ssam_kip_hub_driver); 432 - ssam_device_driver_unregister(&ssam_base_hub_driver); 433 - platform_driver_unregister(&ssam_platform_hub_driver); 434 - } 435 - module_exit(ssam_device_hub_exit); 730 + module_platform_driver(ssam_platform_hub_driver); 436 731 437 732 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 438 733 MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module");