Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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_common_serialbus *serial;
305	struct acpi_resource_uart_serialbus *uart;
306	bool flow_control;
307	int status = 0;
308
309	if (rsc->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
310		return AE_OK;
311
312	serial = &rsc->data.common_serial_bus;
313	if (serial->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
314		return AE_OK;
315
316	uart = &rsc->data.uart_serial_bus;
317
318	/* Set up serdev device. */
319	serdev_device_set_baudrate(serdev, uart->default_baud_rate);
320
321	/* serdev currently only supports RTSCTS flow control. */
322	if (uart->flow_control & (~((u8)ACPI_UART_FLOW_CONTROL_HW))) {
323		dev_warn(&serdev->dev, "setup: unsupported flow control (value: %#04x)\n",
324			 uart->flow_control);
325	}
326
327	/* Set RTSCTS flow control. */
328	flow_control = uart->flow_control & ACPI_UART_FLOW_CONTROL_HW;
329	serdev_device_set_flow_control(serdev, flow_control);
330
331	/* serdev currently only supports EVEN/ODD parity. */
332	switch (uart->parity) {
333	case ACPI_UART_PARITY_NONE:
334		status = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
335		break;
336	case ACPI_UART_PARITY_EVEN:
337		status = serdev_device_set_parity(serdev, SERDEV_PARITY_EVEN);
338		break;
339	case ACPI_UART_PARITY_ODD:
340		status = serdev_device_set_parity(serdev, SERDEV_PARITY_ODD);
341		break;
342	default:
343		dev_warn(&serdev->dev, "setup: unsupported parity (value: %#04x)\n",
344			 uart->parity);
345		break;
346	}
347
348	if (status) {
349		dev_err(&serdev->dev, "setup: failed to set parity (value: %#04x, error: %d)\n",
350			uart->parity, status);
351		return AE_ERROR;
352	}
353
354	/* We've found the resource and are done. */
355	return AE_CTRL_TERMINATE;
356}
357
358static acpi_status ssam_serdev_setup_via_acpi(acpi_handle handle,
359					      struct serdev_device *serdev)
360{
361	return acpi_walk_resources(handle, METHOD_NAME__CRS,
362				   ssam_serdev_setup_via_acpi_crs, serdev);
363}
364
365
366/* -- Power management. ----------------------------------------------------- */
367
368static void ssam_serial_hub_shutdown(struct device *dev)
369{
370	struct ssam_controller *c = dev_get_drvdata(dev);
371	int status;
372
373	/*
374	 * Try to disable notifiers, signal display-off and D0-exit, ignore any
375	 * errors.
376	 *
377	 * Note: It has not been established yet if this is actually
378	 * necessary/useful for shutdown.
379	 */
380
381	status = ssam_notifier_disable_registered(c);
382	if (status) {
383		ssam_err(c, "pm: failed to disable notifiers for shutdown: %d\n",
384			 status);
385	}
386
387	status = ssam_ctrl_notif_display_off(c);
388	if (status)
389		ssam_err(c, "pm: display-off notification failed: %d\n", status);
390
391	status = ssam_ctrl_notif_d0_exit(c);
392	if (status)
393		ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
394}
395
396#ifdef CONFIG_PM_SLEEP
397
398static int ssam_serial_hub_pm_prepare(struct device *dev)
399{
400	struct ssam_controller *c = dev_get_drvdata(dev);
401	int status;
402
403	/*
404	 * Try to signal display-off, This will quiesce events.
405	 *
406	 * Note: Signaling display-off/display-on should normally be done from
407	 * some sort of display state notifier. As that is not available,
408	 * signal it here.
409	 */
410
411	status = ssam_ctrl_notif_display_off(c);
412	if (status)
413		ssam_err(c, "pm: display-off notification failed: %d\n", status);
414
415	return status;
416}
417
418static void ssam_serial_hub_pm_complete(struct device *dev)
419{
420	struct ssam_controller *c = dev_get_drvdata(dev);
421	int status;
422
423	/*
424	 * Try to signal display-on. This will restore events.
425	 *
426	 * Note: Signaling display-off/display-on should normally be done from
427	 * some sort of display state notifier. As that is not available,
428	 * signal it here.
429	 */
430
431	status = ssam_ctrl_notif_display_on(c);
432	if (status)
433		ssam_err(c, "pm: display-on notification failed: %d\n", status);
434}
435
436static int ssam_serial_hub_pm_suspend(struct device *dev)
437{
438	struct ssam_controller *c = dev_get_drvdata(dev);
439	int status;
440
441	/*
442	 * Try to signal D0-exit, enable IRQ wakeup if specified. Abort on
443	 * error.
444	 */
445
446	status = ssam_ctrl_notif_d0_exit(c);
447	if (status) {
448		ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
449		goto err_notif;
450	}
451
452	status = ssam_irq_arm_for_wakeup(c);
453	if (status)
454		goto err_irq;
455
456	WARN_ON(ssam_controller_suspend(c));
457	return 0;
458
459err_irq:
460	ssam_ctrl_notif_d0_entry(c);
461err_notif:
462	ssam_ctrl_notif_display_on(c);
463	return status;
464}
465
466static int ssam_serial_hub_pm_resume(struct device *dev)
467{
468	struct ssam_controller *c = dev_get_drvdata(dev);
469	int status;
470
471	WARN_ON(ssam_controller_resume(c));
472
473	/*
474	 * Try to disable IRQ wakeup (if specified) and signal D0-entry. In
475	 * case of errors, log them and try to restore normal operation state
476	 * as far as possible.
477	 *
478	 * Note: Signaling display-off/display-on should normally be done from
479	 * some sort of display state notifier. As that is not available,
480	 * signal it here.
481	 */
482
483	ssam_irq_disarm_wakeup(c);
484
485	status = ssam_ctrl_notif_d0_entry(c);
486	if (status)
487		ssam_err(c, "pm: D0-entry notification failed: %d\n", status);
488
489	return 0;
490}
491
492static int ssam_serial_hub_pm_freeze(struct device *dev)
493{
494	struct ssam_controller *c = dev_get_drvdata(dev);
495	int status;
496
497	/*
498	 * During hibernation image creation, we only have to ensure that the
499	 * EC doesn't send us any events. This is done via the display-off
500	 * and D0-exit notifications. Note that this sets up the wakeup IRQ
501	 * on the EC side, however, we have disabled it by default on our side
502	 * and won't enable it here.
503	 *
504	 * See ssam_serial_hub_poweroff() for more details on the hibernation
505	 * process.
506	 */
507
508	status = ssam_ctrl_notif_d0_exit(c);
509	if (status) {
510		ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
511		ssam_ctrl_notif_display_on(c);
512		return status;
513	}
514
515	WARN_ON(ssam_controller_suspend(c));
516	return 0;
517}
518
519static int ssam_serial_hub_pm_thaw(struct device *dev)
520{
521	struct ssam_controller *c = dev_get_drvdata(dev);
522	int status;
523
524	WARN_ON(ssam_controller_resume(c));
525
526	status = ssam_ctrl_notif_d0_entry(c);
527	if (status)
528		ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
529
530	return status;
531}
532
533static int ssam_serial_hub_pm_poweroff(struct device *dev)
534{
535	struct ssam_controller *c = dev_get_drvdata(dev);
536	int status;
537
538	/*
539	 * When entering hibernation and powering off the system, the EC, at
540	 * least on some models, may disable events. Without us taking care of
541	 * that, this leads to events not being enabled/restored when the
542	 * system resumes from hibernation, resulting SAM-HID subsystem devices
543	 * (i.e. keyboard, touchpad) not working, AC-plug/AC-unplug events being
544	 * gone, etc.
545	 *
546	 * To avoid these issues, we disable all registered events here (this is
547	 * likely not actually required) and restore them during the drivers PM
548	 * restore callback.
549	 *
550	 * Wakeup from the EC interrupt is not supported during hibernation,
551	 * so don't arm the IRQ here.
552	 */
553
554	status = ssam_notifier_disable_registered(c);
555	if (status) {
556		ssam_err(c, "pm: failed to disable notifiers for hibernation: %d\n",
557			 status);
558		return status;
559	}
560
561	status = ssam_ctrl_notif_d0_exit(c);
562	if (status) {
563		ssam_err(c, "pm: D0-exit notification failed: %d\n", status);
564		ssam_notifier_restore_registered(c);
565		return status;
566	}
567
568	WARN_ON(ssam_controller_suspend(c));
569	return 0;
570}
571
572static int ssam_serial_hub_pm_restore(struct device *dev)
573{
574	struct ssam_controller *c = dev_get_drvdata(dev);
575	int status;
576
577	/*
578	 * Ignore but log errors, try to restore state as much as possible in
579	 * case of failures. See ssam_serial_hub_poweroff() for more details on
580	 * the hibernation process.
581	 */
582
583	WARN_ON(ssam_controller_resume(c));
584
585	status = ssam_ctrl_notif_d0_entry(c);
586	if (status)
587		ssam_err(c, "pm: D0-entry notification failed: %d\n", status);
588
589	ssam_notifier_restore_registered(c);
590	return 0;
591}
592
593static const struct dev_pm_ops ssam_serial_hub_pm_ops = {
594	.prepare  = ssam_serial_hub_pm_prepare,
595	.complete = ssam_serial_hub_pm_complete,
596	.suspend  = ssam_serial_hub_pm_suspend,
597	.resume   = ssam_serial_hub_pm_resume,
598	.freeze   = ssam_serial_hub_pm_freeze,
599	.thaw     = ssam_serial_hub_pm_thaw,
600	.poweroff = ssam_serial_hub_pm_poweroff,
601	.restore  = ssam_serial_hub_pm_restore,
602};
603
604#else /* CONFIG_PM_SLEEP */
605
606static const struct dev_pm_ops ssam_serial_hub_pm_ops = { };
607
608#endif /* CONFIG_PM_SLEEP */
609
610
611/* -- Device/driver setup. -------------------------------------------------- */
612
613static const struct acpi_gpio_params gpio_ssam_wakeup_int = { 0, 0, false };
614static const struct acpi_gpio_params gpio_ssam_wakeup     = { 1, 0, false };
615
616static const struct acpi_gpio_mapping ssam_acpi_gpios[] = {
617	{ "ssam_wakeup-int-gpio", &gpio_ssam_wakeup_int, 1 },
618	{ "ssam_wakeup-gpio",     &gpio_ssam_wakeup,     1 },
619	{ },
620};
621
622static int ssam_serial_hub_probe(struct serdev_device *serdev)
623{
624	struct acpi_device *ssh = ACPI_COMPANION(&serdev->dev);
625	struct ssam_controller *ctrl;
626	acpi_status astatus;
627	int status;
628
629	if (gpiod_count(&serdev->dev, NULL) < 0)
630		return -ENODEV;
631
632	status = devm_acpi_dev_add_driver_gpios(&serdev->dev, ssam_acpi_gpios);
633	if (status)
634		return status;
635
636	/* Allocate controller. */
637	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
638	if (!ctrl)
639		return -ENOMEM;
640
641	/* Initialize controller. */
642	status = ssam_controller_init(ctrl, serdev);
643	if (status)
644		goto err_ctrl_init;
645
646	ssam_controller_lock(ctrl);
647
648	/* Set up serdev device. */
649	serdev_device_set_drvdata(serdev, ctrl);
650	serdev_device_set_client_ops(serdev, &ssam_serdev_ops);
651	status = serdev_device_open(serdev);
652	if (status)
653		goto err_devopen;
654
655	astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev);
656	if (ACPI_FAILURE(astatus)) {
657		status = -ENXIO;
658		goto err_devinit;
659	}
660
661	/* Start controller. */
662	status = ssam_controller_start(ctrl);
663	if (status)
664		goto err_devinit;
665
666	ssam_controller_unlock(ctrl);
667
668	/*
669	 * Initial SAM requests: Log version and notify default/init power
670	 * states.
671	 */
672	status = ssam_log_firmware_version(ctrl);
673	if (status)
674		goto err_initrq;
675
676	status = ssam_ctrl_notif_d0_entry(ctrl);
677	if (status)
678		goto err_initrq;
679
680	status = ssam_ctrl_notif_display_on(ctrl);
681	if (status)
682		goto err_initrq;
683
684	status = sysfs_create_group(&serdev->dev.kobj, &ssam_sam_group);
685	if (status)
686		goto err_initrq;
687
688	/* Set up IRQ. */
689	status = ssam_irq_setup(ctrl);
690	if (status)
691		goto err_irq;
692
693	/* Finally, set main controller reference. */
694	status = ssam_try_set_controller(ctrl);
695	if (WARN_ON(status))	/* Currently, we're the only provider. */
696		goto err_mainref;
697
698	/*
699	 * TODO: The EC can wake up the system via the associated GPIO interrupt
700	 *       in multiple situations. One of which is the remaining battery
701	 *       capacity falling below a certain threshold. Normally, we should
702	 *       use the device_init_wakeup function, however, the EC also seems
703	 *       to have other reasons for waking up the system and it seems
704	 *       that Windows has additional checks whether the system should be
705	 *       resumed. In short, this causes some spurious unwanted wake-ups.
706	 *       For now let's thus default power/wakeup to false.
707	 */
708	device_set_wakeup_capable(&serdev->dev, true);
709	acpi_dev_clear_dependencies(ssh);
710
711	return 0;
712
713err_mainref:
714	ssam_irq_free(ctrl);
715err_irq:
716	sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
717err_initrq:
718	ssam_controller_lock(ctrl);
719	ssam_controller_shutdown(ctrl);
720err_devinit:
721	serdev_device_close(serdev);
722err_devopen:
723	ssam_controller_destroy(ctrl);
724	ssam_controller_unlock(ctrl);
725err_ctrl_init:
726	kfree(ctrl);
727	return status;
728}
729
730static void ssam_serial_hub_remove(struct serdev_device *serdev)
731{
732	struct ssam_controller *ctrl = serdev_device_get_drvdata(serdev);
733	int status;
734
735	/* Clear static reference so that no one else can get a new one. */
736	ssam_clear_controller();
737
738	/* Disable and free IRQ. */
739	ssam_irq_free(ctrl);
740
741	sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
742	ssam_controller_lock(ctrl);
743
744	/* Remove all client devices. */
745	ssam_controller_remove_clients(ctrl);
746
747	/* Act as if suspending to silence events. */
748	status = ssam_ctrl_notif_display_off(ctrl);
749	if (status) {
750		dev_err(&serdev->dev, "display-off notification failed: %d\n",
751			status);
752	}
753
754	status = ssam_ctrl_notif_d0_exit(ctrl);
755	if (status) {
756		dev_err(&serdev->dev, "D0-exit notification failed: %d\n",
757			status);
758	}
759
760	/* Shut down controller and remove serdev device reference from it. */
761	ssam_controller_shutdown(ctrl);
762
763	/* Shut down actual transport. */
764	serdev_device_wait_until_sent(serdev, 0);
765	serdev_device_close(serdev);
766
767	/* Drop our controller reference. */
768	ssam_controller_unlock(ctrl);
769	ssam_controller_put(ctrl);
770
771	device_set_wakeup_capable(&serdev->dev, false);
772}
773
774static const struct acpi_device_id ssam_serial_hub_match[] = {
775	{ "MSHW0084", 0 },
776	{ },
777};
778MODULE_DEVICE_TABLE(acpi, ssam_serial_hub_match);
779
780static struct serdev_device_driver ssam_serial_hub = {
781	.probe = ssam_serial_hub_probe,
782	.remove = ssam_serial_hub_remove,
783	.driver = {
784		.name = "surface_serial_hub",
785		.acpi_match_table = ssam_serial_hub_match,
786		.pm = &ssam_serial_hub_pm_ops,
787		.shutdown = ssam_serial_hub_shutdown,
788		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
789	},
790};
791
792
793/* -- Module setup. --------------------------------------------------------- */
794
795static int __init ssam_core_init(void)
796{
797	int status;
798
799	status = ssam_bus_register();
800	if (status)
801		goto err_bus;
802
803	status = ssh_ctrl_packet_cache_init();
804	if (status)
805		goto err_cpkg;
806
807	status = ssam_event_item_cache_init();
808	if (status)
809		goto err_evitem;
810
811	status = serdev_device_driver_register(&ssam_serial_hub);
812	if (status)
813		goto err_register;
814
815	return 0;
816
817err_register:
818	ssam_event_item_cache_destroy();
819err_evitem:
820	ssh_ctrl_packet_cache_destroy();
821err_cpkg:
822	ssam_bus_unregister();
823err_bus:
824	return status;
825}
826module_init(ssam_core_init);
827
828static void __exit ssam_core_exit(void)
829{
830	serdev_device_driver_unregister(&ssam_serial_hub);
831	ssam_event_item_cache_destroy();
832	ssh_ctrl_packet_cache_destroy();
833	ssam_bus_unregister();
834}
835module_exit(ssam_core_exit);
836
837MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
838MODULE_DESCRIPTION("Subsystem and Surface Serial Hub driver for Surface System Aggregator Module");
839MODULE_LICENSE("GPL");