1
2
3
4
5
6
7
8
9
10
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
34
35
36
37
38
39static struct ssam_controller *__ssam_controller;
40static DEFINE_SPINLOCK(__ssam_controller_lock);
41
42
43
44
45
46
47
48
49
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
72
73
74
75
76
77
78
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
96
97
98
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
153
154
155
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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
215
216
217
218
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
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
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
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
312 serdev_device_set_baudrate(serdev, uart->default_baud_rate);
313
314
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
321 flow_control = uart->flow_control & ACPI_UART_FLOW_CONTROL_HW;
322 serdev_device_set_flow_control(serdev, flow_control);
323
324
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
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
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
368
369
370
371
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
398
399
400
401
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
418
419
420
421
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
436
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
468
469
470
471
472
473
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
492
493
494
495
496
497
498
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
533
534
535
536
537
538
539
540
541
542
543
544
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
572
573
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
598
599static const struct dev_pm_ops ssam_serial_hub_pm_ops = { };
600
601#endif
602
603
604
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
630 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
631 if (!ctrl)
632 return -ENOMEM;
633
634
635 status = ssam_controller_init(ctrl, serdev);
636 if (status)
637 goto err_ctrl_init;
638
639 ssam_controller_lock(ctrl);
640
641
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
655 status = ssam_controller_start(ctrl);
656 if (status)
657 goto err_devinit;
658
659 ssam_controller_unlock(ctrl);
660
661
662
663
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
682 status = ssam_irq_setup(ctrl);
683 if (status)
684 goto err_irq;
685
686
687 status = ssam_try_set_controller(ctrl);
688 if (WARN_ON(status))
689 goto err_mainref;
690
691
692
693
694
695
696
697
698
699
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
729 ssam_clear_controller();
730
731
732 ssam_irq_free(ctrl);
733
734 sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
735 ssam_controller_lock(ctrl);
736
737
738 ssam_controller_remove_clients(ctrl);
739
740
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
754 ssam_controller_shutdown(ctrl);
755
756
757 serdev_device_wait_until_sent(serdev, 0);
758 serdev_device_close(serdev);
759
760
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
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");
833