0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/acpi.h>
0009 #include <linux/atomic.h>
0010 #include <linux/completion.h>
0011 #include <linux/gpio/consumer.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/kref.h>
0014 #include <linux/limits.h>
0015 #include <linux/list.h>
0016 #include <linux/lockdep.h>
0017 #include <linux/mutex.h>
0018 #include <linux/rculist.h>
0019 #include <linux/rbtree.h>
0020 #include <linux/rwsem.h>
0021 #include <linux/serdev.h>
0022 #include <linux/slab.h>
0023 #include <linux/spinlock.h>
0024 #include <linux/srcu.h>
0025 #include <linux/types.h>
0026 #include <linux/workqueue.h>
0027
0028 #include <linux/surface_aggregator/controller.h>
0029 #include <linux/surface_aggregator/serial_hub.h>
0030
0031 #include "controller.h"
0032 #include "ssh_msgb.h"
0033 #include "ssh_request_layer.h"
0034
0035 #include "trace.h"
0036
0037
0038
0039
0040
0041
0042
0043
0044 static void ssh_seq_reset(struct ssh_seq_counter *c)
0045 {
0046 WRITE_ONCE(c->value, 0);
0047 }
0048
0049
0050
0051
0052
0053
0054
0055 static u8 ssh_seq_next(struct ssh_seq_counter *c)
0056 {
0057 u8 old = READ_ONCE(c->value);
0058 u8 new = old + 1;
0059 u8 ret;
0060
0061 while (unlikely((ret = cmpxchg(&c->value, old, new)) != old)) {
0062 old = ret;
0063 new = old + 1;
0064 }
0065
0066 return old;
0067 }
0068
0069
0070
0071
0072
0073 static void ssh_rqid_reset(struct ssh_rqid_counter *c)
0074 {
0075 WRITE_ONCE(c->value, 0);
0076 }
0077
0078
0079
0080
0081
0082
0083
0084
0085 static u16 ssh_rqid_next(struct ssh_rqid_counter *c)
0086 {
0087 u16 old = READ_ONCE(c->value);
0088 u16 new = ssh_rqid_next_valid(old);
0089 u16 ret;
0090
0091 while (unlikely((ret = cmpxchg(&c->value, old, new)) != old)) {
0092 old = ret;
0093 new = ssh_rqid_next_valid(old);
0094 }
0095
0096 return old;
0097 }
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118 static bool ssam_event_matches_notifier(const struct ssam_event_notifier *n,
0119 const struct ssam_event *event)
0120 {
0121 bool match = n->event.id.target_category == event->target_category;
0122
0123 if (n->event.mask & SSAM_EVENT_MASK_TARGET)
0124 match &= n->event.reg.target_id == event->target_id;
0125
0126 if (n->event.mask & SSAM_EVENT_MASK_INSTANCE)
0127 match &= n->event.id.instance == event->instance_id;
0128
0129 return match;
0130 }
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148 static int ssam_nfblk_call_chain(struct ssam_nf_head *nh, struct ssam_event *event)
0149 {
0150 struct ssam_event_notifier *nf;
0151 int ret = 0, idx;
0152
0153 idx = srcu_read_lock(&nh->srcu);
0154
0155 list_for_each_entry_rcu(nf, &nh->head, base.node,
0156 srcu_read_lock_held(&nh->srcu)) {
0157 if (ssam_event_matches_notifier(nf, event)) {
0158 ret = (ret & SSAM_NOTIF_STATE_MASK) | nf->base.fn(nf, event);
0159 if (ret & SSAM_NOTIF_STOP)
0160 break;
0161 }
0162 }
0163
0164 srcu_read_unlock(&nh->srcu, idx);
0165 return ret;
0166 }
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180 static int ssam_nfblk_insert(struct ssam_nf_head *nh, struct ssam_notifier_block *nb)
0181 {
0182 struct ssam_notifier_block *p;
0183 struct list_head *h;
0184
0185
0186 list_for_each(h, &nh->head) {
0187 p = list_entry(h, struct ssam_notifier_block, node);
0188
0189 if (unlikely(p == nb)) {
0190 WARN(1, "double register detected");
0191 return -EEXIST;
0192 }
0193
0194 if (nb->priority > p->priority)
0195 break;
0196 }
0197
0198 list_add_tail_rcu(&nb->node, h);
0199 return 0;
0200 }
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215 static bool ssam_nfblk_find(struct ssam_nf_head *nh, struct ssam_notifier_block *nb)
0216 {
0217 struct ssam_notifier_block *p;
0218
0219
0220 list_for_each_entry(p, &nh->head, node) {
0221 if (p == nb)
0222 return true;
0223 }
0224
0225 return false;
0226 }
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238 static void ssam_nfblk_remove(struct ssam_notifier_block *nb)
0239 {
0240 list_del_rcu(&nb->node);
0241 }
0242
0243
0244
0245
0246
0247 static int ssam_nf_head_init(struct ssam_nf_head *nh)
0248 {
0249 int status;
0250
0251 status = init_srcu_struct(&nh->srcu);
0252 if (status)
0253 return status;
0254
0255 INIT_LIST_HEAD(&nh->head);
0256 return 0;
0257 }
0258
0259
0260
0261
0262
0263 static void ssam_nf_head_destroy(struct ssam_nf_head *nh)
0264 {
0265 cleanup_srcu_struct(&nh->srcu);
0266 }
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277 struct ssam_nf_refcount_key {
0278 struct ssam_event_registry reg;
0279 struct ssam_event_id id;
0280 };
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290 struct ssam_nf_refcount_entry {
0291 struct rb_node node;
0292 struct ssam_nf_refcount_key key;
0293 int refcount;
0294 u8 flags;
0295 };
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314 static struct ssam_nf_refcount_entry *
0315 ssam_nf_refcount_inc(struct ssam_nf *nf, struct ssam_event_registry reg,
0316 struct ssam_event_id id)
0317 {
0318 struct ssam_nf_refcount_entry *entry;
0319 struct ssam_nf_refcount_key key;
0320 struct rb_node **link = &nf->refcount.rb_node;
0321 struct rb_node *parent = NULL;
0322 int cmp;
0323
0324 lockdep_assert_held(&nf->lock);
0325
0326 key.reg = reg;
0327 key.id = id;
0328
0329 while (*link) {
0330 entry = rb_entry(*link, struct ssam_nf_refcount_entry, node);
0331 parent = *link;
0332
0333 cmp = memcmp(&key, &entry->key, sizeof(key));
0334 if (cmp < 0) {
0335 link = &(*link)->rb_left;
0336 } else if (cmp > 0) {
0337 link = &(*link)->rb_right;
0338 } else if (entry->refcount < INT_MAX) {
0339 entry->refcount++;
0340 return entry;
0341 } else {
0342 WARN_ON(1);
0343 return ERR_PTR(-ENOSPC);
0344 }
0345 }
0346
0347 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
0348 if (!entry)
0349 return ERR_PTR(-ENOMEM);
0350
0351 entry->key = key;
0352 entry->refcount = 1;
0353
0354 rb_link_node(&entry->node, parent, link);
0355 rb_insert_color(&entry->node, &nf->refcount);
0356
0357 return entry;
0358 }
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376 static struct ssam_nf_refcount_entry *
0377 ssam_nf_refcount_dec(struct ssam_nf *nf, struct ssam_event_registry reg,
0378 struct ssam_event_id id)
0379 {
0380 struct ssam_nf_refcount_entry *entry;
0381 struct ssam_nf_refcount_key key;
0382 struct rb_node *node = nf->refcount.rb_node;
0383 int cmp;
0384
0385 lockdep_assert_held(&nf->lock);
0386
0387 key.reg = reg;
0388 key.id = id;
0389
0390 while (node) {
0391 entry = rb_entry(node, struct ssam_nf_refcount_entry, node);
0392
0393 cmp = memcmp(&key, &entry->key, sizeof(key));
0394 if (cmp < 0) {
0395 node = node->rb_left;
0396 } else if (cmp > 0) {
0397 node = node->rb_right;
0398 } else {
0399 entry->refcount--;
0400 if (entry->refcount == 0)
0401 rb_erase(&entry->node, &nf->refcount);
0402
0403 return entry;
0404 }
0405 }
0406
0407 return NULL;
0408 }
0409
0410
0411
0412
0413
0414
0415
0416
0417
0418
0419
0420
0421
0422 static void ssam_nf_refcount_dec_free(struct ssam_nf *nf,
0423 struct ssam_event_registry reg,
0424 struct ssam_event_id id)
0425 {
0426 struct ssam_nf_refcount_entry *entry;
0427
0428 lockdep_assert_held(&nf->lock);
0429
0430 entry = ssam_nf_refcount_dec(nf, reg, id);
0431 if (entry && entry->refcount == 0)
0432 kfree(entry);
0433 }
0434
0435
0436
0437
0438
0439
0440 static bool ssam_nf_refcount_empty(struct ssam_nf *nf)
0441 {
0442 return RB_EMPTY_ROOT(&nf->refcount);
0443 }
0444
0445
0446
0447
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462
0463
0464 static void ssam_nf_call(struct ssam_nf *nf, struct device *dev, u16 rqid,
0465 struct ssam_event *event)
0466 {
0467 struct ssam_nf_head *nf_head;
0468 int status, nf_ret;
0469
0470 if (!ssh_rqid_is_event(rqid)) {
0471 dev_warn(dev, "event: unsupported rqid: %#06x\n", rqid);
0472 return;
0473 }
0474
0475 nf_head = &nf->head[ssh_rqid_to_event(rqid)];
0476 nf_ret = ssam_nfblk_call_chain(nf_head, event);
0477 status = ssam_notifier_to_errno(nf_ret);
0478
0479 if (status < 0) {
0480 dev_err(dev,
0481 "event: error handling event: %d (tc: %#04x, tid: %#04x, cid: %#04x, iid: %#04x)\n",
0482 status, event->target_category, event->target_id,
0483 event->command_id, event->instance_id);
0484 } else if (!(nf_ret & SSAM_NOTIF_HANDLED)) {
0485 dev_warn(dev,
0486 "event: unhandled event (rqid: %#04x, tc: %#04x, tid: %#04x, cid: %#04x, iid: %#04x)\n",
0487 rqid, event->target_category, event->target_id,
0488 event->command_id, event->instance_id);
0489 }
0490 }
0491
0492
0493
0494
0495
0496 static int ssam_nf_init(struct ssam_nf *nf)
0497 {
0498 int i, status;
0499
0500 for (i = 0; i < SSH_NUM_EVENTS; i++) {
0501 status = ssam_nf_head_init(&nf->head[i]);
0502 if (status)
0503 break;
0504 }
0505
0506 if (status) {
0507 while (i--)
0508 ssam_nf_head_destroy(&nf->head[i]);
0509
0510 return status;
0511 }
0512
0513 mutex_init(&nf->lock);
0514 return 0;
0515 }
0516
0517
0518
0519
0520
0521 static void ssam_nf_destroy(struct ssam_nf *nf)
0522 {
0523 int i;
0524
0525 for (i = 0; i < SSH_NUM_EVENTS; i++)
0526 ssam_nf_head_destroy(&nf->head[i]);
0527
0528 mutex_destroy(&nf->lock);
0529 }
0530
0531
0532
0533
0534 #define SSAM_CPLT_WQ_NAME "ssam_cpltq"
0535
0536
0537
0538
0539
0540
0541 #define SSAM_CPLT_WQ_BATCH 10
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551 #define SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN 32
0552
0553 static struct kmem_cache *ssam_event_item_cache;
0554
0555
0556
0557
0558 int ssam_event_item_cache_init(void)
0559 {
0560 const unsigned int size = sizeof(struct ssam_event_item)
0561 + SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN;
0562 const unsigned int align = __alignof__(struct ssam_event_item);
0563 struct kmem_cache *cache;
0564
0565 cache = kmem_cache_create("ssam_event_item", size, align, 0, NULL);
0566 if (!cache)
0567 return -ENOMEM;
0568
0569 ssam_event_item_cache = cache;
0570 return 0;
0571 }
0572
0573
0574
0575
0576 void ssam_event_item_cache_destroy(void)
0577 {
0578 kmem_cache_destroy(ssam_event_item_cache);
0579 ssam_event_item_cache = NULL;
0580 }
0581
0582 static void __ssam_event_item_free_cached(struct ssam_event_item *item)
0583 {
0584 kmem_cache_free(ssam_event_item_cache, item);
0585 }
0586
0587 static void __ssam_event_item_free_generic(struct ssam_event_item *item)
0588 {
0589 kfree(item);
0590 }
0591
0592
0593
0594
0595
0596 static void ssam_event_item_free(struct ssam_event_item *item)
0597 {
0598 trace_ssam_event_item_free(item);
0599 item->ops.free(item);
0600 }
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615 static struct ssam_event_item *ssam_event_item_alloc(size_t len, gfp_t flags)
0616 {
0617 struct ssam_event_item *item;
0618
0619 if (len <= SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN) {
0620 item = kmem_cache_alloc(ssam_event_item_cache, flags);
0621 if (!item)
0622 return NULL;
0623
0624 item->ops.free = __ssam_event_item_free_cached;
0625 } else {
0626 item = kzalloc(struct_size(item, event.data, len), flags);
0627 if (!item)
0628 return NULL;
0629
0630 item->ops.free = __ssam_event_item_free_generic;
0631 }
0632
0633 item->event.length = len;
0634
0635 trace_ssam_event_item_alloc(item, len);
0636 return item;
0637 }
0638
0639
0640
0641
0642
0643
0644 static void ssam_event_queue_push(struct ssam_event_queue *q,
0645 struct ssam_event_item *item)
0646 {
0647 spin_lock(&q->lock);
0648 list_add_tail(&item->node, &q->head);
0649 spin_unlock(&q->lock);
0650 }
0651
0652
0653
0654
0655
0656
0657
0658
0659 static struct ssam_event_item *ssam_event_queue_pop(struct ssam_event_queue *q)
0660 {
0661 struct ssam_event_item *item;
0662
0663 spin_lock(&q->lock);
0664 item = list_first_entry_or_null(&q->head, struct ssam_event_item, node);
0665 if (item)
0666 list_del(&item->node);
0667 spin_unlock(&q->lock);
0668
0669 return item;
0670 }
0671
0672
0673
0674
0675
0676 static bool ssam_event_queue_is_empty(struct ssam_event_queue *q)
0677 {
0678 bool empty;
0679
0680 spin_lock(&q->lock);
0681 empty = list_empty(&q->head);
0682 spin_unlock(&q->lock);
0683
0684 return empty;
0685 }
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695
0696
0697
0698 static
0699 struct ssam_event_queue *ssam_cplt_get_event_queue(struct ssam_cplt *cplt,
0700 u8 tid, u16 rqid)
0701 {
0702 u16 event = ssh_rqid_to_event(rqid);
0703 u16 tidx = ssh_tid_to_index(tid);
0704
0705 if (!ssh_rqid_is_event(rqid)) {
0706 dev_err(cplt->dev, "event: unsupported request ID: %#06x\n", rqid);
0707 return NULL;
0708 }
0709
0710 if (!ssh_tid_is_valid(tid)) {
0711 dev_warn(cplt->dev, "event: unsupported target ID: %u\n", tid);
0712 tidx = 0;
0713 }
0714
0715 return &cplt->event.target[tidx].queue[event];
0716 }
0717
0718
0719
0720
0721
0722
0723 static bool ssam_cplt_submit(struct ssam_cplt *cplt, struct work_struct *work)
0724 {
0725 return queue_work(cplt->wq, work);
0726 }
0727
0728
0729
0730
0731
0732
0733
0734
0735
0736
0737
0738
0739
0740 static int ssam_cplt_submit_event(struct ssam_cplt *cplt,
0741 struct ssam_event_item *item)
0742 {
0743 struct ssam_event_queue *evq;
0744
0745 evq = ssam_cplt_get_event_queue(cplt, item->event.target_id, item->rqid);
0746 if (!evq)
0747 return -EINVAL;
0748
0749 ssam_event_queue_push(evq, item);
0750 ssam_cplt_submit(cplt, &evq->work);
0751 return 0;
0752 }
0753
0754
0755
0756
0757
0758
0759
0760
0761
0762
0763
0764
0765
0766
0767
0768
0769
0770
0771 static void ssam_cplt_flush(struct ssam_cplt *cplt)
0772 {
0773 flush_workqueue(cplt->wq);
0774 }
0775
0776 static void ssam_event_queue_work_fn(struct work_struct *work)
0777 {
0778 struct ssam_event_queue *queue;
0779 struct ssam_event_item *item;
0780 struct ssam_nf *nf;
0781 struct device *dev;
0782 unsigned int iterations = SSAM_CPLT_WQ_BATCH;
0783
0784 queue = container_of(work, struct ssam_event_queue, work);
0785 nf = &queue->cplt->event.notif;
0786 dev = queue->cplt->dev;
0787
0788
0789 do {
0790 item = ssam_event_queue_pop(queue);
0791 if (!item)
0792 return;
0793
0794 ssam_nf_call(nf, dev, item->rqid, &item->event);
0795 ssam_event_item_free(item);
0796 } while (--iterations);
0797
0798 if (!ssam_event_queue_is_empty(queue))
0799 ssam_cplt_submit(queue->cplt, &queue->work);
0800 }
0801
0802
0803
0804
0805
0806
0807 static void ssam_event_queue_init(struct ssam_cplt *cplt,
0808 struct ssam_event_queue *evq)
0809 {
0810 evq->cplt = cplt;
0811 spin_lock_init(&evq->lock);
0812 INIT_LIST_HEAD(&evq->head);
0813 INIT_WORK(&evq->work, ssam_event_queue_work_fn);
0814 }
0815
0816
0817
0818
0819
0820
0821 static int ssam_cplt_init(struct ssam_cplt *cplt, struct device *dev)
0822 {
0823 struct ssam_event_target *target;
0824 int status, c, i;
0825
0826 cplt->dev = dev;
0827
0828 cplt->wq = create_workqueue(SSAM_CPLT_WQ_NAME);
0829 if (!cplt->wq)
0830 return -ENOMEM;
0831
0832 for (c = 0; c < ARRAY_SIZE(cplt->event.target); c++) {
0833 target = &cplt->event.target[c];
0834
0835 for (i = 0; i < ARRAY_SIZE(target->queue); i++)
0836 ssam_event_queue_init(cplt, &target->queue[i]);
0837 }
0838
0839 status = ssam_nf_init(&cplt->event.notif);
0840 if (status)
0841 destroy_workqueue(cplt->wq);
0842
0843 return status;
0844 }
0845
0846
0847
0848
0849
0850
0851
0852
0853 static void ssam_cplt_destroy(struct ssam_cplt *cplt)
0854 {
0855
0856
0857
0858
0859
0860
0861 destroy_workqueue(cplt->wq);
0862 ssam_nf_destroy(&cplt->event.notif);
0863 }
0864
0865
0866
0867
0868
0869
0870
0871
0872
0873
0874
0875
0876 struct device *ssam_controller_device(struct ssam_controller *c)
0877 {
0878 return ssh_rtl_get_device(&c->rtl);
0879 }
0880 EXPORT_SYMBOL_GPL(ssam_controller_device);
0881
0882 static void __ssam_controller_release(struct kref *kref)
0883 {
0884 struct ssam_controller *ctrl = to_ssam_controller(kref, kref);
0885
0886
0887
0888
0889
0890
0891 ssam_controller_lock(ctrl);
0892 ssam_controller_destroy(ctrl);
0893 ssam_controller_unlock(ctrl);
0894
0895 kfree(ctrl);
0896 }
0897
0898
0899
0900
0901
0902
0903
0904 struct ssam_controller *ssam_controller_get(struct ssam_controller *c)
0905 {
0906 if (c)
0907 kref_get(&c->kref);
0908 return c;
0909 }
0910 EXPORT_SYMBOL_GPL(ssam_controller_get);
0911
0912
0913
0914
0915
0916 void ssam_controller_put(struct ssam_controller *c)
0917 {
0918 if (c)
0919 kref_put(&c->kref, __ssam_controller_release);
0920 }
0921 EXPORT_SYMBOL_GPL(ssam_controller_put);
0922
0923
0924
0925
0926
0927
0928
0929
0930
0931
0932
0933
0934
0935
0936
0937
0938
0939
0940 void ssam_controller_statelock(struct ssam_controller *c)
0941 {
0942 down_read(&c->lock);
0943 }
0944 EXPORT_SYMBOL_GPL(ssam_controller_statelock);
0945
0946
0947
0948
0949
0950
0951
0952 void ssam_controller_stateunlock(struct ssam_controller *c)
0953 {
0954 up_read(&c->lock);
0955 }
0956 EXPORT_SYMBOL_GPL(ssam_controller_stateunlock);
0957
0958
0959
0960
0961
0962
0963
0964
0965
0966
0967
0968 void ssam_controller_lock(struct ssam_controller *c)
0969 {
0970 down_write(&c->lock);
0971 }
0972
0973
0974
0975
0976
0977
0978
0979 void ssam_controller_unlock(struct ssam_controller *c)
0980 {
0981 up_write(&c->lock);
0982 }
0983
0984 static void ssam_handle_event(struct ssh_rtl *rtl,
0985 const struct ssh_command *cmd,
0986 const struct ssam_span *data)
0987 {
0988 struct ssam_controller *ctrl = to_ssam_controller(rtl, rtl);
0989 struct ssam_event_item *item;
0990
0991 item = ssam_event_item_alloc(data->len, GFP_KERNEL);
0992 if (!item)
0993 return;
0994
0995 item->rqid = get_unaligned_le16(&cmd->rqid);
0996 item->event.target_category = cmd->tc;
0997 item->event.target_id = cmd->tid_in;
0998 item->event.command_id = cmd->cid;
0999 item->event.instance_id = cmd->iid;
1000 memcpy(&item->event.data[0], data->ptr, data->len);
1001
1002 if (WARN_ON(ssam_cplt_submit_event(&ctrl->cplt, item)))
1003 ssam_event_item_free(item);
1004 }
1005
1006 static const struct ssh_rtl_ops ssam_rtl_ops = {
1007 .handle_event = ssam_handle_event,
1008 };
1009
1010 static bool ssam_notifier_is_empty(struct ssam_controller *ctrl);
1011 static void ssam_notifier_unregister_all(struct ssam_controller *ctrl);
1012
1013 #define SSAM_SSH_DSM_REVISION 0
1014
1015
1016 static const guid_t SSAM_SSH_DSM_GUID =
1017 GUID_INIT(0xd5e383e1, 0xd892, 0x4a76,
1018 0x89, 0xfc, 0xf6, 0xaa, 0xae, 0x7e, 0xd5, 0xb5);
1019
1020 enum ssh_dsm_fn {
1021 SSH_DSM_FN_SSH_POWER_PROFILE = 0x05,
1022 SSH_DSM_FN_SCREEN_ON_SLEEP_IDLE_TIMEOUT = 0x06,
1023 SSH_DSM_FN_SCREEN_OFF_SLEEP_IDLE_TIMEOUT = 0x07,
1024 SSH_DSM_FN_D3_CLOSES_HANDLE = 0x08,
1025 SSH_DSM_FN_SSH_BUFFER_SIZE = 0x09,
1026 };
1027
1028 static int ssam_dsm_get_functions(acpi_handle handle, u64 *funcs)
1029 {
1030 union acpi_object *obj;
1031 u64 mask = 0;
1032 int i;
1033
1034 *funcs = 0;
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044 if (!acpi_has_method(handle, "_DSM"))
1045 return 0;
1046
1047 obj = acpi_evaluate_dsm_typed(handle, &SSAM_SSH_DSM_GUID,
1048 SSAM_SSH_DSM_REVISION, 0, NULL,
1049 ACPI_TYPE_BUFFER);
1050 if (!obj)
1051 return -EIO;
1052
1053 for (i = 0; i < obj->buffer.length && i < 8; i++)
1054 mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));
1055
1056 if (mask & BIT(0))
1057 *funcs = mask;
1058
1059 ACPI_FREE(obj);
1060 return 0;
1061 }
1062
1063 static int ssam_dsm_load_u32(acpi_handle handle, u64 funcs, u64 func, u32 *ret)
1064 {
1065 union acpi_object *obj;
1066 u64 val;
1067
1068 if (!(funcs & BIT_ULL(func)))
1069 return 0;
1070
1071 obj = acpi_evaluate_dsm_typed(handle, &SSAM_SSH_DSM_GUID,
1072 SSAM_SSH_DSM_REVISION, func, NULL,
1073 ACPI_TYPE_INTEGER);
1074 if (!obj)
1075 return -EIO;
1076
1077 val = obj->integer.value;
1078 ACPI_FREE(obj);
1079
1080 if (val > U32_MAX)
1081 return -ERANGE;
1082
1083 *ret = val;
1084 return 0;
1085 }
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099 static
1100 int ssam_controller_caps_load_from_acpi(acpi_handle handle,
1101 struct ssam_controller_caps *caps)
1102 {
1103 u32 d3_closes_handle = false;
1104 u64 funcs;
1105 int status;
1106
1107
1108 caps->ssh_power_profile = U32_MAX;
1109 caps->screen_on_sleep_idle_timeout = U32_MAX;
1110 caps->screen_off_sleep_idle_timeout = U32_MAX;
1111 caps->d3_closes_handle = false;
1112 caps->ssh_buffer_size = U32_MAX;
1113
1114
1115 status = ssam_dsm_get_functions(handle, &funcs);
1116 if (status)
1117 return status;
1118
1119
1120 status = ssam_dsm_load_u32(handle, funcs, SSH_DSM_FN_SSH_POWER_PROFILE,
1121 &caps->ssh_power_profile);
1122 if (status)
1123 return status;
1124
1125 status = ssam_dsm_load_u32(handle, funcs,
1126 SSH_DSM_FN_SCREEN_ON_SLEEP_IDLE_TIMEOUT,
1127 &caps->screen_on_sleep_idle_timeout);
1128 if (status)
1129 return status;
1130
1131 status = ssam_dsm_load_u32(handle, funcs,
1132 SSH_DSM_FN_SCREEN_OFF_SLEEP_IDLE_TIMEOUT,
1133 &caps->screen_off_sleep_idle_timeout);
1134 if (status)
1135 return status;
1136
1137 status = ssam_dsm_load_u32(handle, funcs, SSH_DSM_FN_D3_CLOSES_HANDLE,
1138 &d3_closes_handle);
1139 if (status)
1140 return status;
1141
1142 caps->d3_closes_handle = !!d3_closes_handle;
1143
1144 status = ssam_dsm_load_u32(handle, funcs, SSH_DSM_FN_SSH_BUFFER_SIZE,
1145 &caps->ssh_buffer_size);
1146 if (status)
1147 return status;
1148
1149 return 0;
1150 }
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165 int ssam_controller_init(struct ssam_controller *ctrl,
1166 struct serdev_device *serdev)
1167 {
1168 acpi_handle handle = ACPI_HANDLE(&serdev->dev);
1169 int status;
1170
1171 init_rwsem(&ctrl->lock);
1172 kref_init(&ctrl->kref);
1173
1174 status = ssam_controller_caps_load_from_acpi(handle, &ctrl->caps);
1175 if (status)
1176 return status;
1177
1178 dev_dbg(&serdev->dev,
1179 "device capabilities:\n"
1180 " ssh_power_profile: %u\n"
1181 " ssh_buffer_size: %u\n"
1182 " screen_on_sleep_idle_timeout: %u\n"
1183 " screen_off_sleep_idle_timeout: %u\n"
1184 " d3_closes_handle: %u\n",
1185 ctrl->caps.ssh_power_profile,
1186 ctrl->caps.ssh_buffer_size,
1187 ctrl->caps.screen_on_sleep_idle_timeout,
1188 ctrl->caps.screen_off_sleep_idle_timeout,
1189 ctrl->caps.d3_closes_handle);
1190
1191 ssh_seq_reset(&ctrl->counter.seq);
1192 ssh_rqid_reset(&ctrl->counter.rqid);
1193
1194
1195 status = ssam_cplt_init(&ctrl->cplt, &serdev->dev);
1196 if (status)
1197 return status;
1198
1199
1200 status = ssh_rtl_init(&ctrl->rtl, serdev, &ssam_rtl_ops);
1201 if (status) {
1202 ssam_cplt_destroy(&ctrl->cplt);
1203 return status;
1204 }
1205
1206
1207
1208
1209
1210
1211 WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_INITIALIZED);
1212 return 0;
1213 }
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227 int ssam_controller_start(struct ssam_controller *ctrl)
1228 {
1229 int status;
1230
1231 lockdep_assert_held_write(&ctrl->lock);
1232
1233 if (ctrl->state != SSAM_CONTROLLER_INITIALIZED)
1234 return -EINVAL;
1235
1236 status = ssh_rtl_start(&ctrl->rtl);
1237 if (status)
1238 return status;
1239
1240
1241
1242
1243
1244
1245 WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STARTED);
1246 return 0;
1247 }
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257 #define SSAM_CTRL_SHUTDOWN_FLUSH_TIMEOUT msecs_to_jiffies(5000)
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281 void ssam_controller_shutdown(struct ssam_controller *ctrl)
1282 {
1283 enum ssam_controller_state s = ctrl->state;
1284 int status;
1285
1286 lockdep_assert_held_write(&ctrl->lock);
1287
1288 if (s == SSAM_CONTROLLER_UNINITIALIZED || s == SSAM_CONTROLLER_STOPPED)
1289 return;
1290
1291
1292
1293
1294
1295
1296
1297
1298 status = ssh_rtl_flush(&ctrl->rtl, SSAM_CTRL_SHUTDOWN_FLUSH_TIMEOUT);
1299 if (status) {
1300 ssam_err(ctrl, "failed to flush request transport layer: %d\n",
1301 status);
1302 }
1303
1304
1305 ssam_cplt_flush(&ctrl->cplt);
1306
1307
1308
1309
1310
1311
1312 WARN_ON(!ssam_notifier_is_empty(ctrl));
1313
1314
1315
1316
1317
1318 ssam_notifier_unregister_all(ctrl);
1319
1320
1321
1322
1323
1324 ssh_rtl_shutdown(&ctrl->rtl);
1325
1326
1327
1328
1329
1330
1331 WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STOPPED);
1332 ctrl->rtl.ptl.serdev = NULL;
1333 }
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350 void ssam_controller_destroy(struct ssam_controller *ctrl)
1351 {
1352 lockdep_assert_held_write(&ctrl->lock);
1353
1354 if (ctrl->state == SSAM_CONTROLLER_UNINITIALIZED)
1355 return;
1356
1357 WARN_ON(ctrl->state != SSAM_CONTROLLER_STOPPED);
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368 ssam_cplt_destroy(&ctrl->cplt);
1369 ssh_rtl_destroy(&ctrl->rtl);
1370
1371
1372
1373
1374
1375
1376 WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_UNINITIALIZED);
1377 }
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392 int ssam_controller_suspend(struct ssam_controller *ctrl)
1393 {
1394 ssam_controller_lock(ctrl);
1395
1396 if (ctrl->state != SSAM_CONTROLLER_STARTED) {
1397 ssam_controller_unlock(ctrl);
1398 return -EINVAL;
1399 }
1400
1401 ssam_dbg(ctrl, "pm: suspending controller\n");
1402
1403
1404
1405
1406
1407 WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_SUSPENDED);
1408
1409 ssam_controller_unlock(ctrl);
1410 return 0;
1411 }
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424 int ssam_controller_resume(struct ssam_controller *ctrl)
1425 {
1426 ssam_controller_lock(ctrl);
1427
1428 if (ctrl->state != SSAM_CONTROLLER_SUSPENDED) {
1429 ssam_controller_unlock(ctrl);
1430 return -EINVAL;
1431 }
1432
1433 ssam_dbg(ctrl, "pm: resuming controller\n");
1434
1435
1436
1437
1438
1439 WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STARTED);
1440
1441 ssam_controller_unlock(ctrl);
1442 return 0;
1443 }
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468 ssize_t ssam_request_write_data(struct ssam_span *buf,
1469 struct ssam_controller *ctrl,
1470 const struct ssam_request *spec)
1471 {
1472 struct msgbuf msgb;
1473 u16 rqid;
1474 u8 seq;
1475
1476 if (spec->length > SSH_COMMAND_MAX_PAYLOAD_SIZE)
1477 return -EINVAL;
1478
1479 if (SSH_COMMAND_MESSAGE_LENGTH(spec->length) > buf->len)
1480 return -EINVAL;
1481
1482 msgb_init(&msgb, buf->ptr, buf->len);
1483 seq = ssh_seq_next(&ctrl->counter.seq);
1484 rqid = ssh_rqid_next(&ctrl->counter.rqid);
1485 msgb_push_cmd(&msgb, seq, rqid, spec);
1486
1487 return msgb_bytes_used(&msgb);
1488 }
1489 EXPORT_SYMBOL_GPL(ssam_request_write_data);
1490
1491 static void ssam_request_sync_complete(struct ssh_request *rqst,
1492 const struct ssh_command *cmd,
1493 const struct ssam_span *data, int status)
1494 {
1495 struct ssh_rtl *rtl = ssh_request_rtl(rqst);
1496 struct ssam_request_sync *r;
1497
1498 r = container_of(rqst, struct ssam_request_sync, base);
1499 r->status = status;
1500
1501 if (r->resp)
1502 r->resp->length = 0;
1503
1504 if (status) {
1505 rtl_dbg_cond(rtl, "rsp: request failed: %d\n", status);
1506 return;
1507 }
1508
1509 if (!data)
1510 return;
1511
1512 if (!r->resp || !r->resp->pointer) {
1513 if (data->len)
1514 rtl_warn(rtl, "rsp: no response buffer provided, dropping data\n");
1515 return;
1516 }
1517
1518 if (data->len > r->resp->capacity) {
1519 rtl_err(rtl,
1520 "rsp: response buffer too small, capacity: %zu bytes, got: %zu bytes\n",
1521 r->resp->capacity, data->len);
1522 r->status = -ENOSPC;
1523 return;
1524 }
1525
1526 r->resp->length = data->len;
1527 memcpy(r->resp->pointer, data->ptr, data->len);
1528 }
1529
1530 static void ssam_request_sync_release(struct ssh_request *rqst)
1531 {
1532 complete_all(&container_of(rqst, struct ssam_request_sync, base)->comp);
1533 }
1534
1535 static const struct ssh_request_ops ssam_request_sync_ops = {
1536 .release = ssam_request_sync_release,
1537 .complete = ssam_request_sync_complete,
1538 };
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560 int ssam_request_sync_alloc(size_t payload_len, gfp_t flags,
1561 struct ssam_request_sync **rqst,
1562 struct ssam_span *buffer)
1563 {
1564 size_t msglen = SSH_COMMAND_MESSAGE_LENGTH(payload_len);
1565
1566 *rqst = kzalloc(sizeof(**rqst) + msglen, flags);
1567 if (!*rqst)
1568 return -ENOMEM;
1569
1570 buffer->ptr = (u8 *)(*rqst + 1);
1571 buffer->len = msglen;
1572
1573 return 0;
1574 }
1575 EXPORT_SYMBOL_GPL(ssam_request_sync_alloc);
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592 void ssam_request_sync_free(struct ssam_request_sync *rqst)
1593 {
1594 kfree(rqst);
1595 }
1596 EXPORT_SYMBOL_GPL(ssam_request_sync_free);
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610 int ssam_request_sync_init(struct ssam_request_sync *rqst,
1611 enum ssam_request_flags flags)
1612 {
1613 int status;
1614
1615 status = ssh_request_init(&rqst->base, flags, &ssam_request_sync_ops);
1616 if (status)
1617 return status;
1618
1619 init_completion(&rqst->comp);
1620 rqst->resp = NULL;
1621 rqst->status = 0;
1622
1623 return 0;
1624 }
1625 EXPORT_SYMBOL_GPL(ssam_request_sync_init);
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645 int ssam_request_sync_submit(struct ssam_controller *ctrl,
1646 struct ssam_request_sync *rqst)
1647 {
1648 int status;
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664 if (WARN_ON(READ_ONCE(ctrl->state) != SSAM_CONTROLLER_STARTED)) {
1665 ssh_request_put(&rqst->base);
1666 return -ENODEV;
1667 }
1668
1669 status = ssh_rtl_submit(&ctrl->rtl, &rqst->base);
1670 ssh_request_put(&rqst->base);
1671
1672 return status;
1673 }
1674 EXPORT_SYMBOL_GPL(ssam_request_sync_submit);
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689 int ssam_request_sync(struct ssam_controller *ctrl,
1690 const struct ssam_request *spec,
1691 struct ssam_response *rsp)
1692 {
1693 struct ssam_request_sync *rqst;
1694 struct ssam_span buf;
1695 ssize_t len;
1696 int status;
1697
1698 status = ssam_request_sync_alloc(spec->length, GFP_KERNEL, &rqst, &buf);
1699 if (status)
1700 return status;
1701
1702 status = ssam_request_sync_init(rqst, spec->flags);
1703 if (status)
1704 return status;
1705
1706 ssam_request_sync_set_resp(rqst, rsp);
1707
1708 len = ssam_request_write_data(&buf, ctrl, spec);
1709 if (len < 0) {
1710 ssam_request_sync_free(rqst);
1711 return len;
1712 }
1713
1714 ssam_request_sync_set_data(rqst, buf.ptr, len);
1715
1716 status = ssam_request_sync_submit(ctrl, rqst);
1717 if (!status)
1718 status = ssam_request_sync_wait(rqst);
1719
1720 ssam_request_sync_free(rqst);
1721 return status;
1722 }
1723 EXPORT_SYMBOL_GPL(ssam_request_sync);
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746 int ssam_request_sync_with_buffer(struct ssam_controller *ctrl,
1747 const struct ssam_request *spec,
1748 struct ssam_response *rsp,
1749 struct ssam_span *buf)
1750 {
1751 struct ssam_request_sync rqst;
1752 ssize_t len;
1753 int status;
1754
1755 status = ssam_request_sync_init(&rqst, spec->flags);
1756 if (status)
1757 return status;
1758
1759 ssam_request_sync_set_resp(&rqst, rsp);
1760
1761 len = ssam_request_write_data(buf, ctrl, spec);
1762 if (len < 0)
1763 return len;
1764
1765 ssam_request_sync_set_data(&rqst, buf->ptr, len);
1766
1767 status = ssam_request_sync_submit(ctrl, &rqst);
1768 if (!status)
1769 status = ssam_request_sync_wait(&rqst);
1770
1771 return status;
1772 }
1773 EXPORT_SYMBOL_GPL(ssam_request_sync_with_buffer);
1774
1775
1776
1777
1778 SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_get_firmware_version, __le32, {
1779 .target_category = SSAM_SSH_TC_SAM,
1780 .target_id = 0x01,
1781 .command_id = 0x13,
1782 .instance_id = 0x00,
1783 });
1784
1785 SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_off, u8, {
1786 .target_category = SSAM_SSH_TC_SAM,
1787 .target_id = 0x01,
1788 .command_id = 0x15,
1789 .instance_id = 0x00,
1790 });
1791
1792 SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_on, u8, {
1793 .target_category = SSAM_SSH_TC_SAM,
1794 .target_id = 0x01,
1795 .command_id = 0x16,
1796 .instance_id = 0x00,
1797 });
1798
1799 SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_exit, u8, {
1800 .target_category = SSAM_SSH_TC_SAM,
1801 .target_id = 0x01,
1802 .command_id = 0x33,
1803 .instance_id = 0x00,
1804 });
1805
1806 SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_entry, u8, {
1807 .target_category = SSAM_SSH_TC_SAM,
1808 .target_id = 0x01,
1809 .command_id = 0x34,
1810 .instance_id = 0x00,
1811 });
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823 struct ssh_notification_params {
1824 u8 target_category;
1825 u8 flags;
1826 __le16 request_id;
1827 u8 instance_id;
1828 } __packed;
1829
1830 static_assert(sizeof(struct ssh_notification_params) == 5);
1831
1832 static int __ssam_ssh_event_request(struct ssam_controller *ctrl,
1833 struct ssam_event_registry reg, u8 cid,
1834 struct ssam_event_id id, u8 flags)
1835 {
1836 struct ssh_notification_params params;
1837 struct ssam_request rqst;
1838 struct ssam_response result;
1839 int status;
1840
1841 u16 rqid = ssh_tc_to_rqid(id.target_category);
1842 u8 buf = 0;
1843
1844
1845 if (!ssh_rqid_is_event(rqid))
1846 return -EINVAL;
1847
1848 params.target_category = id.target_category;
1849 params.instance_id = id.instance;
1850 params.flags = flags;
1851 put_unaligned_le16(rqid, ¶ms.request_id);
1852
1853 rqst.target_category = reg.target_category;
1854 rqst.target_id = reg.target_id;
1855 rqst.command_id = cid;
1856 rqst.instance_id = 0x00;
1857 rqst.flags = SSAM_REQUEST_HAS_RESPONSE;
1858 rqst.length = sizeof(params);
1859 rqst.payload = (u8 *)¶ms;
1860
1861 result.capacity = sizeof(buf);
1862 result.length = 0;
1863 result.pointer = &buf;
1864
1865 status = ssam_retry(ssam_request_sync_onstack, ctrl, &rqst, &result,
1866 sizeof(params));
1867
1868 return status < 0 ? status : buf;
1869 }
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888 static int ssam_ssh_event_enable(struct ssam_controller *ctrl,
1889 struct ssam_event_registry reg,
1890 struct ssam_event_id id, u8 flags)
1891 {
1892 int status;
1893
1894 status = __ssam_ssh_event_request(ctrl, reg, reg.cid_enable, id, flags);
1895
1896 if (status < 0 && status != -EINVAL) {
1897 ssam_err(ctrl,
1898 "failed to enable event source (tc: %#04x, iid: %#04x, reg: %#04x)\n",
1899 id.target_category, id.instance, reg.target_category);
1900 }
1901
1902 if (status > 0) {
1903 ssam_err(ctrl,
1904 "unexpected result while enabling event source: %#04x (tc: %#04x, iid: %#04x, reg: %#04x)\n",
1905 status, id.target_category, id.instance, reg.target_category);
1906 return -EPROTO;
1907 }
1908
1909 return status;
1910 }
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929 static int ssam_ssh_event_disable(struct ssam_controller *ctrl,
1930 struct ssam_event_registry reg,
1931 struct ssam_event_id id, u8 flags)
1932 {
1933 int status;
1934
1935 status = __ssam_ssh_event_request(ctrl, reg, reg.cid_disable, id, flags);
1936
1937 if (status < 0 && status != -EINVAL) {
1938 ssam_err(ctrl,
1939 "failed to disable event source (tc: %#04x, iid: %#04x, reg: %#04x)\n",
1940 id.target_category, id.instance, reg.target_category);
1941 }
1942
1943 if (status > 0) {
1944 ssam_err(ctrl,
1945 "unexpected result while disabling event source: %#04x (tc: %#04x, iid: %#04x, reg: %#04x)\n",
1946 status, id.target_category, id.instance, reg.target_category);
1947 return -EPROTO;
1948 }
1949
1950 return status;
1951 }
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964 int ssam_get_firmware_version(struct ssam_controller *ctrl, u32 *version)
1965 {
1966 __le32 __version;
1967 int status;
1968
1969 status = ssam_retry(ssam_ssh_get_firmware_version, ctrl, &__version);
1970 if (status)
1971 return status;
1972
1973 *version = le32_to_cpu(__version);
1974 return 0;
1975 }
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004 int ssam_ctrl_notif_display_off(struct ssam_controller *ctrl)
2005 {
2006 int status;
2007 u8 response;
2008
2009 ssam_dbg(ctrl, "pm: notifying display off\n");
2010
2011 status = ssam_retry(ssam_ssh_notif_display_off, ctrl, &response);
2012 if (status)
2013 return status;
2014
2015 if (response != 0) {
2016 ssam_err(ctrl, "unexpected response from display-off notification: %#04x\n",
2017 response);
2018 return -EPROTO;
2019 }
2020
2021 return 0;
2022 }
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043 int ssam_ctrl_notif_display_on(struct ssam_controller *ctrl)
2044 {
2045 int status;
2046 u8 response;
2047
2048 ssam_dbg(ctrl, "pm: notifying display on\n");
2049
2050 status = ssam_retry(ssam_ssh_notif_display_on, ctrl, &response);
2051 if (status)
2052 return status;
2053
2054 if (response != 0) {
2055 ssam_err(ctrl, "unexpected response from display-on notification: %#04x\n",
2056 response);
2057 return -EPROTO;
2058 }
2059
2060 return 0;
2061 }
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082 int ssam_ctrl_notif_d0_exit(struct ssam_controller *ctrl)
2083 {
2084 int status;
2085 u8 response;
2086
2087 if (!ctrl->caps.d3_closes_handle)
2088 return 0;
2089
2090 ssam_dbg(ctrl, "pm: notifying D0 exit\n");
2091
2092 status = ssam_retry(ssam_ssh_notif_d0_exit, ctrl, &response);
2093 if (status)
2094 return status;
2095
2096 if (response != 0) {
2097 ssam_err(ctrl, "unexpected response from D0-exit notification: %#04x\n",
2098 response);
2099 return -EPROTO;
2100 }
2101
2102 return 0;
2103 }
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124 int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl)
2125 {
2126 int status;
2127 u8 response;
2128
2129 if (!ctrl->caps.d3_closes_handle)
2130 return 0;
2131
2132 ssam_dbg(ctrl, "pm: notifying D0 entry\n");
2133
2134 status = ssam_retry(ssam_ssh_notif_d0_entry, ctrl, &response);
2135 if (status)
2136 return status;
2137
2138 if (response != 0) {
2139 ssam_err(ctrl, "unexpected response from D0-entry notification: %#04x\n",
2140 response);
2141 return -EPROTO;
2142 }
2143
2144 return 0;
2145 }
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171 static int ssam_nf_refcount_enable(struct ssam_controller *ctrl,
2172 struct ssam_nf_refcount_entry *entry, u8 flags)
2173 {
2174 const struct ssam_event_registry reg = entry->key.reg;
2175 const struct ssam_event_id id = entry->key.id;
2176 struct ssam_nf *nf = &ctrl->cplt.event.notif;
2177 int status;
2178
2179 lockdep_assert_held(&nf->lock);
2180
2181 ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
2182 reg.target_category, id.target_category, id.instance, entry->refcount);
2183
2184 if (entry->refcount == 1) {
2185 status = ssam_ssh_event_enable(ctrl, reg, id, flags);
2186 if (status)
2187 return status;
2188
2189 entry->flags = flags;
2190
2191 } else if (entry->flags != flags) {
2192 ssam_warn(ctrl,
2193 "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
2194 flags, entry->flags, reg.target_category, id.target_category,
2195 id.instance);
2196 }
2197
2198 return 0;
2199 }
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235 static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
2236 struct ssam_nf_refcount_entry *entry, u8 flags, bool ec)
2237 {
2238 const struct ssam_event_registry reg = entry->key.reg;
2239 const struct ssam_event_id id = entry->key.id;
2240 struct ssam_nf *nf = &ctrl->cplt.event.notif;
2241 int status = 0;
2242
2243 lockdep_assert_held(&nf->lock);
2244
2245 ssam_dbg(ctrl, "%s event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
2246 ec ? "disabling" : "detaching", reg.target_category, id.target_category,
2247 id.instance, entry->refcount);
2248
2249 if (entry->flags != flags) {
2250 ssam_warn(ctrl,
2251 "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
2252 flags, entry->flags, reg.target_category, id.target_category,
2253 id.instance);
2254 }
2255
2256 if (ec && entry->refcount == 0) {
2257 status = ssam_ssh_event_disable(ctrl, reg, id, flags);
2258 kfree(entry);
2259 }
2260
2261 return status;
2262 }
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286 int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
2287 {
2288 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
2289 struct ssam_nf_refcount_entry *entry = NULL;
2290 struct ssam_nf_head *nf_head;
2291 struct ssam_nf *nf;
2292 int status;
2293
2294 if (!ssh_rqid_is_event(rqid))
2295 return -EINVAL;
2296
2297 nf = &ctrl->cplt.event.notif;
2298 nf_head = &nf->head[ssh_rqid_to_event(rqid)];
2299
2300 mutex_lock(&nf->lock);
2301
2302 if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) {
2303 entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
2304 if (IS_ERR(entry)) {
2305 mutex_unlock(&nf->lock);
2306 return PTR_ERR(entry);
2307 }
2308 }
2309
2310 status = ssam_nfblk_insert(nf_head, &n->base);
2311 if (status) {
2312 if (entry)
2313 ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id);
2314
2315 mutex_unlock(&nf->lock);
2316 return status;
2317 }
2318
2319 if (entry) {
2320 status = ssam_nf_refcount_enable(ctrl, entry, n->event.flags);
2321 if (status) {
2322 ssam_nfblk_remove(&n->base);
2323 ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id);
2324 mutex_unlock(&nf->lock);
2325 synchronize_srcu(&nf_head->srcu);
2326 return status;
2327 }
2328 }
2329
2330 mutex_unlock(&nf->lock);
2331 return 0;
2332 }
2333 EXPORT_SYMBOL_GPL(ssam_notifier_register);
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354 int __ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n,
2355 bool disable)
2356 {
2357 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
2358 struct ssam_nf_refcount_entry *entry;
2359 struct ssam_nf_head *nf_head;
2360 struct ssam_nf *nf;
2361 int status = 0;
2362
2363 if (!ssh_rqid_is_event(rqid))
2364 return -EINVAL;
2365
2366 nf = &ctrl->cplt.event.notif;
2367 nf_head = &nf->head[ssh_rqid_to_event(rqid)];
2368
2369 mutex_lock(&nf->lock);
2370
2371 if (!ssam_nfblk_find(nf_head, &n->base)) {
2372 mutex_unlock(&nf->lock);
2373 return -ENOENT;
2374 }
2375
2376
2377
2378
2379
2380 if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) {
2381 entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
2382 if (WARN_ON(!entry)) {
2383
2384
2385
2386
2387
2388
2389 status = -ENOENT;
2390 goto remove;
2391 }
2392
2393 status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags, disable);
2394 }
2395
2396 remove:
2397 ssam_nfblk_remove(&n->base);
2398 mutex_unlock(&nf->lock);
2399 synchronize_srcu(&nf_head->srcu);
2400
2401 return status;
2402 }
2403 EXPORT_SYMBOL_GPL(__ssam_notifier_unregister);
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426 int ssam_controller_event_enable(struct ssam_controller *ctrl,
2427 struct ssam_event_registry reg,
2428 struct ssam_event_id id, u8 flags)
2429 {
2430 u16 rqid = ssh_tc_to_rqid(id.target_category);
2431 struct ssam_nf *nf = &ctrl->cplt.event.notif;
2432 struct ssam_nf_refcount_entry *entry;
2433 int status;
2434
2435 if (!ssh_rqid_is_event(rqid))
2436 return -EINVAL;
2437
2438 mutex_lock(&nf->lock);
2439
2440 entry = ssam_nf_refcount_inc(nf, reg, id);
2441 if (IS_ERR(entry)) {
2442 mutex_unlock(&nf->lock);
2443 return PTR_ERR(entry);
2444 }
2445
2446 status = ssam_nf_refcount_enable(ctrl, entry, flags);
2447 if (status) {
2448 ssam_nf_refcount_dec_free(nf, reg, id);
2449 mutex_unlock(&nf->lock);
2450 return status;
2451 }
2452
2453 mutex_unlock(&nf->lock);
2454 return 0;
2455 }
2456 EXPORT_SYMBOL_GPL(ssam_controller_event_enable);
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477 int ssam_controller_event_disable(struct ssam_controller *ctrl,
2478 struct ssam_event_registry reg,
2479 struct ssam_event_id id, u8 flags)
2480 {
2481 u16 rqid = ssh_tc_to_rqid(id.target_category);
2482 struct ssam_nf *nf = &ctrl->cplt.event.notif;
2483 struct ssam_nf_refcount_entry *entry;
2484 int status;
2485
2486 if (!ssh_rqid_is_event(rqid))
2487 return -EINVAL;
2488
2489 mutex_lock(&nf->lock);
2490
2491 entry = ssam_nf_refcount_dec(nf, reg, id);
2492 if (!entry) {
2493 mutex_unlock(&nf->lock);
2494 return -ENOENT;
2495 }
2496
2497 status = ssam_nf_refcount_disable_free(ctrl, entry, flags, true);
2498
2499 mutex_unlock(&nf->lock);
2500 return status;
2501 }
2502 EXPORT_SYMBOL_GPL(ssam_controller_event_disable);
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525 int ssam_notifier_disable_registered(struct ssam_controller *ctrl)
2526 {
2527 struct ssam_nf *nf = &ctrl->cplt.event.notif;
2528 struct rb_node *n;
2529 int status;
2530
2531 mutex_lock(&nf->lock);
2532 for (n = rb_first(&nf->refcount); n; n = rb_next(n)) {
2533 struct ssam_nf_refcount_entry *e;
2534
2535 e = rb_entry(n, struct ssam_nf_refcount_entry, node);
2536 status = ssam_ssh_event_disable(ctrl, e->key.reg,
2537 e->key.id, e->flags);
2538 if (status)
2539 goto err;
2540 }
2541 mutex_unlock(&nf->lock);
2542
2543 return 0;
2544
2545 err:
2546 for (n = rb_prev(n); n; n = rb_prev(n)) {
2547 struct ssam_nf_refcount_entry *e;
2548
2549 e = rb_entry(n, struct ssam_nf_refcount_entry, node);
2550 ssam_ssh_event_enable(ctrl, e->key.reg, e->key.id, e->flags);
2551 }
2552 mutex_unlock(&nf->lock);
2553
2554 return status;
2555 }
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570 void ssam_notifier_restore_registered(struct ssam_controller *ctrl)
2571 {
2572 struct ssam_nf *nf = &ctrl->cplt.event.notif;
2573 struct rb_node *n;
2574
2575 mutex_lock(&nf->lock);
2576 for (n = rb_first(&nf->refcount); n; n = rb_next(n)) {
2577 struct ssam_nf_refcount_entry *e;
2578
2579 e = rb_entry(n, struct ssam_nf_refcount_entry, node);
2580
2581
2582 ssam_ssh_event_enable(ctrl, e->key.reg, e->key.id, e->flags);
2583 }
2584 mutex_unlock(&nf->lock);
2585 }
2586
2587
2588
2589
2590
2591
2592
2593
2594 static bool ssam_notifier_is_empty(struct ssam_controller *ctrl)
2595 {
2596 struct ssam_nf *nf = &ctrl->cplt.event.notif;
2597 bool result;
2598
2599 mutex_lock(&nf->lock);
2600 result = ssam_nf_refcount_empty(nf);
2601 mutex_unlock(&nf->lock);
2602
2603 return result;
2604 }
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615 static void ssam_notifier_unregister_all(struct ssam_controller *ctrl)
2616 {
2617 struct ssam_nf *nf = &ctrl->cplt.event.notif;
2618 struct ssam_nf_refcount_entry *e, *n;
2619
2620 mutex_lock(&nf->lock);
2621 rbtree_postorder_for_each_entry_safe(e, n, &nf->refcount, node) {
2622
2623 ssam_ssh_event_disable(ctrl, e->key.reg, e->key.id, e->flags);
2624 kfree(e);
2625 }
2626 nf->refcount = RB_ROOT;
2627 mutex_unlock(&nf->lock);
2628 }
2629
2630
2631
2632
2633 static irqreturn_t ssam_irq_handle(int irq, void *dev_id)
2634 {
2635 struct ssam_controller *ctrl = dev_id;
2636
2637 ssam_dbg(ctrl, "pm: wake irq triggered\n");
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660 return IRQ_HANDLED;
2661 }
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695 int ssam_irq_setup(struct ssam_controller *ctrl)
2696 {
2697 struct device *dev = ssam_controller_device(ctrl);
2698 struct gpio_desc *gpiod;
2699 int irq;
2700 int status;
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713 const int irqf = IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN;
2714
2715 gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS);
2716 if (IS_ERR(gpiod))
2717 return PTR_ERR(gpiod);
2718
2719 irq = gpiod_to_irq(gpiod);
2720 gpiod_put(gpiod);
2721
2722 if (irq < 0)
2723 return irq;
2724
2725 status = request_threaded_irq(irq, NULL, ssam_irq_handle, irqf,
2726 "ssam_wakeup", ctrl);
2727 if (status)
2728 return status;
2729
2730 ctrl->irq.num = irq;
2731 return 0;
2732 }
2733
2734
2735
2736
2737
2738
2739
2740 void ssam_irq_free(struct ssam_controller *ctrl)
2741 {
2742 free_irq(ctrl->irq.num, ctrl);
2743 ctrl->irq.num = -1;
2744 }
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760 int ssam_irq_arm_for_wakeup(struct ssam_controller *ctrl)
2761 {
2762 struct device *dev = ssam_controller_device(ctrl);
2763 int status;
2764
2765 enable_irq(ctrl->irq.num);
2766 if (device_may_wakeup(dev)) {
2767 status = enable_irq_wake(ctrl->irq.num);
2768 if (status) {
2769 ssam_err(ctrl, "failed to enable wake IRQ: %d\n", status);
2770 disable_irq(ctrl->irq.num);
2771 return status;
2772 }
2773
2774 ctrl->irq.wakeup_enabled = true;
2775 } else {
2776 ctrl->irq.wakeup_enabled = false;
2777 }
2778
2779 return 0;
2780 }
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793 void ssam_irq_disarm_wakeup(struct ssam_controller *ctrl)
2794 {
2795 int status;
2796
2797 if (ctrl->irq.wakeup_enabled) {
2798 status = disable_irq_wake(ctrl->irq.num);
2799 if (status)
2800 ssam_err(ctrl, "failed to disable wake IRQ: %d\n", status);
2801
2802 ctrl->irq.wakeup_enabled = false;
2803 }
2804 disable_irq(ctrl->irq.num);
2805 }