core.c (22973B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Surface Serial Hub (SSH) driver for communication with the Surface/System 4 * Aggregator Module (SSAM/SAM). 5 * 6 * Provides access to a SAM-over-SSH connected EC via a controller device. 7 * Handles communication via requests as well as enabling, disabling, and 8 * relaying of events. 9 * 10 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com> 11 */ 12 13#include <linux/acpi.h> 14#include <linux/atomic.h> 15#include <linux/completion.h> 16#include <linux/gpio/consumer.h> 17#include <linux/kernel.h> 18#include <linux/kref.h> 19#include <linux/module.h> 20#include <linux/pm.h> 21#include <linux/serdev.h> 22#include <linux/sysfs.h> 23 24#include <linux/surface_aggregator/controller.h> 25#include <linux/surface_aggregator/device.h> 26 27#include "bus.h" 28#include "controller.h" 29 30#define CREATE_TRACE_POINTS 31#include "trace.h" 32 33 34/* -- Static controller reference. ------------------------------------------ */ 35 36/* 37 * Main controller reference. The corresponding lock must be held while 38 * accessing (reading/writing) the reference. 39 */ 40static struct ssam_controller *__ssam_controller; 41static DEFINE_SPINLOCK(__ssam_controller_lock); 42 43/** 44 * ssam_get_controller() - Get reference to SSAM controller. 45 * 46 * Returns a reference to the SSAM controller of the system or %NULL if there 47 * is none, it hasn't been set up yet, or it has already been unregistered. 48 * This function automatically increments the reference count of the 49 * controller, thus the calling party must ensure that ssam_controller_put() 50 * is called when it doesn't need the controller any more. 51 */ 52struct ssam_controller *ssam_get_controller(void) 53{ 54 struct ssam_controller *ctrl; 55 56 spin_lock(&__ssam_controller_lock); 57 58 ctrl = __ssam_controller; 59 if (!ctrl) 60 goto out; 61 62 if (WARN_ON(!kref_get_unless_zero(&ctrl->kref))) 63 ctrl = NULL; 64 65out: 66 spin_unlock(&__ssam_controller_lock); 67 return ctrl; 68} 69EXPORT_SYMBOL_GPL(ssam_get_controller); 70 71/** 72 * ssam_try_set_controller() - Try to set the main controller reference. 73 * @ctrl: The controller to which the reference should point. 74 * 75 * Set the main controller reference to the given pointer if the reference 76 * hasn't been set already. 77 * 78 * Return: Returns zero on success or %-EEXIST if the reference has already 79 * been set. 80 */ 81static int ssam_try_set_controller(struct ssam_controller *ctrl) 82{ 83 int status = 0; 84 85 spin_lock(&__ssam_controller_lock); 86 if (!__ssam_controller) 87 __ssam_controller = ctrl; 88 else 89 status = -EEXIST; 90 spin_unlock(&__ssam_controller_lock); 91 92 return status; 93} 94 95/** 96 * ssam_clear_controller() - Remove/clear the main controller reference. 97 * 98 * Clears the main controller reference, i.e. sets it to %NULL. This function 99 * should be called before the controller is shut down. 100 */ 101static void ssam_clear_controller(void) 102{ 103 spin_lock(&__ssam_controller_lock); 104 __ssam_controller = NULL; 105 spin_unlock(&__ssam_controller_lock); 106} 107 108/** 109 * ssam_client_link() - Link an arbitrary client device to the controller. 110 * @c: The controller to link to. 111 * @client: The client device. 112 * 113 * Link an arbitrary client device to the controller by creating a device link 114 * between it as consumer and the controller device as provider. This function 115 * can be used for non-SSAM devices (or SSAM devices not registered as child 116 * under the controller) to guarantee that the controller is valid for as long 117 * as the driver of the client device is bound, and that proper suspend and 118 * resume ordering is guaranteed. 119 * 120 * The device link does not have to be destructed manually. It is removed 121 * automatically once the driver of the client device unbinds. 122 * 123 * Return: Returns zero on success, %-ENODEV if the controller is not ready or 124 * going to be removed soon, or %-ENOMEM if the device link could not be 125 * created for other reasons. 126 */ 127int ssam_client_link(struct ssam_controller *c, struct device *client) 128{ 129 const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER; 130 struct device_link *link; 131 struct device *ctrldev; 132 133 ssam_controller_statelock(c); 134 135 if (c->state != SSAM_CONTROLLER_STARTED) { 136 ssam_controller_stateunlock(c); 137 return -ENODEV; 138 } 139 140 ctrldev = ssam_controller_device(c); 141 if (!ctrldev) { 142 ssam_controller_stateunlock(c); 143 return -ENODEV; 144 } 145 146 link = device_link_add(client, ctrldev, flags); 147 if (!link) { 148 ssam_controller_stateunlock(c); 149 return -ENOMEM; 150 } 151 152 /* 153 * Return -ENODEV if supplier driver is on its way to be removed. In 154 * this case, the controller won't be around for much longer and the 155 * device link is not going to save us any more, as unbinding is 156 * already in progress. 157 */ 158 if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) { 159 ssam_controller_stateunlock(c); 160 return -ENODEV; 161 } 162 163 ssam_controller_stateunlock(c); 164 return 0; 165} 166EXPORT_SYMBOL_GPL(ssam_client_link); 167 168/** 169 * ssam_client_bind() - Bind an arbitrary client device to the controller. 170 * @client: The client device. 171 * 172 * Link an arbitrary client device to the controller by creating a device link 173 * between it as consumer and the main controller device as provider. This 174 * function can be used for non-SSAM devices to guarantee that the controller 175 * returned by this function is valid for as long as the driver of the client 176 * device is bound, and that proper suspend and resume ordering is guaranteed. 177 * 178 * This function does essentially the same as ssam_client_link(), except that 179 * it first fetches the main controller reference, then creates the link, and 180 * finally returns this reference. Note that this function does not increment 181 * the reference counter of the controller, as, due to the link, the 182 * controller lifetime is assured as long as the driver of the client device 183 * is bound. 184 * 185 * It is not valid to use the controller reference obtained by this method 186 * outside of the driver bound to the client device at the time of calling 187 * this function, without first incrementing the reference count of the 188 * controller via ssam_controller_get(). Even after doing this, care must be 189 * taken that requests are only submitted and notifiers are only 190 * (un-)registered when the controller is active and not suspended. In other 191 * words: The device link only lives as long as the client driver is bound and 192 * any guarantees enforced by this link (e.g. active controller state) can 193 * only be relied upon as long as this link exists and may need to be enforced 194 * in other ways afterwards. 195 * 196 * The created device link does not have to be destructed manually. It is 197 * removed automatically once the driver of the client device unbinds. 198 * 199 * Return: Returns the controller on success, an error pointer with %-ENODEV 200 * if the controller is not present, not ready or going to be removed soon, or 201 * %-ENOMEM if the device link could not be created for other reasons. 202 */ 203struct ssam_controller *ssam_client_bind(struct device *client) 204{ 205 struct ssam_controller *c; 206 int status; 207 208 c = ssam_get_controller(); 209 if (!c) 210 return ERR_PTR(-ENODEV); 211 212 status = ssam_client_link(c, client); 213 214 /* 215 * Note that we can drop our controller reference in both success and 216 * failure cases: On success, we have bound the controller lifetime 217 * inherently to the client driver lifetime, i.e. it the controller is 218 * now guaranteed to outlive the client driver. On failure, we're not 219 * going to use the controller any more. 220 */ 221 ssam_controller_put(c); 222 223 return status >= 0 ? c : ERR_PTR(status); 224} 225EXPORT_SYMBOL_GPL(ssam_client_bind); 226 227 228/* -- Glue layer (serdev_device -> ssam_controller). ------------------------ */ 229 230static int ssam_receive_buf(struct serdev_device *dev, const unsigned char *buf, 231 size_t n) 232{ 233 struct ssam_controller *ctrl; 234 235 ctrl = serdev_device_get_drvdata(dev); 236 return ssam_controller_receive_buf(ctrl, buf, n); 237} 238 239static void ssam_write_wakeup(struct serdev_device *dev) 240{ 241 ssam_controller_write_wakeup(serdev_device_get_drvdata(dev)); 242} 243 244static const struct serdev_device_ops ssam_serdev_ops = { 245 .receive_buf = ssam_receive_buf, 246 .write_wakeup = ssam_write_wakeup, 247}; 248 249 250/* -- SysFS and misc. ------------------------------------------------------- */ 251 252static int ssam_log_firmware_version(struct ssam_controller *ctrl) 253{ 254 u32 version, a, b, c; 255 int status; 256 257 status = ssam_get_firmware_version(ctrl, &version); 258 if (status) 259 return status; 260 261 a = (version >> 24) & 0xff; 262 b = ((version >> 8) & 0xffff); 263 c = version & 0xff; 264 265 ssam_info(ctrl, "SAM firmware version: %u.%u.%u\n", a, b, c); 266 return 0; 267} 268 269static ssize_t firmware_version_show(struct device *dev, 270 struct device_attribute *attr, char *buf) 271{ 272 struct ssam_controller *ctrl = dev_get_drvdata(dev); 273 u32 version, a, b, c; 274 int status; 275 276 status = ssam_get_firmware_version(ctrl, &version); 277 if (status < 0) 278 return status; 279 280 a = (version >> 24) & 0xff; 281 b = ((version >> 8) & 0xffff); 282 c = version & 0xff; 283 284 return sysfs_emit(buf, "%u.%u.%u\n", a, b, c); 285} 286static DEVICE_ATTR_RO(firmware_version); 287 288static struct attribute *ssam_sam_attrs[] = { 289 &dev_attr_firmware_version.attr, 290 NULL 291}; 292 293static const struct attribute_group ssam_sam_group = { 294 .name = "sam", 295 .attrs = ssam_sam_attrs, 296}; 297 298 299/* -- ACPI based device setup. ---------------------------------------------- */ 300 301static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc, 302 void *ctx) 303{ 304 struct serdev_device *serdev = ctx; 305 struct acpi_resource_uart_serialbus *uart; 306 bool flow_control; 307 int status = 0; 308 309 if (!serdev_acpi_get_uart_resource(rsc, &uart)) 310 return AE_OK; 311 312 /* Set up serdev device. */ 313 serdev_device_set_baudrate(serdev, uart->default_baud_rate); 314 315 /* serdev currently only supports RTSCTS flow control. */ 316 if (uart->flow_control & (~((u8)ACPI_UART_FLOW_CONTROL_HW))) { 317 dev_warn(&serdev->dev, "setup: unsupported flow control (value: %#04x)\n", 318 uart->flow_control); 319 } 320 321 /* Set RTSCTS flow control. */ 322 flow_control = uart->flow_control & ACPI_UART_FLOW_CONTROL_HW; 323 serdev_device_set_flow_control(serdev, flow_control); 324 325 /* serdev currently only supports EVEN/ODD parity. */ 326 switch (uart->parity) { 327 case ACPI_UART_PARITY_NONE: 328 status = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); 329 break; 330 case ACPI_UART_PARITY_EVEN: 331 status = serdev_device_set_parity(serdev, SERDEV_PARITY_EVEN); 332 break; 333 case ACPI_UART_PARITY_ODD: 334 status = serdev_device_set_parity(serdev, SERDEV_PARITY_ODD); 335 break; 336 default: 337 dev_warn(&serdev->dev, "setup: unsupported parity (value: %#04x)\n", 338 uart->parity); 339 break; 340 } 341 342 if (status) { 343 dev_err(&serdev->dev, "setup: failed to set parity (value: %#04x, error: %d)\n", 344 uart->parity, status); 345 return AE_ERROR; 346 } 347 348 /* We've found the resource and are done. */ 349 return AE_CTRL_TERMINATE; 350} 351 352static acpi_status ssam_serdev_setup_via_acpi(acpi_handle handle, 353 struct serdev_device *serdev) 354{ 355 return acpi_walk_resources(handle, METHOD_NAME__CRS, 356 ssam_serdev_setup_via_acpi_crs, serdev); 357} 358 359 360/* -- Power management. ----------------------------------------------------- */ 361 362static void ssam_serial_hub_shutdown(struct device *dev) 363{ 364 struct ssam_controller *c = dev_get_drvdata(dev); 365 int status; 366 367 /* 368 * Try to disable notifiers, signal display-off and D0-exit, ignore any 369 * errors. 370 * 371 * Note: It has not been established yet if this is actually 372 * necessary/useful for shutdown. 373 */ 374 375 status = ssam_notifier_disable_registered(c); 376 if (status) { 377 ssam_err(c, "pm: failed to disable notifiers for shutdown: %d\n", 378 status); 379 } 380 381 status = ssam_ctrl_notif_display_off(c); 382 if (status) 383 ssam_err(c, "pm: display-off notification failed: %d\n", status); 384 385 status = ssam_ctrl_notif_d0_exit(c); 386 if (status) 387 ssam_err(c, "pm: D0-exit notification failed: %d\n", status); 388} 389 390#ifdef CONFIG_PM_SLEEP 391 392static int ssam_serial_hub_pm_prepare(struct device *dev) 393{ 394 struct ssam_controller *c = dev_get_drvdata(dev); 395 int status; 396 397 /* 398 * Try to signal display-off, This will quiesce events. 399 * 400 * Note: Signaling display-off/display-on should normally be done from 401 * some sort of display state notifier. As that is not available, 402 * signal it here. 403 */ 404 405 status = ssam_ctrl_notif_display_off(c); 406 if (status) 407 ssam_err(c, "pm: display-off notification failed: %d\n", status); 408 409 return status; 410} 411 412static void ssam_serial_hub_pm_complete(struct device *dev) 413{ 414 struct ssam_controller *c = dev_get_drvdata(dev); 415 int status; 416 417 /* 418 * Try to signal display-on. This will restore events. 419 * 420 * Note: Signaling display-off/display-on should normally be done from 421 * some sort of display state notifier. As that is not available, 422 * signal it here. 423 */ 424 425 status = ssam_ctrl_notif_display_on(c); 426 if (status) 427 ssam_err(c, "pm: display-on notification failed: %d\n", status); 428} 429 430static int ssam_serial_hub_pm_suspend(struct device *dev) 431{ 432 struct ssam_controller *c = dev_get_drvdata(dev); 433 int status; 434 435 /* 436 * Try to signal D0-exit, enable IRQ wakeup if specified. Abort on 437 * error. 438 */ 439 440 status = ssam_ctrl_notif_d0_exit(c); 441 if (status) { 442 ssam_err(c, "pm: D0-exit notification failed: %d\n", status); 443 goto err_notif; 444 } 445 446 status = ssam_irq_arm_for_wakeup(c); 447 if (status) 448 goto err_irq; 449 450 WARN_ON(ssam_controller_suspend(c)); 451 return 0; 452 453err_irq: 454 ssam_ctrl_notif_d0_entry(c); 455err_notif: 456 ssam_ctrl_notif_display_on(c); 457 return status; 458} 459 460static int ssam_serial_hub_pm_resume(struct device *dev) 461{ 462 struct ssam_controller *c = dev_get_drvdata(dev); 463 int status; 464 465 WARN_ON(ssam_controller_resume(c)); 466 467 /* 468 * Try to disable IRQ wakeup (if specified) and signal D0-entry. In 469 * case of errors, log them and try to restore normal operation state 470 * as far as possible. 471 * 472 * Note: Signaling display-off/display-on should normally be done from 473 * some sort of display state notifier. As that is not available, 474 * signal it here. 475 */ 476 477 ssam_irq_disarm_wakeup(c); 478 479 status = ssam_ctrl_notif_d0_entry(c); 480 if (status) 481 ssam_err(c, "pm: D0-entry notification failed: %d\n", status); 482 483 return 0; 484} 485 486static int ssam_serial_hub_pm_freeze(struct device *dev) 487{ 488 struct ssam_controller *c = dev_get_drvdata(dev); 489 int status; 490 491 /* 492 * During hibernation image creation, we only have to ensure that the 493 * EC doesn't send us any events. This is done via the display-off 494 * and D0-exit notifications. Note that this sets up the wakeup IRQ 495 * on the EC side, however, we have disabled it by default on our side 496 * and won't enable it here. 497 * 498 * See ssam_serial_hub_poweroff() for more details on the hibernation 499 * process. 500 */ 501 502 status = ssam_ctrl_notif_d0_exit(c); 503 if (status) { 504 ssam_err(c, "pm: D0-exit notification failed: %d\n", status); 505 ssam_ctrl_notif_display_on(c); 506 return status; 507 } 508 509 WARN_ON(ssam_controller_suspend(c)); 510 return 0; 511} 512 513static int ssam_serial_hub_pm_thaw(struct device *dev) 514{ 515 struct ssam_controller *c = dev_get_drvdata(dev); 516 int status; 517 518 WARN_ON(ssam_controller_resume(c)); 519 520 status = ssam_ctrl_notif_d0_entry(c); 521 if (status) 522 ssam_err(c, "pm: D0-exit notification failed: %d\n", status); 523 524 return status; 525} 526 527static int ssam_serial_hub_pm_poweroff(struct device *dev) 528{ 529 struct ssam_controller *c = dev_get_drvdata(dev); 530 int status; 531 532 /* 533 * When entering hibernation and powering off the system, the EC, at 534 * least on some models, may disable events. Without us taking care of 535 * that, this leads to events not being enabled/restored when the 536 * system resumes from hibernation, resulting SAM-HID subsystem devices 537 * (i.e. keyboard, touchpad) not working, AC-plug/AC-unplug events being 538 * gone, etc. 539 * 540 * To avoid these issues, we disable all registered events here (this is 541 * likely not actually required) and restore them during the drivers PM 542 * restore callback. 543 * 544 * Wakeup from the EC interrupt is not supported during hibernation, 545 * so don't arm the IRQ here. 546 */ 547 548 status = ssam_notifier_disable_registered(c); 549 if (status) { 550 ssam_err(c, "pm: failed to disable notifiers for hibernation: %d\n", 551 status); 552 return status; 553 } 554 555 status = ssam_ctrl_notif_d0_exit(c); 556 if (status) { 557 ssam_err(c, "pm: D0-exit notification failed: %d\n", status); 558 ssam_notifier_restore_registered(c); 559 return status; 560 } 561 562 WARN_ON(ssam_controller_suspend(c)); 563 return 0; 564} 565 566static int ssam_serial_hub_pm_restore(struct device *dev) 567{ 568 struct ssam_controller *c = dev_get_drvdata(dev); 569 int status; 570 571 /* 572 * Ignore but log errors, try to restore state as much as possible in 573 * case of failures. See ssam_serial_hub_poweroff() for more details on 574 * the hibernation process. 575 */ 576 577 WARN_ON(ssam_controller_resume(c)); 578 579 status = ssam_ctrl_notif_d0_entry(c); 580 if (status) 581 ssam_err(c, "pm: D0-entry notification failed: %d\n", status); 582 583 ssam_notifier_restore_registered(c); 584 return 0; 585} 586 587static const struct dev_pm_ops ssam_serial_hub_pm_ops = { 588 .prepare = ssam_serial_hub_pm_prepare, 589 .complete = ssam_serial_hub_pm_complete, 590 .suspend = ssam_serial_hub_pm_suspend, 591 .resume = ssam_serial_hub_pm_resume, 592 .freeze = ssam_serial_hub_pm_freeze, 593 .thaw = ssam_serial_hub_pm_thaw, 594 .poweroff = ssam_serial_hub_pm_poweroff, 595 .restore = ssam_serial_hub_pm_restore, 596}; 597 598#else /* CONFIG_PM_SLEEP */ 599 600static const struct dev_pm_ops ssam_serial_hub_pm_ops = { }; 601 602#endif /* CONFIG_PM_SLEEP */ 603 604 605/* -- Device/driver setup. -------------------------------------------------- */ 606 607static const struct acpi_gpio_params gpio_ssam_wakeup_int = { 0, 0, false }; 608static const struct acpi_gpio_params gpio_ssam_wakeup = { 1, 0, false }; 609 610static const struct acpi_gpio_mapping ssam_acpi_gpios[] = { 611 { "ssam_wakeup-int-gpio", &gpio_ssam_wakeup_int, 1 }, 612 { "ssam_wakeup-gpio", &gpio_ssam_wakeup, 1 }, 613 { }, 614}; 615 616static int ssam_serial_hub_probe(struct serdev_device *serdev) 617{ 618 struct acpi_device *ssh = ACPI_COMPANION(&serdev->dev); 619 struct ssam_controller *ctrl; 620 acpi_status astatus; 621 int status; 622 623 if (gpiod_count(&serdev->dev, NULL) < 0) 624 return -ENODEV; 625 626 status = devm_acpi_dev_add_driver_gpios(&serdev->dev, ssam_acpi_gpios); 627 if (status) 628 return status; 629 630 /* Allocate controller. */ 631 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); 632 if (!ctrl) 633 return -ENOMEM; 634 635 /* Initialize controller. */ 636 status = ssam_controller_init(ctrl, serdev); 637 if (status) 638 goto err_ctrl_init; 639 640 ssam_controller_lock(ctrl); 641 642 /* Set up serdev device. */ 643 serdev_device_set_drvdata(serdev, ctrl); 644 serdev_device_set_client_ops(serdev, &ssam_serdev_ops); 645 status = serdev_device_open(serdev); 646 if (status) 647 goto err_devopen; 648 649 astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev); 650 if (ACPI_FAILURE(astatus)) { 651 status = -ENXIO; 652 goto err_devinit; 653 } 654 655 /* Start controller. */ 656 status = ssam_controller_start(ctrl); 657 if (status) 658 goto err_devinit; 659 660 ssam_controller_unlock(ctrl); 661 662 /* 663 * Initial SAM requests: Log version and notify default/init power 664 * states. 665 */ 666 status = ssam_log_firmware_version(ctrl); 667 if (status) 668 goto err_initrq; 669 670 status = ssam_ctrl_notif_d0_entry(ctrl); 671 if (status) 672 goto err_initrq; 673 674 status = ssam_ctrl_notif_display_on(ctrl); 675 if (status) 676 goto err_initrq; 677 678 status = sysfs_create_group(&serdev->dev.kobj, &ssam_sam_group); 679 if (status) 680 goto err_initrq; 681 682 /* Set up IRQ. */ 683 status = ssam_irq_setup(ctrl); 684 if (status) 685 goto err_irq; 686 687 /* Finally, set main controller reference. */ 688 status = ssam_try_set_controller(ctrl); 689 if (WARN_ON(status)) /* Currently, we're the only provider. */ 690 goto err_mainref; 691 692 /* 693 * TODO: The EC can wake up the system via the associated GPIO interrupt 694 * in multiple situations. One of which is the remaining battery 695 * capacity falling below a certain threshold. Normally, we should 696 * use the device_init_wakeup function, however, the EC also seems 697 * to have other reasons for waking up the system and it seems 698 * that Windows has additional checks whether the system should be 699 * resumed. In short, this causes some spurious unwanted wake-ups. 700 * For now let's thus default power/wakeup to false. 701 */ 702 device_set_wakeup_capable(&serdev->dev, true); 703 acpi_dev_clear_dependencies(ssh); 704 705 return 0; 706 707err_mainref: 708 ssam_irq_free(ctrl); 709err_irq: 710 sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group); 711err_initrq: 712 ssam_controller_lock(ctrl); 713 ssam_controller_shutdown(ctrl); 714err_devinit: 715 serdev_device_close(serdev); 716err_devopen: 717 ssam_controller_destroy(ctrl); 718 ssam_controller_unlock(ctrl); 719err_ctrl_init: 720 kfree(ctrl); 721 return status; 722} 723 724static void ssam_serial_hub_remove(struct serdev_device *serdev) 725{ 726 struct ssam_controller *ctrl = serdev_device_get_drvdata(serdev); 727 int status; 728 729 /* Clear static reference so that no one else can get a new one. */ 730 ssam_clear_controller(); 731 732 /* Disable and free IRQ. */ 733 ssam_irq_free(ctrl); 734 735 sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group); 736 ssam_controller_lock(ctrl); 737 738 /* Remove all client devices. */ 739 ssam_remove_clients(&serdev->dev); 740 741 /* Act as if suspending to silence events. */ 742 status = ssam_ctrl_notif_display_off(ctrl); 743 if (status) { 744 dev_err(&serdev->dev, "display-off notification failed: %d\n", 745 status); 746 } 747 748 status = ssam_ctrl_notif_d0_exit(ctrl); 749 if (status) { 750 dev_err(&serdev->dev, "D0-exit notification failed: %d\n", 751 status); 752 } 753 754 /* Shut down controller and remove serdev device reference from it. */ 755 ssam_controller_shutdown(ctrl); 756 757 /* Shut down actual transport. */ 758 serdev_device_wait_until_sent(serdev, 0); 759 serdev_device_close(serdev); 760 761 /* Drop our controller reference. */ 762 ssam_controller_unlock(ctrl); 763 ssam_controller_put(ctrl); 764 765 device_set_wakeup_capable(&serdev->dev, false); 766} 767 768static const struct acpi_device_id ssam_serial_hub_match[] = { 769 { "MSHW0084", 0 }, 770 { }, 771}; 772MODULE_DEVICE_TABLE(acpi, ssam_serial_hub_match); 773 774static struct serdev_device_driver ssam_serial_hub = { 775 .probe = ssam_serial_hub_probe, 776 .remove = ssam_serial_hub_remove, 777 .driver = { 778 .name = "surface_serial_hub", 779 .acpi_match_table = ssam_serial_hub_match, 780 .pm = &ssam_serial_hub_pm_ops, 781 .shutdown = ssam_serial_hub_shutdown, 782 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 783 }, 784}; 785 786 787/* -- Module setup. --------------------------------------------------------- */ 788 789static int __init ssam_core_init(void) 790{ 791 int status; 792 793 status = ssam_bus_register(); 794 if (status) 795 goto err_bus; 796 797 status = ssh_ctrl_packet_cache_init(); 798 if (status) 799 goto err_cpkg; 800 801 status = ssam_event_item_cache_init(); 802 if (status) 803 goto err_evitem; 804 805 status = serdev_device_driver_register(&ssam_serial_hub); 806 if (status) 807 goto err_register; 808 809 return 0; 810 811err_register: 812 ssam_event_item_cache_destroy(); 813err_evitem: 814 ssh_ctrl_packet_cache_destroy(); 815err_cpkg: 816 ssam_bus_unregister(); 817err_bus: 818 return status; 819} 820subsys_initcall(ssam_core_init); 821 822static void __exit ssam_core_exit(void) 823{ 824 serdev_device_driver_unregister(&ssam_serial_hub); 825 ssam_event_item_cache_destroy(); 826 ssh_ctrl_packet_cache_destroy(); 827 ssam_bus_unregister(); 828} 829module_exit(ssam_core_exit); 830 831MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 832MODULE_DESCRIPTION("Subsystem and Surface Serial Hub driver for Surface System Aggregator Module"); 833MODULE_LICENSE("GPL");