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