cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");