0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
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
0035
0036
0037
0038
0039
0040 static struct ssam_controller *__ssam_controller;
0041 static DEFINE_SPINLOCK(__ssam_controller_lock);
0042
0043
0044
0045
0046
0047
0048
0049
0050
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
0073
0074
0075
0076
0077
0078
0079
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
0097
0098
0099
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
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
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
0154
0155
0156
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
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
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
0216
0217
0218
0219
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
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
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
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
0313 serdev_device_set_baudrate(serdev, uart->default_baud_rate);
0314
0315
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
0322 flow_control = uart->flow_control & ACPI_UART_FLOW_CONTROL_HW;
0323 serdev_device_set_flow_control(serdev, flow_control);
0324
0325
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
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
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
0369
0370
0371
0372
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
0399
0400
0401
0402
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
0419
0420
0421
0422
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
0437
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
0469
0470
0471
0472
0473
0474
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
0493
0494
0495
0496
0497
0498
0499
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
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
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
0573
0574
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
0599
0600 static const struct dev_pm_ops ssam_serial_hub_pm_ops = { };
0601
0602 #endif
0603
0604
0605
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
0631 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
0632 if (!ctrl)
0633 return -ENOMEM;
0634
0635
0636 status = ssam_controller_init(ctrl, serdev);
0637 if (status)
0638 goto err_ctrl_init;
0639
0640 ssam_controller_lock(ctrl);
0641
0642
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
0656 status = ssam_controller_start(ctrl);
0657 if (status)
0658 goto err_devinit;
0659
0660 ssam_controller_unlock(ctrl);
0661
0662
0663
0664
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
0683 status = ssam_irq_setup(ctrl);
0684 if (status)
0685 goto err_irq;
0686
0687
0688 status = ssam_try_set_controller(ctrl);
0689 if (WARN_ON(status))
0690 goto err_mainref;
0691
0692
0693
0694
0695
0696
0697
0698
0699
0700
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
0730 ssam_clear_controller();
0731
0732
0733 ssam_irq_free(ctrl);
0734
0735 sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
0736 ssam_controller_lock(ctrl);
0737
0738
0739 ssam_remove_clients(&serdev->dev);
0740
0741
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
0755 ssam_controller_shutdown(ctrl);
0756
0757
0758 serdev_device_wait_until_sent(serdev, 0);
0759 serdev_device_close(serdev);
0760
0761
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
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");