Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Surface Serial Hub (SSH) driver for communication with the Surface/System
0004  * Aggregator Module (SSAM/SAM).
0005  *
0006  * Provides access to a SAM-over-SSH connected EC via a controller device.
0007  * Handles communication via requests as well as enabling, disabling, and
0008  * relaying of events.
0009  *
0010  * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
0011  */
0012 
0013 #include <linux/acpi.h>
0014 #include <linux/atomic.h>
0015 #include <linux/completion.h>
0016 #include <linux/gpio/consumer.h>
0017 #include <linux/kernel.h>
0018 #include <linux/kref.h>
0019 #include <linux/module.h>
0020 #include <linux/pm.h>
0021 #include <linux/serdev.h>
0022 #include <linux/sysfs.h>
0023 
0024 #include <linux/surface_aggregator/controller.h>
0025 #include <linux/surface_aggregator/device.h>
0026 
0027 #include "bus.h"
0028 #include "controller.h"
0029 
0030 #define CREATE_TRACE_POINTS
0031 #include "trace.h"
0032 
0033 
0034 /* -- Static controller reference. ------------------------------------------ */
0035 
0036 /*
0037  * Main controller reference. The corresponding lock must be held while
0038  * accessing (reading/writing) the reference.
0039  */
0040 static struct ssam_controller *__ssam_controller;
0041 static DEFINE_SPINLOCK(__ssam_controller_lock);
0042 
0043 /**
0044  * ssam_get_controller() - Get reference to SSAM controller.
0045  *
0046  * Returns a reference to the SSAM controller of the system or %NULL if there
0047  * is none, it hasn't been set up yet, or it has already been unregistered.
0048  * This function automatically increments the reference count of the
0049  * controller, thus the calling party must ensure that ssam_controller_put()
0050  * is called when it doesn't need the controller any more.
0051  */
0052 struct ssam_controller *ssam_get_controller(void)
0053 {
0054     struct ssam_controller *ctrl;
0055 
0056     spin_lock(&__ssam_controller_lock);
0057 
0058     ctrl = __ssam_controller;
0059     if (!ctrl)
0060         goto out;
0061 
0062     if (WARN_ON(!kref_get_unless_zero(&ctrl->kref)))
0063         ctrl = NULL;
0064 
0065 out:
0066     spin_unlock(&__ssam_controller_lock);
0067     return ctrl;
0068 }
0069 EXPORT_SYMBOL_GPL(ssam_get_controller);
0070 
0071 /**
0072  * ssam_try_set_controller() - Try to set the main controller reference.
0073  * @ctrl: The controller to which the reference should point.
0074  *
0075  * Set the main controller reference to the given pointer if the reference
0076  * hasn't been set already.
0077  *
0078  * Return: Returns zero on success or %-EEXIST if the reference has already
0079  * been set.
0080  */
0081 static int ssam_try_set_controller(struct ssam_controller *ctrl)
0082 {
0083     int status = 0;
0084 
0085     spin_lock(&__ssam_controller_lock);
0086     if (!__ssam_controller)
0087         __ssam_controller = ctrl;
0088     else
0089         status = -EEXIST;
0090     spin_unlock(&__ssam_controller_lock);
0091 
0092     return status;
0093 }
0094 
0095 /**
0096  * ssam_clear_controller() - Remove/clear the main controller reference.
0097  *
0098  * Clears the main controller reference, i.e. sets it to %NULL. This function
0099  * should be called before the controller is shut down.
0100  */
0101 static void ssam_clear_controller(void)
0102 {
0103     spin_lock(&__ssam_controller_lock);
0104     __ssam_controller = NULL;
0105     spin_unlock(&__ssam_controller_lock);
0106 }
0107 
0108 /**
0109  * ssam_client_link() - Link an arbitrary client device to the controller.
0110  * @c: The controller to link to.
0111  * @client: The client device.
0112  *
0113  * Link an arbitrary client device to the controller by creating a device link
0114  * between it as consumer and the controller device as provider. This function
0115  * can be used for non-SSAM devices (or SSAM devices not registered as child
0116  * under the controller) to guarantee that the controller is valid for as long
0117  * as the driver of the client device is bound, and that proper suspend and
0118  * resume ordering is guaranteed.
0119  *
0120  * The device link does not have to be destructed manually. It is removed
0121  * automatically once the driver of the client device unbinds.
0122  *
0123  * Return: Returns zero on success, %-ENODEV if the controller is not ready or
0124  * going to be removed soon, or %-ENOMEM if the device link could not be
0125  * created for other reasons.
0126  */
0127 int ssam_client_link(struct ssam_controller *c, struct device *client)
0128 {
0129     const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER;
0130     struct device_link *link;
0131     struct device *ctrldev;
0132 
0133     ssam_controller_statelock(c);
0134 
0135     if (c->state != SSAM_CONTROLLER_STARTED) {
0136         ssam_controller_stateunlock(c);
0137         return -ENODEV;
0138     }
0139 
0140     ctrldev = ssam_controller_device(c);
0141     if (!ctrldev) {
0142         ssam_controller_stateunlock(c);
0143         return -ENODEV;
0144     }
0145 
0146     link = device_link_add(client, ctrldev, flags);
0147     if (!link) {
0148         ssam_controller_stateunlock(c);
0149         return -ENOMEM;
0150     }
0151 
0152     /*
0153      * Return -ENODEV if supplier driver is on its way to be removed. In
0154      * this case, the controller won't be around for much longer and the
0155      * device link is not going to save us any more, as unbinding is
0156      * already in progress.
0157      */
0158     if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) {
0159         ssam_controller_stateunlock(c);
0160         return -ENODEV;
0161     }
0162 
0163     ssam_controller_stateunlock(c);
0164     return 0;
0165 }
0166 EXPORT_SYMBOL_GPL(ssam_client_link);
0167 
0168 /**
0169  * ssam_client_bind() - Bind an arbitrary client device to the controller.
0170  * @client: The client device.
0171  *
0172  * Link an arbitrary client device to the controller by creating a device link
0173  * between it as consumer and the main controller device as provider. This
0174  * function can be used for non-SSAM devices to guarantee that the controller
0175  * returned by this function is valid for as long as the driver of the client
0176  * device is bound, and that proper suspend and resume ordering is guaranteed.
0177  *
0178  * This function does essentially the same as ssam_client_link(), except that
0179  * it first fetches the main controller reference, then creates the link, and
0180  * finally returns this reference. Note that this function does not increment
0181  * the reference counter of the controller, as, due to the link, the
0182  * controller lifetime is assured as long as the driver of the client device
0183  * is bound.
0184  *
0185  * It is not valid to use the controller reference obtained by this method
0186  * outside of the driver bound to the client device at the time of calling
0187  * this function, without first incrementing the reference count of the
0188  * controller via ssam_controller_get(). Even after doing this, care must be
0189  * taken that requests are only submitted and notifiers are only
0190  * (un-)registered when the controller is active and not suspended. In other
0191  * words: The device link only lives as long as the client driver is bound and
0192  * any guarantees enforced by this link (e.g. active controller state) can
0193  * only be relied upon as long as this link exists and may need to be enforced
0194  * in other ways afterwards.
0195  *
0196  * The created device link does not have to be destructed manually. It is
0197  * removed automatically once the driver of the client device unbinds.
0198  *
0199  * Return: Returns the controller on success, an error pointer with %-ENODEV
0200  * if the controller is not present, not ready or going to be removed soon, or
0201  * %-ENOMEM if the device link could not be created for other reasons.
0202  */
0203 struct ssam_controller *ssam_client_bind(struct device *client)
0204 {
0205     struct ssam_controller *c;
0206     int status;
0207 
0208     c = ssam_get_controller();
0209     if (!c)
0210         return ERR_PTR(-ENODEV);
0211 
0212     status = ssam_client_link(c, client);
0213 
0214     /*
0215      * Note that we can drop our controller reference in both success and
0216      * failure cases: On success, we have bound the controller lifetime
0217      * inherently to the client driver lifetime, i.e. it the controller is
0218      * now guaranteed to outlive the client driver. On failure, we're not
0219      * going to use the controller any more.
0220      */
0221     ssam_controller_put(c);
0222 
0223     return status >= 0 ? c : ERR_PTR(status);
0224 }
0225 EXPORT_SYMBOL_GPL(ssam_client_bind);
0226 
0227 
0228 /* -- Glue layer (serdev_device -> ssam_controller). ------------------------ */
0229 
0230 static int ssam_receive_buf(struct serdev_device *dev, const unsigned char *buf,
0231                 size_t n)
0232 {
0233     struct ssam_controller *ctrl;
0234 
0235     ctrl = serdev_device_get_drvdata(dev);
0236     return ssam_controller_receive_buf(ctrl, buf, n);
0237 }
0238 
0239 static void ssam_write_wakeup(struct serdev_device *dev)
0240 {
0241     ssam_controller_write_wakeup(serdev_device_get_drvdata(dev));
0242 }
0243 
0244 static const struct serdev_device_ops ssam_serdev_ops = {
0245     .receive_buf = ssam_receive_buf,
0246     .write_wakeup = ssam_write_wakeup,
0247 };
0248 
0249 
0250 /* -- SysFS and misc. ------------------------------------------------------- */
0251 
0252 static int ssam_log_firmware_version(struct ssam_controller *ctrl)
0253 {
0254     u32 version, a, b, c;
0255     int status;
0256 
0257     status = ssam_get_firmware_version(ctrl, &version);
0258     if (status)
0259         return status;
0260 
0261     a = (version >> 24) & 0xff;
0262     b = ((version >> 8) & 0xffff);
0263     c = version & 0xff;
0264 
0265     ssam_info(ctrl, "SAM firmware version: %u.%u.%u\n", a, b, c);
0266     return 0;
0267 }
0268 
0269 static ssize_t firmware_version_show(struct device *dev,
0270                      struct device_attribute *attr, char *buf)
0271 {
0272     struct ssam_controller *ctrl = dev_get_drvdata(dev);
0273     u32 version, a, b, c;
0274     int status;
0275 
0276     status = ssam_get_firmware_version(ctrl, &version);
0277     if (status < 0)
0278         return status;
0279 
0280     a = (version >> 24) & 0xff;
0281     b = ((version >> 8) & 0xffff);
0282     c = version & 0xff;
0283 
0284     return sysfs_emit(buf, "%u.%u.%u\n", a, b, c);
0285 }
0286 static DEVICE_ATTR_RO(firmware_version);
0287 
0288 static struct attribute *ssam_sam_attrs[] = {
0289     &dev_attr_firmware_version.attr,
0290     NULL
0291 };
0292 
0293 static const struct attribute_group ssam_sam_group = {
0294     .name = "sam",
0295     .attrs = ssam_sam_attrs,
0296 };
0297 
0298 
0299 /* -- ACPI based device setup. ---------------------------------------------- */
0300 
0301 static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc,
0302                           void *ctx)
0303 {
0304     struct serdev_device *serdev = ctx;
0305     struct acpi_resource_uart_serialbus *uart;
0306     bool flow_control;
0307     int status = 0;
0308 
0309     if (!serdev_acpi_get_uart_resource(rsc, &uart))
0310         return AE_OK;
0311 
0312     /* Set up serdev device. */
0313     serdev_device_set_baudrate(serdev, uart->default_baud_rate);
0314 
0315     /* serdev currently only supports RTSCTS flow control. */
0316     if (uart->flow_control & (~((u8)ACPI_UART_FLOW_CONTROL_HW))) {
0317         dev_warn(&serdev->dev, "setup: unsupported flow control (value: %#04x)\n",
0318              uart->flow_control);
0319     }
0320 
0321     /* Set RTSCTS flow control. */
0322     flow_control = uart->flow_control & ACPI_UART_FLOW_CONTROL_HW;
0323     serdev_device_set_flow_control(serdev, flow_control);
0324 
0325     /* serdev currently only supports EVEN/ODD parity. */
0326     switch (uart->parity) {
0327     case ACPI_UART_PARITY_NONE:
0328         status = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
0329         break;
0330     case ACPI_UART_PARITY_EVEN:
0331         status = serdev_device_set_parity(serdev, SERDEV_PARITY_EVEN);
0332         break;
0333     case ACPI_UART_PARITY_ODD:
0334         status = serdev_device_set_parity(serdev, SERDEV_PARITY_ODD);
0335         break;
0336     default:
0337         dev_warn(&serdev->dev, "setup: unsupported parity (value: %#04x)\n",
0338              uart->parity);
0339         break;
0340     }
0341 
0342     if (status) {
0343         dev_err(&serdev->dev, "setup: failed to set parity (value: %#04x, error: %d)\n",
0344             uart->parity, status);
0345         return AE_ERROR;
0346     }
0347 
0348     /* We've found the resource and are done. */
0349     return AE_CTRL_TERMINATE;
0350 }
0351 
0352 static acpi_status ssam_serdev_setup_via_acpi(acpi_handle handle,
0353                           struct serdev_device *serdev)
0354 {
0355     return acpi_walk_resources(handle, METHOD_NAME__CRS,
0356                    ssam_serdev_setup_via_acpi_crs, serdev);
0357 }
0358 
0359 
0360 /* -- Power management. ----------------------------------------------------- */
0361 
0362 static void ssam_serial_hub_shutdown(struct device *dev)
0363 {
0364     struct ssam_controller *c = dev_get_drvdata(dev);
0365     int status;
0366 
0367     /*
0368      * Try to disable notifiers, signal display-off and D0-exit, ignore any
0369      * errors.
0370      *
0371      * Note: It has not been established yet if this is actually
0372      * necessary/useful for shutdown.
0373      */
0374 
0375     status = ssam_notifier_disable_registered(c);
0376     if (status) {
0377         ssam_err(c, "pm: failed to disable notifiers for shutdown: %d\n",
0378              status);
0379     }
0380 
0381     status = ssam_ctrl_notif_display_off(c);
0382     if (status)
0383         ssam_err(c, "pm: display-off notification failed: %d\n", status);
0384 
0385     status = ssam_ctrl_notif_d0_exit(c);
0386     if (status)
0387         ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
0388 }
0389 
0390 #ifdef CONFIG_PM_SLEEP
0391 
0392 static int ssam_serial_hub_pm_prepare(struct device *dev)
0393 {
0394     struct ssam_controller *c = dev_get_drvdata(dev);
0395     int status;
0396 
0397     /*
0398      * Try to signal display-off, This will quiesce events.
0399      *
0400      * Note: Signaling display-off/display-on should normally be done from
0401      * some sort of display state notifier. As that is not available,
0402      * signal it here.
0403      */
0404 
0405     status = ssam_ctrl_notif_display_off(c);
0406     if (status)
0407         ssam_err(c, "pm: display-off notification failed: %d\n", status);
0408 
0409     return status;
0410 }
0411 
0412 static void ssam_serial_hub_pm_complete(struct device *dev)
0413 {
0414     struct ssam_controller *c = dev_get_drvdata(dev);
0415     int status;
0416 
0417     /*
0418      * Try to signal display-on. This will restore events.
0419      *
0420      * Note: Signaling display-off/display-on should normally be done from
0421      * some sort of display state notifier. As that is not available,
0422      * signal it here.
0423      */
0424 
0425     status = ssam_ctrl_notif_display_on(c);
0426     if (status)
0427         ssam_err(c, "pm: display-on notification failed: %d\n", status);
0428 }
0429 
0430 static int ssam_serial_hub_pm_suspend(struct device *dev)
0431 {
0432     struct ssam_controller *c = dev_get_drvdata(dev);
0433     int status;
0434 
0435     /*
0436      * Try to signal D0-exit, enable IRQ wakeup if specified. Abort on
0437      * error.
0438      */
0439 
0440     status = ssam_ctrl_notif_d0_exit(c);
0441     if (status) {
0442         ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
0443         goto err_notif;
0444     }
0445 
0446     status = ssam_irq_arm_for_wakeup(c);
0447     if (status)
0448         goto err_irq;
0449 
0450     WARN_ON(ssam_controller_suspend(c));
0451     return 0;
0452 
0453 err_irq:
0454     ssam_ctrl_notif_d0_entry(c);
0455 err_notif:
0456     ssam_ctrl_notif_display_on(c);
0457     return status;
0458 }
0459 
0460 static int ssam_serial_hub_pm_resume(struct device *dev)
0461 {
0462     struct ssam_controller *c = dev_get_drvdata(dev);
0463     int status;
0464 
0465     WARN_ON(ssam_controller_resume(c));
0466 
0467     /*
0468      * Try to disable IRQ wakeup (if specified) and signal D0-entry. In
0469      * case of errors, log them and try to restore normal operation state
0470      * as far as possible.
0471      *
0472      * Note: Signaling display-off/display-on should normally be done from
0473      * some sort of display state notifier. As that is not available,
0474      * signal it here.
0475      */
0476 
0477     ssam_irq_disarm_wakeup(c);
0478 
0479     status = ssam_ctrl_notif_d0_entry(c);
0480     if (status)
0481         ssam_err(c, "pm: D0-entry notification failed: %d\n", status);
0482 
0483     return 0;
0484 }
0485 
0486 static int ssam_serial_hub_pm_freeze(struct device *dev)
0487 {
0488     struct ssam_controller *c = dev_get_drvdata(dev);
0489     int status;
0490 
0491     /*
0492      * During hibernation image creation, we only have to ensure that the
0493      * EC doesn't send us any events. This is done via the display-off
0494      * and D0-exit notifications. Note that this sets up the wakeup IRQ
0495      * on the EC side, however, we have disabled it by default on our side
0496      * and won't enable it here.
0497      *
0498      * See ssam_serial_hub_poweroff() for more details on the hibernation
0499      * process.
0500      */
0501 
0502     status = ssam_ctrl_notif_d0_exit(c);
0503     if (status) {
0504         ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
0505         ssam_ctrl_notif_display_on(c);
0506         return status;
0507     }
0508 
0509     WARN_ON(ssam_controller_suspend(c));
0510     return 0;
0511 }
0512 
0513 static int ssam_serial_hub_pm_thaw(struct device *dev)
0514 {
0515     struct ssam_controller *c = dev_get_drvdata(dev);
0516     int status;
0517 
0518     WARN_ON(ssam_controller_resume(c));
0519 
0520     status = ssam_ctrl_notif_d0_entry(c);
0521     if (status)
0522         ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
0523 
0524     return status;
0525 }
0526 
0527 static int ssam_serial_hub_pm_poweroff(struct device *dev)
0528 {
0529     struct ssam_controller *c = dev_get_drvdata(dev);
0530     int status;
0531 
0532     /*
0533      * When entering hibernation and powering off the system, the EC, at
0534      * least on some models, may disable events. Without us taking care of
0535      * that, this leads to events not being enabled/restored when the
0536      * system resumes from hibernation, resulting SAM-HID subsystem devices
0537      * (i.e. keyboard, touchpad) not working, AC-plug/AC-unplug events being
0538      * gone, etc.
0539      *
0540      * To avoid these issues, we disable all registered events here (this is
0541      * likely not actually required) and restore them during the drivers PM
0542      * restore callback.
0543      *
0544      * Wakeup from the EC interrupt is not supported during hibernation,
0545      * so don't arm the IRQ here.
0546      */
0547 
0548     status = ssam_notifier_disable_registered(c);
0549     if (status) {
0550         ssam_err(c, "pm: failed to disable notifiers for hibernation: %d\n",
0551              status);
0552         return status;
0553     }
0554 
0555     status = ssam_ctrl_notif_d0_exit(c);
0556     if (status) {
0557         ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
0558         ssam_notifier_restore_registered(c);
0559         return status;
0560     }
0561 
0562     WARN_ON(ssam_controller_suspend(c));
0563     return 0;
0564 }
0565 
0566 static int ssam_serial_hub_pm_restore(struct device *dev)
0567 {
0568     struct ssam_controller *c = dev_get_drvdata(dev);
0569     int status;
0570 
0571     /*
0572      * Ignore but log errors, try to restore state as much as possible in
0573      * case of failures. See ssam_serial_hub_poweroff() for more details on
0574      * the hibernation process.
0575      */
0576 
0577     WARN_ON(ssam_controller_resume(c));
0578 
0579     status = ssam_ctrl_notif_d0_entry(c);
0580     if (status)
0581         ssam_err(c, "pm: D0-entry notification failed: %d\n", status);
0582 
0583     ssam_notifier_restore_registered(c);
0584     return 0;
0585 }
0586 
0587 static const struct dev_pm_ops ssam_serial_hub_pm_ops = {
0588     .prepare  = ssam_serial_hub_pm_prepare,
0589     .complete = ssam_serial_hub_pm_complete,
0590     .suspend  = ssam_serial_hub_pm_suspend,
0591     .resume   = ssam_serial_hub_pm_resume,
0592     .freeze   = ssam_serial_hub_pm_freeze,
0593     .thaw     = ssam_serial_hub_pm_thaw,
0594     .poweroff = ssam_serial_hub_pm_poweroff,
0595     .restore  = ssam_serial_hub_pm_restore,
0596 };
0597 
0598 #else /* CONFIG_PM_SLEEP */
0599 
0600 static const struct dev_pm_ops ssam_serial_hub_pm_ops = { };
0601 
0602 #endif /* CONFIG_PM_SLEEP */
0603 
0604 
0605 /* -- Device/driver setup. -------------------------------------------------- */
0606 
0607 static const struct acpi_gpio_params gpio_ssam_wakeup_int = { 0, 0, false };
0608 static const struct acpi_gpio_params gpio_ssam_wakeup     = { 1, 0, false };
0609 
0610 static const struct acpi_gpio_mapping ssam_acpi_gpios[] = {
0611     { "ssam_wakeup-int-gpio", &gpio_ssam_wakeup_int, 1 },
0612     { "ssam_wakeup-gpio",     &gpio_ssam_wakeup,     1 },
0613     { },
0614 };
0615 
0616 static int ssam_serial_hub_probe(struct serdev_device *serdev)
0617 {
0618     struct acpi_device *ssh = ACPI_COMPANION(&serdev->dev);
0619     struct ssam_controller *ctrl;
0620     acpi_status astatus;
0621     int status;
0622 
0623     if (gpiod_count(&serdev->dev, NULL) < 0)
0624         return -ENODEV;
0625 
0626     status = devm_acpi_dev_add_driver_gpios(&serdev->dev, ssam_acpi_gpios);
0627     if (status)
0628         return status;
0629 
0630     /* Allocate controller. */
0631     ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
0632     if (!ctrl)
0633         return -ENOMEM;
0634 
0635     /* Initialize controller. */
0636     status = ssam_controller_init(ctrl, serdev);
0637     if (status)
0638         goto err_ctrl_init;
0639 
0640     ssam_controller_lock(ctrl);
0641 
0642     /* Set up serdev device. */
0643     serdev_device_set_drvdata(serdev, ctrl);
0644     serdev_device_set_client_ops(serdev, &ssam_serdev_ops);
0645     status = serdev_device_open(serdev);
0646     if (status)
0647         goto err_devopen;
0648 
0649     astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev);
0650     if (ACPI_FAILURE(astatus)) {
0651         status = -ENXIO;
0652         goto err_devinit;
0653     }
0654 
0655     /* Start controller. */
0656     status = ssam_controller_start(ctrl);
0657     if (status)
0658         goto err_devinit;
0659 
0660     ssam_controller_unlock(ctrl);
0661 
0662     /*
0663      * Initial SAM requests: Log version and notify default/init power
0664      * states.
0665      */
0666     status = ssam_log_firmware_version(ctrl);
0667     if (status)
0668         goto err_initrq;
0669 
0670     status = ssam_ctrl_notif_d0_entry(ctrl);
0671     if (status)
0672         goto err_initrq;
0673 
0674     status = ssam_ctrl_notif_display_on(ctrl);
0675     if (status)
0676         goto err_initrq;
0677 
0678     status = sysfs_create_group(&serdev->dev.kobj, &ssam_sam_group);
0679     if (status)
0680         goto err_initrq;
0681 
0682     /* Set up IRQ. */
0683     status = ssam_irq_setup(ctrl);
0684     if (status)
0685         goto err_irq;
0686 
0687     /* Finally, set main controller reference. */
0688     status = ssam_try_set_controller(ctrl);
0689     if (WARN_ON(status))    /* Currently, we're the only provider. */
0690         goto err_mainref;
0691 
0692     /*
0693      * TODO: The EC can wake up the system via the associated GPIO interrupt
0694      *       in multiple situations. One of which is the remaining battery
0695      *       capacity falling below a certain threshold. Normally, we should
0696      *       use the device_init_wakeup function, however, the EC also seems
0697      *       to have other reasons for waking up the system and it seems
0698      *       that Windows has additional checks whether the system should be
0699      *       resumed. In short, this causes some spurious unwanted wake-ups.
0700      *       For now let's thus default power/wakeup to false.
0701      */
0702     device_set_wakeup_capable(&serdev->dev, true);
0703     acpi_dev_clear_dependencies(ssh);
0704 
0705     return 0;
0706 
0707 err_mainref:
0708     ssam_irq_free(ctrl);
0709 err_irq:
0710     sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
0711 err_initrq:
0712     ssam_controller_lock(ctrl);
0713     ssam_controller_shutdown(ctrl);
0714 err_devinit:
0715     serdev_device_close(serdev);
0716 err_devopen:
0717     ssam_controller_destroy(ctrl);
0718     ssam_controller_unlock(ctrl);
0719 err_ctrl_init:
0720     kfree(ctrl);
0721     return status;
0722 }
0723 
0724 static void ssam_serial_hub_remove(struct serdev_device *serdev)
0725 {
0726     struct ssam_controller *ctrl = serdev_device_get_drvdata(serdev);
0727     int status;
0728 
0729     /* Clear static reference so that no one else can get a new one. */
0730     ssam_clear_controller();
0731 
0732     /* Disable and free IRQ. */
0733     ssam_irq_free(ctrl);
0734 
0735     sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
0736     ssam_controller_lock(ctrl);
0737 
0738     /* Remove all client devices. */
0739     ssam_remove_clients(&serdev->dev);
0740 
0741     /* Act as if suspending to silence events. */
0742     status = ssam_ctrl_notif_display_off(ctrl);
0743     if (status) {
0744         dev_err(&serdev->dev, "display-off notification failed: %d\n",
0745             status);
0746     }
0747 
0748     status = ssam_ctrl_notif_d0_exit(ctrl);
0749     if (status) {
0750         dev_err(&serdev->dev, "D0-exit notification failed: %d\n",
0751             status);
0752     }
0753 
0754     /* Shut down controller and remove serdev device reference from it. */
0755     ssam_controller_shutdown(ctrl);
0756 
0757     /* Shut down actual transport. */
0758     serdev_device_wait_until_sent(serdev, 0);
0759     serdev_device_close(serdev);
0760 
0761     /* Drop our controller reference. */
0762     ssam_controller_unlock(ctrl);
0763     ssam_controller_put(ctrl);
0764 
0765     device_set_wakeup_capable(&serdev->dev, false);
0766 }
0767 
0768 static const struct acpi_device_id ssam_serial_hub_match[] = {
0769     { "MSHW0084", 0 },
0770     { },
0771 };
0772 MODULE_DEVICE_TABLE(acpi, ssam_serial_hub_match);
0773 
0774 static struct serdev_device_driver ssam_serial_hub = {
0775     .probe = ssam_serial_hub_probe,
0776     .remove = ssam_serial_hub_remove,
0777     .driver = {
0778         .name = "surface_serial_hub",
0779         .acpi_match_table = ssam_serial_hub_match,
0780         .pm = &ssam_serial_hub_pm_ops,
0781         .shutdown = ssam_serial_hub_shutdown,
0782         .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0783     },
0784 };
0785 
0786 
0787 /* -- Module setup. --------------------------------------------------------- */
0788 
0789 static int __init ssam_core_init(void)
0790 {
0791     int status;
0792 
0793     status = ssam_bus_register();
0794     if (status)
0795         goto err_bus;
0796 
0797     status = ssh_ctrl_packet_cache_init();
0798     if (status)
0799         goto err_cpkg;
0800 
0801     status = ssam_event_item_cache_init();
0802     if (status)
0803         goto err_evitem;
0804 
0805     status = serdev_device_driver_register(&ssam_serial_hub);
0806     if (status)
0807         goto err_register;
0808 
0809     return 0;
0810 
0811 err_register:
0812     ssam_event_item_cache_destroy();
0813 err_evitem:
0814     ssh_ctrl_packet_cache_destroy();
0815 err_cpkg:
0816     ssam_bus_unregister();
0817 err_bus:
0818     return status;
0819 }
0820 subsys_initcall(ssam_core_init);
0821 
0822 static void __exit ssam_core_exit(void)
0823 {
0824     serdev_device_driver_unregister(&ssam_serial_hub);
0825     ssam_event_item_cache_destroy();
0826     ssh_ctrl_packet_cache_destroy();
0827     ssam_bus_unregister();
0828 }
0829 module_exit(ssam_core_exit);
0830 
0831 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
0832 MODULE_DESCRIPTION("Subsystem and Surface Serial Hub driver for Surface System Aggregator Module");
0833 MODULE_LICENSE("GPL");