|
@@ -1,4 +1,4 @@
|
|
|
-From 25788573eb6b46e5a525013489433a3a7a6ffffd Mon Sep 17 00:00:00 2001
|
|
|
+From cebebc5233f7fc9406332e77ada2950a4e1896ab Mon Sep 17 00:00:00 2001
|
|
|
From: qzed <qzed@users.noreply.github.com>
|
|
|
Date: Mon, 26 Aug 2019 01:11:08 +0200
|
|
|
Subject: [PATCH 3/6] surface-sam
|
|
@@ -22,12 +22,12 @@ Subject: [PATCH 3/6] surface-sam
|
|
|
.../x86/surface_sam/surface_sam_sid_power.h | 16 +
|
|
|
.../x86/surface_sam/surface_sam_sid_vhf.c | 429 ++
|
|
|
.../x86/surface_sam/surface_sam_sid_vhf.h | 14 +
|
|
|
- .../x86/surface_sam/surface_sam_ssh.c | 5190 +++++++++++++++++
|
|
|
+ .../x86/surface_sam/surface_sam_ssh.c | 5329 +++++++++++++++++
|
|
|
.../x86/surface_sam/surface_sam_ssh.h | 717 +++
|
|
|
- .../x86/surface_sam/surface_sam_ssh_trace.h | 532 ++
|
|
|
+ .../x86/surface_sam/surface_sam_ssh_trace.h | 587 ++
|
|
|
.../x86/surface_sam/surface_sam_vhf.c | 266 +
|
|
|
drivers/tty/serdev/core.c | 111 +-
|
|
|
- 23 files changed, 12349 insertions(+), 16 deletions(-)
|
|
|
+ 23 files changed, 12543 insertions(+), 16 deletions(-)
|
|
|
create mode 100644 drivers/platform/x86/surface_sam/Kconfig
|
|
|
create mode 100644 drivers/platform/x86/surface_sam/Makefile
|
|
|
create mode 100644 drivers/platform/x86/surface_sam/surface_sam_debugfs.c
|
|
@@ -5732,10 +5732,10 @@ index 0000000000000..d956de5cf877a
|
|
|
+#endif /* _SURFACE_SAM_SID_VHF_H */
|
|
|
diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh.c b/drivers/platform/x86/surface_sam/surface_sam_ssh.c
|
|
|
new file mode 100644
|
|
|
-index 0000000000000..4144fe0b3d192
|
|
|
+index 0000000000000..4551b75570f22
|
|
|
--- /dev/null
|
|
|
+++ b/drivers/platform/x86/surface_sam/surface_sam_ssh.c
|
|
|
-@@ -0,0 +1,5190 @@
|
|
|
+@@ -0,0 +1,5329 @@
|
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
+/*
|
|
|
+ * Surface Serial Hub (SSH) driver for communication with the Surface/System
|
|
@@ -5806,11 +5806,6 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ return rqid > 0 ? rqid + 1u : rqid + SSH_NUM_EVENTS + 1u;
|
|
|
+}
|
|
|
+
|
|
|
-+static inline u16 ssh_event_to_rqid(u16 event)
|
|
|
-+{
|
|
|
-+ return event + 1u;
|
|
|
-+}
|
|
|
-+
|
|
|
+static inline u16 ssh_rqid_to_event(u16 rqid)
|
|
|
+{
|
|
|
+ return rqid - 1u;
|
|
@@ -5826,11 +5821,6 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ return tc;
|
|
|
+}
|
|
|
+
|
|
|
-+static inline int ssh_tc_to_event(u8 tc)
|
|
|
-+{
|
|
|
-+ return ssh_rqid_to_event(ssh_tc_to_rqid(tc));
|
|
|
-+}
|
|
|
-+
|
|
|
+static inline u8 ssh_channel_to_index(u8 channel)
|
|
|
+{
|
|
|
+ return channel - 1u;
|
|
@@ -5906,31 +5896,6 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ msgb->ptr = ptr;
|
|
|
+}
|
|
|
+
|
|
|
-+static inline int msgb_alloc(struct msgbuf *msgb, size_t cap, gfp_t flags)
|
|
|
-+{
|
|
|
-+ u8 *buf;
|
|
|
-+
|
|
|
-+ buf = kzalloc(cap, flags);
|
|
|
-+ if (!buf)
|
|
|
-+ return -ENOMEM;
|
|
|
-+
|
|
|
-+ msgb_init(msgb, buf, cap);
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static inline void msgb_free(struct msgbuf *msgb)
|
|
|
-+{
|
|
|
-+ kfree(msgb->begin);
|
|
|
-+ msgb->begin = NULL;
|
|
|
-+ msgb->end = NULL;
|
|
|
-+ msgb->ptr = NULL;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static inline void msgb_reset(struct msgbuf *msgb)
|
|
|
-+{
|
|
|
-+ msgb->ptr = msgb->begin;
|
|
|
-+}
|
|
|
-+
|
|
|
+static inline size_t msgb_bytes_used(const struct msgbuf *msgb)
|
|
|
+{
|
|
|
+ return msgb->ptr - msgb->begin;
|
|
@@ -6204,11 +6169,6 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ buf->cap = 0;
|
|
|
+}
|
|
|
+
|
|
|
-+static inline void sshp_buf_reset(struct sshp_buf *buf)
|
|
|
-+{
|
|
|
-+ buf->len = 0;
|
|
|
-+}
|
|
|
-+
|
|
|
+static inline void sshp_buf_drop(struct sshp_buf *buf, size_t n)
|
|
|
+{
|
|
|
+ memmove(buf->ptr, buf->ptr + n, buf->len - n);
|
|
@@ -6623,10 +6583,8 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
-+static inline int ssh_ptl_write_buf(struct ssh_ptl *ptl,
|
|
|
-+ struct ssh_packet *packet,
|
|
|
-+ const unsigned char *buf,
|
|
|
-+ size_t count)
|
|
|
++static int ssh_ptl_write_buf(struct ssh_ptl *ptl, struct ssh_packet *packet,
|
|
|
++ const unsigned char *buf, size_t count)
|
|
|
+{
|
|
|
+ int status;
|
|
|
+
|
|
@@ -6643,7 +6601,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ return serdev_device_write_buf(ptl->serdev, buf, count);
|
|
|
+}
|
|
|
+
|
|
|
-+static inline void ssh_ptl_tx_inject_invalid_data(struct ssh_packet *packet)
|
|
|
++static void ssh_ptl_tx_inject_invalid_data(struct ssh_packet *packet)
|
|
|
+{
|
|
|
+ // ignore packets that don't carry any data (i.e. flush)
|
|
|
+ if (!packet->data.ptr || !packet->data.len)
|
|
@@ -6669,8 +6627,8 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ memset(packet->data.ptr, 0xb3, packet->data.len);
|
|
|
+}
|
|
|
+
|
|
|
-+static inline void ssh_ptl_rx_inject_invalid_syn(struct ssh_ptl *ptl,
|
|
|
-+ struct ssam_span *data)
|
|
|
++static void ssh_ptl_rx_inject_invalid_syn(struct ssh_ptl *ptl,
|
|
|
++ struct ssam_span *data)
|
|
|
+{
|
|
|
+ struct ssam_span frame;
|
|
|
+
|
|
@@ -6686,8 +6644,8 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ data->ptr[1] = 0xb3; // set second byte of SYN to "random" value
|
|
|
+}
|
|
|
+
|
|
|
-+static inline void ssh_ptl_rx_inject_invalid_data(struct ssh_ptl *ptl,
|
|
|
-+ struct ssam_span *frame)
|
|
|
++static void ssh_ptl_rx_inject_invalid_data(struct ssh_ptl *ptl,
|
|
|
++ struct ssam_span *frame)
|
|
|
+{
|
|
|
+ size_t payload_len, message_len;
|
|
|
+ struct ssh_frame *sshf;
|
|
@@ -6803,41 +6761,51 @@ index 0000000000000..4144fe0b3d192
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
-+static int ptl_alloc_ctrl_packet(struct ssh_ptl *ptl,
|
|
|
-+ struct ssh_packet **packet,
|
|
|
-+ struct ssam_span *buffer, gfp_t flags)
|
|
|
++static struct kmem_cache *ssh_ctrl_packet_cache;
|
|
|
++
|
|
|
++static int __init ssh_ctrl_packet_cache_init(void)
|
|
|
+{
|
|
|
-+ // TODO: cache packets
|
|
|
-+ // - Potential problem with kmem_cache: Minimum alloc size of that is
|
|
|
-+ // PAGE_SIZE (???), which is somewhat overkill here.
|
|
|
-+ // - Note: Mempool always tries to allocate with alloc callback first.
|
|
|
-+ // Buffered objects are only chosen on allocation failure.
|
|
|
-+ //
|
|
|
-+ // => either kmem_cache or custom, try kmem_cache first and check via
|
|
|
-+ // /proc/slabinfo
|
|
|
-+ //
|
|
|
-+ // Note: kmem_cache_create needs unique name
|
|
|
++ const unsigned int size = sizeof(struct ssh_packet) + SSH_MSG_LEN_CTRL;
|
|
|
++ const unsigned int align = __alignof__(struct ssh_packet);
|
|
|
++ struct kmem_cache *cache;
|
|
|
++
|
|
|
++ cache = kmem_cache_create("ssam_ctrl_packet", size, align, 0, NULL);
|
|
|
++ if (!cache)
|
|
|
++ return -ENOMEM;
|
|
|
++
|
|
|
++ ssh_ctrl_packet_cache = cache;
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++static void __exit ssh_ctrl_packet_cache_destroy(void)
|
|
|
++{
|
|
|
++ kmem_cache_destroy(ssh_ctrl_packet_cache);
|
|
|
++ ssh_ctrl_packet_cache = NULL;
|
|
|
++}
|
|
|
+
|
|
|
-+ *packet = kzalloc(sizeof(struct ssh_packet) + SSH_MSG_LEN_CTRL, flags);
|
|
|
++static int ssh_ctrl_packet_alloc(struct ssh_packet **packet,
|
|
|
++ struct ssam_span *buffer, gfp_t flags)
|
|
|
++{
|
|
|
++ *packet = kmem_cache_alloc(ssh_ctrl_packet_cache, flags);
|
|
|
+ if (!*packet)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ buffer->ptr = (u8 *)(*packet + 1);
|
|
|
+ buffer->len = SSH_MSG_LEN_CTRL;
|
|
|
+
|
|
|
++ trace_ssam_ctrl_packet_alloc(*packet, buffer->len);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
-+static void ptl_free_ctrl_packet(struct ssh_packet *p)
|
|
|
++static void ssh_ctrl_packet_free(struct ssh_packet *p)
|
|
|
+{
|
|
|
-+ // TODO: cache packets
|
|
|
-+
|
|
|
-+ kfree(p);
|
|
|
++ trace_ssam_ctrl_packet_free(p);
|
|
|
++ kmem_cache_free(ssh_ctrl_packet_cache, p);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct ssh_packet_ops ssh_ptl_ctrl_packet_ops = {
|
|
|
+ .complete = NULL,
|
|
|
-+ .release = ptl_free_ctrl_packet,
|
|
|
++ .release = ssh_ctrl_packet_free,
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
@@ -7687,7 +7655,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ struct msgbuf msgb;
|
|
|
+ int status;
|
|
|
+
|
|
|
-+ status = ptl_alloc_ctrl_packet(ptl, &packet, &buf, GFP_KERNEL);
|
|
|
++ status = ssh_ctrl_packet_alloc(&packet, &buf, GFP_KERNEL);
|
|
|
+ if (status) {
|
|
|
+ ptl_err(ptl, "ptl: failed to allocate ACK packet\n");
|
|
|
+ return;
|
|
@@ -7714,7 +7682,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ struct msgbuf msgb;
|
|
|
+ int status;
|
|
|
+
|
|
|
-+ status = ptl_alloc_ctrl_packet(ptl, &packet, &buf, GFP_KERNEL);
|
|
|
++ status = ssh_ctrl_packet_alloc(&packet, &buf, GFP_KERNEL);
|
|
|
+ if (status) {
|
|
|
+ ptl_err(ptl, "ptl: failed to allocate NAK packet\n");
|
|
|
+ return;
|
|
@@ -8021,7 +7989,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+
|
|
|
+static inline struct device *ssh_ptl_get_device(struct ssh_ptl *ptl)
|
|
|
+{
|
|
|
-+ return &ptl->serdev->dev;
|
|
|
++ return ptl->serdev ? &ptl->serdev->dev : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int ssh_ptl_init(struct ssh_ptl *ptl, struct serdev_device *serdev,
|
|
@@ -9393,6 +9361,11 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ return -ENOENT;
|
|
|
+}
|
|
|
+
|
|
|
++static bool ssam_nf_refcount_empty(struct ssam_nf *nf)
|
|
|
++{
|
|
|
++ return RB_EMPTY_ROOT(&nf->refcount);
|
|
|
++}
|
|
|
++
|
|
|
+static void ssam_nf_call(struct ssam_nf *nf, struct device *dev, u16 rqid,
|
|
|
+ struct ssam_event *event)
|
|
|
+{
|
|
@@ -9461,10 +9434,17 @@ index 0000000000000..4144fe0b3d192
|
|
|
+
|
|
|
+
|
|
|
+struct ssam_cplt;
|
|
|
++struct ssam_event_item;
|
|
|
++
|
|
|
++struct ssam_event_item_ops {
|
|
|
++ void (*free)(struct ssam_event_item *);
|
|
|
++};
|
|
|
+
|
|
|
+struct ssam_event_item {
|
|
|
+ struct list_head node;
|
|
|
+ u16 rqid;
|
|
|
++
|
|
|
++ struct ssam_event_item_ops ops;
|
|
|
+ struct ssam_event event; // must be last
|
|
|
+};
|
|
|
+
|
|
@@ -9491,6 +9471,79 @@ index 0000000000000..4144fe0b3d192
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
++/**
|
|
|
++ * Maximum payload length for cached `ssam_event_item`s.
|
|
|
++ *
|
|
|
++ * This length has been chosen to be accomodate standard touchpad and keyboard
|
|
|
++ * input events. Events with larger payloads will be allocated separately.
|
|
|
++ */
|
|
|
++#define SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN 32
|
|
|
++
|
|
|
++static struct kmem_cache *ssam_event_item_cache;
|
|
|
++
|
|
|
++static int ssam_event_item_cache_init(void)
|
|
|
++{
|
|
|
++ const unsigned int size = sizeof(struct ssam_event_item)
|
|
|
++ + SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN;
|
|
|
++ const unsigned int align = __alignof__(struct ssam_event_item);
|
|
|
++ struct kmem_cache *cache;
|
|
|
++
|
|
|
++ cache = kmem_cache_create("ssam_event_item", size, align, 0, NULL);
|
|
|
++ if (!cache)
|
|
|
++ return -ENOMEM;
|
|
|
++
|
|
|
++ ssam_event_item_cache = cache;
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++static void ssam_event_item_cache_destroy(void)
|
|
|
++{
|
|
|
++ kmem_cache_destroy(ssam_event_item_cache);
|
|
|
++ ssam_event_item_cache = NULL;
|
|
|
++}
|
|
|
++
|
|
|
++static void __ssam_event_item_free_cached(struct ssam_event_item *item)
|
|
|
++{
|
|
|
++ kmem_cache_free(ssam_event_item_cache, item);
|
|
|
++}
|
|
|
++
|
|
|
++static void __ssam_event_item_free_generic(struct ssam_event_item *item)
|
|
|
++{
|
|
|
++ kfree(item);
|
|
|
++}
|
|
|
++
|
|
|
++static inline void ssam_event_item_free(struct ssam_event_item *item)
|
|
|
++{
|
|
|
++ trace_ssam_event_item_free(item);
|
|
|
++ item->ops.free(item);
|
|
|
++}
|
|
|
++
|
|
|
++static struct ssam_event_item *ssam_event_item_alloc(size_t len, gfp_t flags)
|
|
|
++{
|
|
|
++ struct ssam_event_item *item;
|
|
|
++
|
|
|
++ if (len <= SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN) {
|
|
|
++ item = kmem_cache_alloc(ssam_event_item_cache, GFP_KERNEL);
|
|
|
++ if (!item)
|
|
|
++ return NULL;
|
|
|
++
|
|
|
++ item->ops.free = __ssam_event_item_free_cached;
|
|
|
++ } else {
|
|
|
++ const size_t n = sizeof(struct ssam_event_item) + len;
|
|
|
++ item = kzalloc(n, GFP_KERNEL);
|
|
|
++ if (!item)
|
|
|
++ return NULL;
|
|
|
++
|
|
|
++ item->ops.free = __ssam_event_item_free_generic;
|
|
|
++ }
|
|
|
++
|
|
|
++ item->event.length = len;
|
|
|
++
|
|
|
++ trace_ssam_event_item_alloc(item, len);
|
|
|
++ return item;
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
+static void ssam_event_queue_push(struct ssam_event_queue *q,
|
|
|
+ struct ssam_event_item *item)
|
|
|
+{
|
|
@@ -9586,7 +9639,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ return;
|
|
|
+
|
|
|
+ ssam_nf_call(nf, dev, item->rqid, &item->event);
|
|
|
-+ kfree(item);
|
|
|
++ ssam_event_item_free(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ssam_event_queue_is_empty(queue))
|
|
@@ -9629,6 +9682,12 @@ index 0000000000000..4144fe0b3d192
|
|
|
+
|
|
|
+static void ssam_cplt_destroy(struct ssam_cplt *cplt)
|
|
|
+{
|
|
|
++ /*
|
|
|
++ * Note: destroy_workqueue ensures that all currently queued work will
|
|
|
++ * be fully completed and the workqueue drained. This means that this
|
|
|
++ * call will inherently also free any queued ssam_event_items, thus we
|
|
|
++ * don't have to take care of that here explicitly.
|
|
|
++ */
|
|
|
+ destroy_workqueue(cplt->wq);
|
|
|
+ ssam_nf_destroy(&cplt->event.notif);
|
|
|
+}
|
|
@@ -9679,7 +9738,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+
|
|
|
+struct device *ssam_controller_device(struct ssam_controller *c)
|
|
|
+{
|
|
|
-+ return (c && c->rtl.ptl.serdev) ? &c->rtl.ptl.serdev->dev : NULL;
|
|
|
++ return ssh_rtl_get_device(&c->rtl);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ssam_controller_device);
|
|
|
+
|
|
@@ -9691,7 +9750,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ struct ssam_controller *ctrl = to_ssam_controller(rtl, rtl);
|
|
|
+ struct ssam_event_item *item;
|
|
|
+
|
|
|
-+ item = kzalloc(sizeof(struct ssam_event_item) + data->len, GFP_KERNEL);
|
|
|
++ item = ssam_event_item_alloc(data->len, GFP_KERNEL);
|
|
|
+ if (!item)
|
|
|
+ return;
|
|
|
+
|
|
@@ -9700,7 +9759,6 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ item->event.command_id = cmd->cid;
|
|
|
+ item->event.instance_id = cmd->iid;
|
|
|
+ item->event.channel = cmd->chn_in;
|
|
|
-+ item->event.length = data->len;
|
|
|
+ memcpy(&item->event.data[0], data->ptr, data->len);
|
|
|
+
|
|
|
+ ssam_cplt_submit_event(&ctrl->cplt, item);
|
|
@@ -9711,6 +9769,10 @@ index 0000000000000..4144fe0b3d192
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
++static bool ssam_notifier_empty(struct ssam_controller *ctrl);
|
|
|
++static void ssam_notifier_unregister_all(struct ssam_controller *ctrl);
|
|
|
++
|
|
|
++
|
|
|
+#define SSAM_SSH_DSM_REVISION 0
|
|
|
+#define SSAM_SSH_DSM_NOTIF_D0 8
|
|
|
+static const guid_t SSAM_SSH_DSM_UUID = GUID_INIT(0xd5e383e1, 0xd892, 0x4a76,
|
|
@@ -9786,7 +9848,6 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ // initialize request and packet transmission layers
|
|
|
+ status = ssh_rtl_init(&ctrl->rtl, serdev, &ssam_rtl_ops);
|
|
|
+ if (status) {
|
|
|
-+ ssam_cplt_flush(&ctrl->cplt);
|
|
|
+ ssam_cplt_destroy(&ctrl->cplt);
|
|
|
+ return status;
|
|
|
+ }
|
|
@@ -9832,9 +9893,22 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ status);
|
|
|
+ }
|
|
|
+
|
|
|
-+ // flush out all currently completing requests and events
|
|
|
++ // try to flush out all currently completing requests and events
|
|
|
+ ssam_cplt_flush(&ctrl->cplt);
|
|
|
+
|
|
|
++ /*
|
|
|
++ * We expect all notifiers to have been removed by the respective client
|
|
|
++ * driver that set them up at this point. If this warning occurs, some
|
|
|
++ * client driver has not done that...
|
|
|
++ */
|
|
|
++ WARN_ON(!ssam_notifier_empty(ctrl));
|
|
|
++
|
|
|
++ /*
|
|
|
++ * Nevertheless, we should still take care of drivers that don't behave
|
|
|
++ * well. Thus disable all enabled events, unregister all notifiers.
|
|
|
++ */
|
|
|
++ ssam_notifier_unregister_all(ctrl);
|
|
|
++
|
|
|
+ // cancel rem. requests, ensure no new ones can be queued, stop threads
|
|
|
+ ssh_rtl_tx_flush(&ctrl->rtl);
|
|
|
+ ssh_rtl_shutdown(&ctrl->rtl);
|
|
@@ -9848,13 +9922,12 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ return;
|
|
|
+
|
|
|
+ /*
|
|
|
-+ * Ensure _all_ events are completed. New ones could still have been
|
|
|
-+ * received after the previous flush in ssam_controller_shutdown, before
|
|
|
-+ * the request transport layer has been shut down. At this point we can
|
|
|
-+ * be sure that no new requests will be queued for completion after this
|
|
|
-+ * call.
|
|
|
++ * Note: New events could still have been received after the previous
|
|
|
++ * flush in ssam_controller_shutdown, before the request transport layer
|
|
|
++ * has been shut down. At this point, after the shutdown, we can be sure
|
|
|
++ * that no new events will be queued. The call to ssam_cplt_destroy will
|
|
|
++ * ensure that those remaining are being completed and freed.
|
|
|
+ */
|
|
|
-+ ssam_cplt_flush(&ctrl->cplt);
|
|
|
+
|
|
|
+ // actually free resources
|
|
|
+ ssam_cplt_destroy(&ctrl->cplt);
|
|
@@ -9863,7 +9936,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ smp_store_release(&ctrl->state, SSAM_CONTROLLER_UNINITIALIZED);
|
|
|
+}
|
|
|
+
|
|
|
-+static inline int ssam_controller_suspend(struct ssam_controller *ctrl)
|
|
|
++static int ssam_controller_suspend(struct ssam_controller *ctrl)
|
|
|
+{
|
|
|
+ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED)
|
|
|
+ return -EINVAL;
|
|
@@ -9873,7 +9946,7 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
-+static inline int ssam_controller_resume(struct ssam_controller *ctrl)
|
|
|
++static int ssam_controller_resume(struct ssam_controller *ctrl)
|
|
|
+{
|
|
|
+ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_SUSPENDED)
|
|
|
+ return -EINVAL;
|
|
@@ -10353,14 +10426,16 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ if (!ssh_rqid_is_event(rqid))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
-+ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED)
|
|
|
-+ return -ENXIO;
|
|
|
-+
|
|
|
+ nf = &ctrl->cplt.event.notif;
|
|
|
+ nf_head = &nf->head[ssh_rqid_to_event(rqid)];
|
|
|
+
|
|
|
+ mutex_lock(&nf->lock);
|
|
|
+
|
|
|
++ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED) {
|
|
|
++ mutex_unlock(&nf->lock);
|
|
|
++ return -ENXIO;
|
|
|
++ }
|
|
|
++
|
|
|
+ rc = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
|
|
|
+ if (rc < 0) {
|
|
|
+ mutex_unlock(&nf->lock);
|
|
@@ -10407,14 +10482,16 @@ index 0000000000000..4144fe0b3d192
|
|
|
+ if (!ssh_rqid_is_event(rqid))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
-+ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED)
|
|
|
-+ return -ENXIO;
|
|
|
-+
|
|
|
+ nf = &ctrl->cplt.event.notif;
|
|
|
+ nf_head = &nf->head[ssh_rqid_to_event(rqid)];
|
|
|
+
|
|
|
+ mutex_lock(&nf->lock);
|
|
|
+
|
|
|
++ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED) {
|
|
|
++ mutex_unlock(&nf->lock);
|
|
|
++ return -ENXIO;
|
|
|
++ }
|
|
|
++
|
|
|
+ rc = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
|
|
|
+ if (rc < 0) {
|
|
|
+ mutex_unlock(&nf->lock);
|
|
@@ -10438,6 +10515,33 @@ index 0000000000000..4144fe0b3d192
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ssam_notifier_unregister);
|
|
|
+
|
|
|
++static bool ssam_notifier_empty(struct ssam_controller *ctrl)
|
|
|
++{
|
|
|
++ struct ssam_nf *nf = &ctrl->cplt.event.notif;
|
|
|
++ bool result;
|
|
|
++
|
|
|
++ mutex_lock(&nf->lock);
|
|
|
++ result = ssam_nf_refcount_empty(nf);
|
|
|
++ mutex_unlock(&nf->lock);
|
|
|
++
|
|
|
++ return result;
|
|
|
++}
|
|
|
++
|
|
|
++static void ssam_notifier_unregister_all(struct ssam_controller *ctrl)
|
|
|
++{
|
|
|
++ struct ssam_nf *nf = &ctrl->cplt.event.notif;
|
|
|
++ struct ssam_nf_refcount_entry *pos, *n;
|
|
|
++
|
|
|
++ mutex_lock(&nf->lock);
|
|
|
++ rbtree_postorder_for_each_entry_safe(pos, n, &nf->refcount, node) {
|
|
|
++ // ignore errors, will get logged in call
|
|
|
++ ssam_ssh_event_disable(ctrl, pos->key.reg, pos->key.id, 0);
|
|
|
++ kfree(pos);
|
|
|
++ }
|
|
|
++ nf->refcount = RB_ROOT;
|
|
|
++ mutex_unlock(&nf->lock);
|
|
|
++}
|
|
|
++
|
|
|
+
|
|
|
+/* -- Wakeup IRQ. ----------------------------------------------------------- */
|
|
|
+
|
|
@@ -10480,12 +10584,24 @@ index 0000000000000..4144fe0b3d192
|
|
|
+
|
|
|
+static int ssam_irq_setup(struct ssam_controller *ctrl)
|
|
|
+{
|
|
|
-+ const int irqf = IRQF_SHARED | IRQF_ONESHOT;
|
|
|
+ struct device *dev = ssam_controller_device(ctrl);
|
|
|
+ struct gpio_desc *gpiod;
|
|
|
+ int irq;
|
|
|
+ int status;
|
|
|
+
|
|
|
++ /*
|
|
|
++ * The actual GPIO interrupt is declared in ACPI as TRIGGER_HIGH.
|
|
|
++ * However, the GPIO line only gets reset by sending the GPIO callback
|
|
|
++ * command to SAM (or alternatively the display-on notification). As
|
|
|
++ * proper handling for this interrupt is not implemented yet, leaving
|
|
|
++ * the IRQ at TRIGGER_HIGH would cause an IRQ storm (as the callback
|
|
|
++ * never gets sent and thus the line line never gets reset). To avoid
|
|
|
++ * this, mark the IRQ as TRIGGER_RISING for now, only creating a single
|
|
|
++ * interrupt, and let the SAM resume callback during the controller
|
|
|
++ * resume process clear it.
|
|
|
++ */
|
|
|
++ const int irqf = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING;
|
|
|
++
|
|
|
+ gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS);
|
|
|
+ if (IS_ERR(gpiod))
|
|
|
+ return PTR_ERR(gpiod);
|
|
@@ -10902,12 +11018,35 @@ index 0000000000000..4144fe0b3d192
|
|
|
+
|
|
|
+static int __init surface_sam_ssh_init(void)
|
|
|
+{
|
|
|
-+ return serdev_device_driver_register(&surface_sam_ssh);
|
|
|
++ int status;
|
|
|
++
|
|
|
++ status = ssh_ctrl_packet_cache_init();
|
|
|
++ if (status)
|
|
|
++ goto err_cpkg;
|
|
|
++
|
|
|
++ status = ssam_event_item_cache_init();
|
|
|
++ if (status)
|
|
|
++ goto err_evitem;
|
|
|
++
|
|
|
++ status = serdev_device_driver_register(&surface_sam_ssh);
|
|
|
++ if (status)
|
|
|
++ goto err_register;
|
|
|
++
|
|
|
++ return 0;
|
|
|
++
|
|
|
++err_register:
|
|
|
++ ssam_event_item_cache_destroy();
|
|
|
++err_evitem:
|
|
|
++ ssh_ctrl_packet_cache_destroy();
|
|
|
++err_cpkg:
|
|
|
++ return status;
|
|
|
+}
|
|
|
+
|
|
|
+static void __exit surface_sam_ssh_exit(void)
|
|
|
+{
|
|
|
+ serdev_device_driver_unregister(&surface_sam_ssh);
|
|
|
++ ssam_event_item_cache_destroy();
|
|
|
++ ssh_ctrl_packet_cache_destroy();
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
@@ -11651,10 +11790,10 @@ index 0000000000000..ba57adb2a3c9d
|
|
|
+#endif /* _SURFACE_SAM_SSH_H */
|
|
|
diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h b/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h
|
|
|
new file mode 100644
|
|
|
-index 0000000000000..4755183fa423b
|
|
|
+index 0000000000000..8ea9a2fc99d7e
|
|
|
--- /dev/null
|
|
|
+++ b/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h
|
|
|
-@@ -0,0 +1,532 @@
|
|
|
+@@ -0,0 +1,587 @@
|
|
|
+#undef TRACE_SYSTEM
|
|
|
+#define TRACE_SYSTEM surface_sam_ssh
|
|
|
+
|
|
@@ -12125,6 +12264,55 @@ index 0000000000000..4755183fa423b
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
++DECLARE_EVENT_CLASS(ssam_alloc_class,
|
|
|
++ TP_PROTO(void *ptr, size_t len),
|
|
|
++
|
|
|
++ TP_ARGS(ptr, len),
|
|
|
++
|
|
|
++ TP_STRUCT__entry(
|
|
|
++ __array(char, uid, SSAM_PTR_UID_LEN)
|
|
|
++ __field(size_t, len)
|
|
|
++ ),
|
|
|
++
|
|
|
++ TP_fast_assign(
|
|
|
++ ssam_trace_ptr_uid(ptr, __entry->uid);
|
|
|
++ __entry->len = len;
|
|
|
++ ),
|
|
|
++
|
|
|
++ TP_printk("uid=%s, len=%zu", __entry->uid, __entry->len)
|
|
|
++);
|
|
|
++
|
|
|
++#define DEFINE_SSAM_ALLOC_EVENT(name) \
|
|
|
++ DEFINE_EVENT(ssam_alloc_class, ssam_##name, \
|
|
|
++ TP_PROTO(void *ptr, size_t len), \
|
|
|
++ TP_ARGS(ptr, len) \
|
|
|
++ )
|
|
|
++
|
|
|
++
|
|
|
++DECLARE_EVENT_CLASS(ssam_free_class,
|
|
|
++ TP_PROTO(void *ptr),
|
|
|
++
|
|
|
++ TP_ARGS(ptr),
|
|
|
++
|
|
|
++ TP_STRUCT__entry(
|
|
|
++ __array(char, uid, SSAM_PTR_UID_LEN)
|
|
|
++ __field(size_t, len)
|
|
|
++ ),
|
|
|
++
|
|
|
++ TP_fast_assign(
|
|
|
++ ssam_trace_ptr_uid(ptr, __entry->uid);
|
|
|
++ ),
|
|
|
++
|
|
|
++ TP_printk("uid=%s", __entry->uid)
|
|
|
++);
|
|
|
++
|
|
|
++#define DEFINE_SSAM_FREE_EVENT(name) \
|
|
|
++ DEFINE_EVENT(ssam_free_class, ssam_##name, \
|
|
|
++ TP_PROTO(void *ptr), \
|
|
|
++ TP_ARGS(ptr) \
|
|
|
++ )
|
|
|
++
|
|
|
++
|
|
|
+DECLARE_EVENT_CLASS(ssam_generic_uint_class,
|
|
|
+ TP_PROTO(const char* property, unsigned int value),
|
|
|
+
|
|
@@ -12177,6 +12365,12 @@ index 0000000000000..4755183fa423b
|
|
|
+DEFINE_SSAM_FRAME_EVENT(ei_rx_corrupt_data);
|
|
|
+DEFINE_SSAM_REQUEST_EVENT(ei_rx_drop_response);
|
|
|
+
|
|
|
++DEFINE_SSAM_ALLOC_EVENT(ctrl_packet_alloc);
|
|
|
++DEFINE_SSAM_FREE_EVENT(ctrl_packet_free);
|
|
|
++
|
|
|
++DEFINE_SSAM_ALLOC_EVENT(event_item_alloc);
|
|
|
++DEFINE_SSAM_FREE_EVENT(event_item_free);
|
|
|
++
|
|
|
+#endif /* _SURFACE_SAM_SSH_TRACE_H */
|
|
|
+
|
|
|
+/* This part must be outside protection */
|