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#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
35
36
37
38
39
40static struct ssam_controller *__ssam_controller;
41static DEFINE_SPINLOCK(__ssam_controller_lock);
42
43
44
45
46
47
48
49
50
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
73
74
75
76
77
78
79
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
97
98
99
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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
154
155
156
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
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
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
216
217
218
219
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
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
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
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
313 serdev_device_set_baudrate(serdev, uart->default_baud_rate);
314
315
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
322 flow_control = uart->flow_control & ACPI_UART_FLOW_CONTROL_HW;
323 serdev_device_set_flow_control(serdev, flow_control);
324
325
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
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
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
369
370
371
372
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
399
400
401
402
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
419
420
421
422
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
437
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
469
470
471
472
473
474
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
493
494
495
496
497
498
499
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
534
535
536
537
538
539
540
541
542
543
544
545
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
573
574
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
599
600static const struct dev_pm_ops ssam_serial_hub_pm_ops = { };
601
602#endif
603
604
605
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
631 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
632 if (!ctrl)
633 return -ENOMEM;
634
635
636 status = ssam_controller_init(ctrl, serdev);
637 if (status)
638 goto err_ctrl_init;
639
640 ssam_controller_lock(ctrl);
641
642
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
656 status = ssam_controller_start(ctrl);
657 if (status)
658 goto err_devinit;
659
660 ssam_controller_unlock(ctrl);
661
662
663
664
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
683 status = ssam_irq_setup(ctrl);
684 if (status)
685 goto err_irq;
686
687
688 status = ssam_try_set_controller(ctrl);
689 if (WARN_ON(status))
690 goto err_mainref;
691
692
693
694
695
696
697
698
699
700
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
730 ssam_clear_controller();
731
732
733 ssam_irq_free(ctrl);
734
735 sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
736 ssam_controller_lock(ctrl);
737
738
739 ssam_remove_clients(&serdev->dev);
740
741
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
755 ssam_controller_shutdown(ctrl);
756
757
758 serdev_device_wait_until_sent(serdev, 0);
759 serdev_device_close(serdev);
760
761
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
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");
834