From 67e6fb74074fe3f2a7f4556c84c4a6849b364f5d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 1 May 2024 00:36:34 +0000 Subject: [PATCH] chore: autopublish 2024-05-01T00:36:34Z --- .../surface-sources/files/6.8/0004-ipts.patch | 53 + .../surface-sources/files/6.8/0005-ithc.patch | 2520 +++++++++++++++++ .../files/6.8/0006-surface-sam.patch | 54 + 3 files changed, 2627 insertions(+) diff --git a/sys-kernel/surface-sources/files/6.8/0004-ipts.patch b/sys-kernel/surface-sources/files/6.8/0004-ipts.patch index 383a872..bab19d1 100644 --- a/sys-kernel/surface-sources/files/6.8/0004-ipts.patch +++ b/sys-kernel/surface-sources/files/6.8/0004-ipts.patch @@ -3236,3 +3236,56 @@ index 000000000000..1f966b8b32c4 -- 2.44.0 +From e5b4ea8a015bee22023bb5c8fb6dd8b708bff7d5 Mon Sep 17 00:00:00 2001 +From: Jasmin Huber +Date: Mon, 15 Apr 2024 10:22:55 +0200 +Subject: [PATCH] Inlude headers to avoid compiler warnings 6.8 kernels compile + with -Wmissing-prototypes. + +Signed-off-by: Dorian Stoll +Patchset: ipts +--- + drivers/hid/ipts/eds1.c | 1 + + drivers/hid/ipts/eds2.c | 1 + + drivers/hid/ipts/receiver.c | 1 + + 3 files changed, 3 insertions(+) + +diff --git a/drivers/hid/ipts/eds1.c b/drivers/hid/ipts/eds1.c +index ecbb3a8bdaf6..7b9f54388a9f 100644 +--- a/drivers/hid/ipts/eds1.c ++++ b/drivers/hid/ipts/eds1.c +@@ -14,6 +14,7 @@ + #include "context.h" + #include "control.h" + #include "desc.h" ++#include "eds1.h" + #include "spec-device.h" + + int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size) +diff --git a/drivers/hid/ipts/eds2.c b/drivers/hid/ipts/eds2.c +index 198dc65d7887..639940794615 100644 +--- a/drivers/hid/ipts/eds2.c ++++ b/drivers/hid/ipts/eds2.c +@@ -15,6 +15,7 @@ + #include "context.h" + #include "control.h" + #include "desc.h" ++#include "eds2.h" + #include "spec-data.h" + + int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size) +diff --git a/drivers/hid/ipts/receiver.c b/drivers/hid/ipts/receiver.c +index ef66c3c9db80..977724c728c3 100644 +--- a/drivers/hid/ipts/receiver.c ++++ b/drivers/hid/ipts/receiver.c +@@ -16,6 +16,7 @@ + #include "context.h" + #include "control.h" + #include "hid.h" ++#include "receiver.h" + #include "resources.h" + #include "spec-device.h" + #include "thread.h" +-- +2.44.0 + diff --git a/sys-kernel/surface-sources/files/6.8/0005-ithc.patch b/sys-kernel/surface-sources/files/6.8/0005-ithc.patch index 38aaadb..f9722e9 100644 --- a/sys-kernel/surface-sources/files/6.8/0005-ithc.patch +++ b/sys-kernel/surface-sources/files/6.8/0005-ithc.patch @@ -1818,3 +1818,2523 @@ index 000000000000..028e55a4ec53 -- 2.44.0 +From 62697b8742afc950b55ed24d8ef2fca5834426b6 Mon Sep 17 00:00:00 2001 +From: quo +Date: Fri, 19 Apr 2024 22:11:09 +0200 +Subject: [PATCH] hid: ithc: Update from quo/ithc-linux + + - Added QuickSPI support for Surface Laptop Studio 2 + - Use Latency Tolerance Reporting instead of manual CPU latency adjustments + +Based on: https://github.com/quo/ithc-linux/commit/18afc6ffacd70b49fdee2eb1ab0a8acd159edb31 + +Signed-off-by: Dorian Stoll +Patchset: ithc +--- + drivers/hid/ithc/Kbuild | 2 +- + drivers/hid/ithc/ithc-debug.c | 33 +- + drivers/hid/ithc/ithc-debug.h | 7 + + drivers/hid/ithc/ithc-dma.c | 125 ++----- + drivers/hid/ithc/ithc-dma.h | 24 +- + drivers/hid/ithc/ithc-hid.c | 207 +++++++++++ + drivers/hid/ithc/ithc-hid.h | 32 ++ + drivers/hid/ithc/ithc-legacy.c | 252 ++++++++++++++ + drivers/hid/ithc/ithc-legacy.h | 8 + + drivers/hid/ithc/ithc-main.c | 386 ++++----------------- + drivers/hid/ithc/ithc-quickspi.c | 578 +++++++++++++++++++++++++++++++ + drivers/hid/ithc/ithc-quickspi.h | 39 +++ + drivers/hid/ithc/ithc-regs.c | 72 +++- + drivers/hid/ithc/ithc-regs.h | 143 ++++---- + drivers/hid/ithc/ithc.h | 71 ++-- + 15 files changed, 1441 insertions(+), 538 deletions(-) + create mode 100644 drivers/hid/ithc/ithc-debug.h + create mode 100644 drivers/hid/ithc/ithc-hid.c + create mode 100644 drivers/hid/ithc/ithc-hid.h + create mode 100644 drivers/hid/ithc/ithc-legacy.c + create mode 100644 drivers/hid/ithc/ithc-legacy.h + create mode 100644 drivers/hid/ithc/ithc-quickspi.c + create mode 100644 drivers/hid/ithc/ithc-quickspi.h + +diff --git a/drivers/hid/ithc/Kbuild b/drivers/hid/ithc/Kbuild +index aea83f2ac07b..4937ba131297 100644 +--- a/drivers/hid/ithc/Kbuild ++++ b/drivers/hid/ithc/Kbuild +@@ -1,6 +1,6 @@ + obj-$(CONFIG_HID_ITHC) := ithc.o + +-ithc-objs := ithc-main.o ithc-regs.o ithc-dma.o ithc-debug.o ++ithc-objs := ithc-main.o ithc-regs.o ithc-dma.o ithc-hid.o ithc-legacy.o ithc-quickspi.o ithc-debug.o + + ccflags-y := -std=gnu11 -Wno-declaration-after-statement + +diff --git a/drivers/hid/ithc/ithc-debug.c b/drivers/hid/ithc/ithc-debug.c +index 1f1f1e33f2e5..2d8c6afe9966 100644 +--- a/drivers/hid/ithc/ithc-debug.c ++++ b/drivers/hid/ithc/ithc-debug.c +@@ -85,10 +85,11 @@ static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, si + case 'd': // dma command: cmd len data... + // get report descriptor: d 7 8 0 0 + // enable multitouch: d 3 2 0x0105 +- if (n < 2 || a[1] > (n - 2) * 4) ++ if (n < 1) + return -EINVAL; +- pci_info(ithc->pci, "debug dma command %u with %u bytes of data\n", a[0], a[1]); +- if (ithc_dma_tx(ithc, a[0], a[1], a + 2)) ++ pci_info(ithc->pci, "debug dma command with %u bytes of data\n", n * 4); ++ struct ithc_data data = { .type = ITHC_DATA_RAW, .size = n * 4, .data = a }; ++ if (ithc_dma_tx(ithc, &data)) + pci_err(ithc->pci, "dma tx failed\n"); + break; + default: +@@ -98,6 +99,23 @@ static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, si + return len; + } + ++static struct dentry *dbg_dir; ++ ++void __init ithc_debug_init_module(void) ++{ ++ struct dentry *d = debugfs_create_dir(DEVNAME, NULL); ++ if (IS_ERR(d)) ++ pr_warn("failed to create debugfs dir (%li)\n", PTR_ERR(d)); ++ else ++ dbg_dir = d; ++} ++ ++void __exit ithc_debug_exit_module(void) ++{ ++ debugfs_remove_recursive(dbg_dir); ++ dbg_dir = NULL; ++} ++ + static const struct file_operations ithc_debugfops_cmd = { + .owner = THIS_MODULE, + .write = ithc_debugfs_cmd_write, +@@ -106,17 +124,18 @@ static const struct file_operations ithc_debugfops_cmd = { + static void ithc_debugfs_devres_release(struct device *dev, void *res) + { + struct dentry **dbgm = res; +- if (*dbgm) +- debugfs_remove_recursive(*dbgm); ++ debugfs_remove_recursive(*dbgm); + } + +-int ithc_debug_init(struct ithc *ithc) ++int ithc_debug_init_device(struct ithc *ithc) + { ++ if (!dbg_dir) ++ return -ENOENT; + struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof(*dbgm), GFP_KERNEL); + if (!dbgm) + return -ENOMEM; + devres_add(&ithc->pci->dev, dbgm); +- struct dentry *dbg = debugfs_create_dir(DEVNAME, NULL); ++ struct dentry *dbg = debugfs_create_dir(pci_name(ithc->pci), dbg_dir); + if (IS_ERR(dbg)) + return PTR_ERR(dbg); + *dbgm = dbg; +diff --git a/drivers/hid/ithc/ithc-debug.h b/drivers/hid/ithc/ithc-debug.h +new file mode 100644 +index 000000000000..38c53d916bdb +--- /dev/null ++++ b/drivers/hid/ithc/ithc-debug.h +@@ -0,0 +1,7 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ ++ ++void ithc_debug_init_module(void); ++void ithc_debug_exit_module(void); ++int ithc_debug_init_device(struct ithc *ithc); ++void ithc_log_regs(struct ithc *ithc); ++ +diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c +index ffb8689b8a78..bf4eab33062b 100644 +--- a/drivers/hid/ithc/ithc-dma.c ++++ b/drivers/hid/ithc/ithc-dma.c +@@ -173,10 +173,9 @@ int ithc_dma_rx_init(struct ithc *ithc, u8 channel) + mutex_init(&rx->mutex); + + // Allocate buffers. +- u32 buf_size = DEVCFG_DMA_RX_SIZE(ithc->config.dma_buf_sizes); +- unsigned int num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE; ++ unsigned int num_pages = (ithc->max_rx_size + PAGE_SIZE - 1) / PAGE_SIZE; + pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n", +- NUM_RX_BUF, buf_size, num_pages); ++ NUM_RX_BUF, ithc->max_rx_size, num_pages); + CHECK_RET(ithc_dma_prd_alloc, ithc, &rx->prds, NUM_RX_BUF, num_pages, DMA_FROM_DEVICE); + for (unsigned int i = 0; i < NUM_RX_BUF; i++) + CHECK_RET(ithc_dma_data_alloc, ithc, &rx->prds, &rx->bufs[i]); +@@ -214,10 +213,9 @@ int ithc_dma_tx_init(struct ithc *ithc) + mutex_init(&tx->mutex); + + // Allocate buffers. +- tx->max_size = DEVCFG_DMA_TX_SIZE(ithc->config.dma_buf_sizes); +- unsigned int num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE; ++ unsigned int num_pages = (ithc->max_tx_size + PAGE_SIZE - 1) / PAGE_SIZE; + pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n", +- tx->max_size, num_pages); ++ ithc->max_tx_size, num_pages); + CHECK_RET(ithc_dma_prd_alloc, ithc, &tx->prds, 1, num_pages, DMA_TO_DEVICE); + CHECK_RET(ithc_dma_data_alloc, ithc, &tx->prds, &tx->buf); + +@@ -230,71 +228,6 @@ int ithc_dma_tx_init(struct ithc *ithc) + return 0; + } + +-static int ithc_dma_rx_process_buf(struct ithc *ithc, struct ithc_dma_data_buffer *data, +- u8 channel, u8 buf) +-{ +- if (buf >= NUM_RX_BUF) { +- pci_err(ithc->pci, "invalid dma ringbuffer index\n"); +- return -EINVAL; +- } +- u32 len = data->data_size; +- struct ithc_dma_rx_header *hdr = data->addr; +- u8 *hiddata = (void *)(hdr + 1); +- if (len >= sizeof(*hdr) && hdr->code == DMA_RX_CODE_RESET) { +- // The THC sends a reset request when we need to reinitialize the device. +- // This usually only happens if we send an invalid command or put the device +- // in a bad state. +- CHECK(ithc_reset, ithc); +- } else if (len < sizeof(*hdr) || len != sizeof(*hdr) + hdr->data_size) { +- if (hdr->code == DMA_RX_CODE_INPUT_REPORT) { +- // When the CPU enters a low power state during DMA, we can get truncated +- // messages. For Surface devices, this will typically be a single touch +- // report that is only 1 byte, or a multitouch report that is 257 bytes. +- // See also ithc_set_active(). +- } else { +- pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u, code %u, data size %u\n", +- channel, buf, len, hdr->code, hdr->data_size); +- print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, +- hdr, min(len, 0x400u), 0); +- } +- } else if (hdr->code == DMA_RX_CODE_REPORT_DESCRIPTOR && hdr->data_size > 8) { +- // Response to a 'get report descriptor' request. +- // The actual descriptor is preceded by 8 nul bytes. +- CHECK(hid_parse_report, ithc->hid, hiddata + 8, hdr->data_size - 8); +- WRITE_ONCE(ithc->hid_parse_done, true); +- wake_up(&ithc->wait_hid_parse); +- } else if (hdr->code == DMA_RX_CODE_INPUT_REPORT) { +- // Standard HID input report containing touch data. +- CHECK(hid_input_report, ithc->hid, HID_INPUT_REPORT, hiddata, hdr->data_size, 1); +- } else if (hdr->code == DMA_RX_CODE_FEATURE_REPORT) { +- // Response to a 'get feature' request. +- bool done = false; +- mutex_lock(&ithc->hid_get_feature_mutex); +- if (ithc->hid_get_feature_buf) { +- if (hdr->data_size < ithc->hid_get_feature_size) +- ithc->hid_get_feature_size = hdr->data_size; +- memcpy(ithc->hid_get_feature_buf, hiddata, ithc->hid_get_feature_size); +- ithc->hid_get_feature_buf = NULL; +- done = true; +- } +- mutex_unlock(&ithc->hid_get_feature_mutex); +- if (done) { +- wake_up(&ithc->wait_hid_get_feature); +- } else { +- // Received data without a matching request, or the request already +- // timed out. (XXX What's the correct thing to do here?) +- CHECK(hid_input_report, ithc->hid, HID_FEATURE_REPORT, +- hiddata, hdr->data_size, 1); +- } +- } else { +- pci_dbg(ithc->pci, "unhandled dma rx data! channel %u, buffer %u, size %u, code %u\n", +- channel, buf, len, hdr->code); +- print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, +- hdr, min(len, 0x400u), 0); +- } +- return 0; +-} +- + static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) + { + // Process all filled RX buffers from the ringbuffer. +@@ -316,7 +249,16 @@ static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) + rx->num_received = ++n; + + // process data +- CHECK(ithc_dma_rx_process_buf, ithc, b, channel, tail); ++ struct ithc_data d; ++ if ((ithc->use_quickspi ? ithc_quickspi_decode_rx : ithc_legacy_decode_rx) ++ (ithc, b->addr, b->data_size, &d) < 0) { ++ pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u: %*ph\n", ++ channel, tail, b->data_size, min((int)b->data_size, 64), b->addr); ++ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, ++ b->addr, min(b->data_size, 0x400u), 0); ++ } else { ++ ithc_hid_process_data(ithc, &d); ++ } + + // give the buffer back to the device + CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, b, tail); +@@ -331,31 +273,28 @@ int ithc_dma_rx(struct ithc *ithc, u8 channel) + return ret; + } + +-static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) ++static int ithc_dma_tx_unlocked(struct ithc *ithc, const struct ithc_data *data) + { +- ithc_set_active(ithc, 100 * USEC_PER_MSEC); +- + // Send a single TX buffer to the THC. +- pci_dbg(ithc->pci, "dma tx command %u, size %u\n", cmdcode, datasize); +- struct ithc_dma_tx_header *hdr; +- // Data must be padded to next 4-byte boundary. +- u8 padding = datasize & 3 ? 4 - (datasize & 3) : 0; +- unsigned int fullsize = sizeof(*hdr) + datasize + padding; +- if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE) +- return -EINVAL; ++ pci_dbg(ithc->pci, "dma tx data type %u, size %u\n", data->type, data->size); + CHECK_RET(ithc_dma_data_buffer_get, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + + // Fill the TX buffer with header and data. +- ithc->dma_tx.buf.data_size = fullsize; +- hdr = ithc->dma_tx.buf.addr; +- hdr->code = cmdcode; +- hdr->data_size = datasize; +- u8 *dest = (void *)(hdr + 1); +- memcpy(dest, data, datasize); +- dest += datasize; +- for (u8 p = 0; p < padding; p++) +- *dest++ = 0; ++ ssize_t sz; ++ if (data->type == ITHC_DATA_RAW) { ++ sz = min(data->size, ithc->max_tx_size); ++ memcpy(ithc->dma_tx.buf.addr, data->data, sz); ++ } else { ++ sz = (ithc->use_quickspi ? ithc_quickspi_encode_tx : ithc_legacy_encode_tx) ++ (ithc, data, ithc->dma_tx.buf.addr, ithc->max_tx_size); ++ } ++ ithc->dma_tx.buf.data_size = sz < 0 ? 0 : sz; + CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); ++ if (sz < 0) { ++ pci_err(ithc->pci, "failed to encode tx data type %i, size %u, error %i\n", ++ data->type, data->size, (int)sz); ++ return -EINVAL; ++ } + + // Let the THC process the buffer. + bitsb_set(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND); +@@ -363,10 +302,10 @@ static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, vo + writel(DMA_TX_STATUS_DONE, &ithc->regs->dma_tx.status); + return 0; + } +-int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) ++int ithc_dma_tx(struct ithc *ithc, const struct ithc_data *data) + { + mutex_lock(&ithc->dma_tx.mutex); +- int ret = ithc_dma_tx_unlocked(ithc, cmdcode, datasize, data); ++ int ret = ithc_dma_tx_unlocked(ithc, data); + mutex_unlock(&ithc->dma_tx.mutex); + return ret; + } +diff --git a/drivers/hid/ithc/ithc-dma.h b/drivers/hid/ithc/ithc-dma.h +index 93652e4476bf..1749a5819b3e 100644 +--- a/drivers/hid/ithc/ithc-dma.h ++++ b/drivers/hid/ithc/ithc-dma.h +@@ -11,27 +11,6 @@ struct ithc_phys_region_desc { + u32 unused; + }; + +-#define DMA_RX_CODE_INPUT_REPORT 3 +-#define DMA_RX_CODE_FEATURE_REPORT 4 +-#define DMA_RX_CODE_REPORT_DESCRIPTOR 5 +-#define DMA_RX_CODE_RESET 7 +- +-struct ithc_dma_rx_header { +- u32 code; +- u32 data_size; +- u32 _unknown[14]; +-}; +- +-#define DMA_TX_CODE_SET_FEATURE 3 +-#define DMA_TX_CODE_GET_FEATURE 4 +-#define DMA_TX_CODE_OUTPUT_REPORT 5 +-#define DMA_TX_CODE_GET_REPORT_DESCRIPTOR 7 +- +-struct ithc_dma_tx_header { +- u32 code; +- u32 data_size; +-}; +- + struct ithc_dma_prd_buffer { + void *addr; + dma_addr_t dma_addr; +@@ -49,7 +28,6 @@ struct ithc_dma_data_buffer { + + struct ithc_dma_tx { + struct mutex mutex; +- u32 max_size; + struct ithc_dma_prd_buffer prds; + struct ithc_dma_data_buffer buf; + }; +@@ -65,5 +43,5 @@ int ithc_dma_rx_init(struct ithc *ithc, u8 channel); + void ithc_dma_rx_enable(struct ithc *ithc, u8 channel); + int ithc_dma_tx_init(struct ithc *ithc); + int ithc_dma_rx(struct ithc *ithc, u8 channel); +-int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *cmddata); ++int ithc_dma_tx(struct ithc *ithc, const struct ithc_data *data); + +diff --git a/drivers/hid/ithc/ithc-hid.c b/drivers/hid/ithc/ithc-hid.c +new file mode 100644 +index 000000000000..065646ab499e +--- /dev/null ++++ b/drivers/hid/ithc/ithc-hid.c +@@ -0,0 +1,207 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause ++ ++#include "ithc.h" ++ ++static int ithc_hid_start(struct hid_device *hdev) { return 0; } ++static void ithc_hid_stop(struct hid_device *hdev) { } ++static int ithc_hid_open(struct hid_device *hdev) { return 0; } ++static void ithc_hid_close(struct hid_device *hdev) { } ++ ++static int ithc_hid_parse(struct hid_device *hdev) ++{ ++ struct ithc *ithc = hdev->driver_data; ++ const struct ithc_data get_report_desc = { .type = ITHC_DATA_REPORT_DESCRIPTOR }; ++ WRITE_ONCE(ithc->hid.parse_done, false); ++ for (int retries = 0; ; retries++) { ++ ithc_log_regs(ithc); ++ CHECK_RET(ithc_dma_tx, ithc, &get_report_desc); ++ if (wait_event_timeout(ithc->hid.wait_parse, READ_ONCE(ithc->hid.parse_done), ++ msecs_to_jiffies(200))) { ++ ithc_log_regs(ithc); ++ return 0; ++ } ++ if (retries > 5) { ++ ithc_log_regs(ithc); ++ pci_err(ithc->pci, "failed to read report descriptor\n"); ++ return -ETIMEDOUT; ++ } ++ pci_warn(ithc->pci, "failed to read report descriptor, retrying\n"); ++ } ++} ++ ++static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, ++ size_t len, unsigned char rtype, int reqtype) ++{ ++ struct ithc *ithc = hdev->driver_data; ++ if (!buf || !len) ++ return -EINVAL; ++ ++ struct ithc_data d = { .size = len, .data = buf }; ++ buf[0] = reportnum; ++ ++ if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) { ++ d.type = ITHC_DATA_OUTPUT_REPORT; ++ CHECK_RET(ithc_dma_tx, ithc, &d); ++ return 0; ++ } ++ ++ if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) { ++ d.type = ITHC_DATA_SET_FEATURE; ++ CHECK_RET(ithc_dma_tx, ithc, &d); ++ return 0; ++ } ++ ++ if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) { ++ d.type = ITHC_DATA_GET_FEATURE; ++ d.data = &reportnum; ++ d.size = 1; ++ ++ // Prepare for response. ++ mutex_lock(&ithc->hid.get_feature_mutex); ++ ithc->hid.get_feature_buf = buf; ++ ithc->hid.get_feature_size = len; ++ mutex_unlock(&ithc->hid.get_feature_mutex); ++ ++ // Transmit 'get feature' request. ++ int r = CHECK(ithc_dma_tx, ithc, &d); ++ if (!r) { ++ r = wait_event_interruptible_timeout(ithc->hid.wait_get_feature, ++ !ithc->hid.get_feature_buf, msecs_to_jiffies(1000)); ++ if (!r) ++ r = -ETIMEDOUT; ++ else if (r < 0) ++ r = -EINTR; ++ else ++ r = 0; ++ } ++ ++ // If everything went ok, the buffer has been filled with the response data. ++ // Return the response size. ++ mutex_lock(&ithc->hid.get_feature_mutex); ++ ithc->hid.get_feature_buf = NULL; ++ if (!r) ++ r = ithc->hid.get_feature_size; ++ mutex_unlock(&ithc->hid.get_feature_mutex); ++ return r; ++ } ++ ++ pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", ++ rtype, reqtype, reportnum); ++ return -EINVAL; ++} ++ ++// FIXME hid_input_report()/hid_parse_report() currently don't take const buffers, so we have to ++// cast away the const to avoid a compiler warning... ++#define NOCONST(x) ((void *)x) ++ ++void ithc_hid_process_data(struct ithc *ithc, struct ithc_data *d) ++{ ++ WARN_ON(!ithc->hid.dev); ++ if (!ithc->hid.dev) ++ return; ++ ++ switch (d->type) { ++ ++ case ITHC_DATA_IGNORE: ++ return; ++ ++ case ITHC_DATA_ERROR: ++ CHECK(ithc_reset, ithc); ++ return; ++ ++ case ITHC_DATA_REPORT_DESCRIPTOR: ++ // Response to the report descriptor request sent by ithc_hid_parse(). ++ CHECK(hid_parse_report, ithc->hid.dev, NOCONST(d->data), d->size); ++ WRITE_ONCE(ithc->hid.parse_done, true); ++ wake_up(&ithc->hid.wait_parse); ++ return; ++ ++ case ITHC_DATA_INPUT_REPORT: ++ { ++ // Standard HID input report. ++ int r = hid_input_report(ithc->hid.dev, HID_INPUT_REPORT, NOCONST(d->data), d->size, 1); ++ if (r < 0) { ++ pci_warn(ithc->pci, "hid_input_report failed with %i (size %u, report ID 0x%02x)\n", ++ r, d->size, d->size ? *(u8 *)d->data : 0); ++ print_hex_dump_debug(DEVNAME " report: ", DUMP_PREFIX_OFFSET, 32, 1, ++ d->data, min(d->size, 0x400u), 0); ++ } ++ return; ++ } ++ ++ case ITHC_DATA_GET_FEATURE: ++ { ++ // Response to a 'get feature' request sent by ithc_hid_raw_request(). ++ bool done = false; ++ mutex_lock(&ithc->hid.get_feature_mutex); ++ if (ithc->hid.get_feature_buf) { ++ if (d->size < ithc->hid.get_feature_size) ++ ithc->hid.get_feature_size = d->size; ++ memcpy(ithc->hid.get_feature_buf, d->data, ithc->hid.get_feature_size); ++ ithc->hid.get_feature_buf = NULL; ++ done = true; ++ } ++ mutex_unlock(&ithc->hid.get_feature_mutex); ++ if (done) { ++ wake_up(&ithc->hid.wait_get_feature); ++ } else { ++ // Received data without a matching request, or the request already ++ // timed out. (XXX What's the correct thing to do here?) ++ CHECK(hid_input_report, ithc->hid.dev, HID_FEATURE_REPORT, ++ NOCONST(d->data), d->size, 1); ++ } ++ return; ++ } ++ ++ default: ++ pci_err(ithc->pci, "unhandled data type %i\n", d->type); ++ return; ++ } ++} ++ ++static struct hid_ll_driver ithc_ll_driver = { ++ .start = ithc_hid_start, ++ .stop = ithc_hid_stop, ++ .open = ithc_hid_open, ++ .close = ithc_hid_close, ++ .parse = ithc_hid_parse, ++ .raw_request = ithc_hid_raw_request, ++}; ++ ++static void ithc_hid_devres_release(struct device *dev, void *res) ++{ ++ struct hid_device **hidm = res; ++ if (*hidm) ++ hid_destroy_device(*hidm); ++} ++ ++int ithc_hid_init(struct ithc *ithc) ++{ ++ struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof(*hidm), GFP_KERNEL); ++ if (!hidm) ++ return -ENOMEM; ++ devres_add(&ithc->pci->dev, hidm); ++ struct hid_device *hid = hid_allocate_device(); ++ if (IS_ERR(hid)) ++ return PTR_ERR(hid); ++ *hidm = hid; ++ ++ strscpy(hid->name, DEVFULLNAME, sizeof(hid->name)); ++ strscpy(hid->phys, ithc->phys, sizeof(hid->phys)); ++ hid->ll_driver = &ithc_ll_driver; ++ hid->bus = BUS_PCI; ++ hid->vendor = ithc->vendor_id; ++ hid->product = ithc->product_id; ++ hid->version = 0x100; ++ hid->dev.parent = &ithc->pci->dev; ++ hid->driver_data = ithc; ++ ++ ithc->hid.dev = hid; ++ ++ init_waitqueue_head(&ithc->hid.wait_parse); ++ init_waitqueue_head(&ithc->hid.wait_get_feature); ++ mutex_init(&ithc->hid.get_feature_mutex); ++ ++ return 0; ++} ++ +diff --git a/drivers/hid/ithc/ithc-hid.h b/drivers/hid/ithc/ithc-hid.h +new file mode 100644 +index 000000000000..599eb912c8c8 +--- /dev/null ++++ b/drivers/hid/ithc/ithc-hid.h +@@ -0,0 +1,32 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ ++ ++enum ithc_data_type { ++ ITHC_DATA_IGNORE, ++ ITHC_DATA_RAW, ++ ITHC_DATA_ERROR, ++ ITHC_DATA_REPORT_DESCRIPTOR, ++ ITHC_DATA_INPUT_REPORT, ++ ITHC_DATA_OUTPUT_REPORT, ++ ITHC_DATA_GET_FEATURE, ++ ITHC_DATA_SET_FEATURE, ++}; ++ ++struct ithc_data { ++ enum ithc_data_type type; ++ u32 size; ++ const void *data; ++}; ++ ++struct ithc_hid { ++ struct hid_device *dev; ++ bool parse_done; ++ wait_queue_head_t wait_parse; ++ wait_queue_head_t wait_get_feature; ++ struct mutex get_feature_mutex; ++ void *get_feature_buf; ++ size_t get_feature_size; ++}; ++ ++int ithc_hid_init(struct ithc *ithc); ++void ithc_hid_process_data(struct ithc *ithc, struct ithc_data *d); ++ +diff --git a/drivers/hid/ithc/ithc-legacy.c b/drivers/hid/ithc/ithc-legacy.c +new file mode 100644 +index 000000000000..5c1da11e3f1d +--- /dev/null ++++ b/drivers/hid/ithc/ithc-legacy.c +@@ -0,0 +1,252 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause ++ ++#include "ithc.h" ++ ++#define DEVCFG_DMA_RX_SIZE(x) ((((x) & 0x3fff) + 1) << 6) ++#define DEVCFG_DMA_TX_SIZE(x) (((((x) >> 14) & 0x3ff) + 1) << 6) ++ ++#define DEVCFG_TOUCH_MASK 0x3f ++#define DEVCFG_TOUCH_ENABLE BIT(0) ++#define DEVCFG_TOUCH_PROP_DATA_ENABLE BIT(1) ++#define DEVCFG_TOUCH_HID_REPORT_ENABLE BIT(2) ++#define DEVCFG_TOUCH_POWER_STATE(x) (((x) & 7) << 3) ++#define DEVCFG_TOUCH_UNKNOWN_6 BIT(6) ++ ++#define DEVCFG_DEVICE_ID_TIC 0x43495424 // "$TIC" ++ ++#define DEVCFG_SPI_CLKDIV(x) (((x) >> 1) & 7) ++#define DEVCFG_SPI_CLKDIV_8 BIT(4) ++#define DEVCFG_SPI_SUPPORTS_SINGLE BIT(5) ++#define DEVCFG_SPI_SUPPORTS_DUAL BIT(6) ++#define DEVCFG_SPI_SUPPORTS_QUAD BIT(7) ++#define DEVCFG_SPI_MAX_TOUCH_POINTS(x) (((x) >> 8) & 0x3f) ++#define DEVCFG_SPI_MIN_RESET_TIME(x) (((x) >> 16) & 0xf) ++#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) // TODO implement heartbeat ++#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((x) >> 21) & 7) ++#define DEVCFG_SPI_UNKNOWN_25 BIT(25) ++#define DEVCFG_SPI_UNKNOWN_26 BIT(26) ++#define DEVCFG_SPI_UNKNOWN_27 BIT(27) ++#define DEVCFG_SPI_DELAY(x) (((x) >> 28) & 7) // TODO use this ++#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this? ++ ++struct ithc_device_config { // (Example values are from an SP7+.) ++ u32 irq_cause; // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET) ++ u32 error; // 04 = 0x00000000 ++ u32 dma_buf_sizes; // 08 = 0x000a00ff ++ u32 touch_cfg; // 0c = 0x0000001c ++ u32 touch_state; // 10 = 0x0000001c ++ u32 device_id; // 14 = 0x43495424 = "$TIC" ++ u32 spi_config; // 18 = 0xfda00a2e ++ u16 vendor_id; // 1c = 0x045e = Microsoft Corp. ++ u16 product_id; // 1e = 0x0c1a ++ u32 revision; // 20 = 0x00000001 ++ u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139 (this value looks more random on newer devices) ++ u32 command; // 28 = 0x00000000 ++ u32 fw_mode; // 2c = 0x00000000 (for fw update?) ++ u32 _unknown_30; // 30 = 0x00000000 ++ u8 eds_minor_ver; // 34 = 0x5e ++ u8 eds_major_ver; // 35 = 0x03 ++ u8 interface_rev; // 36 = 0x04 ++ u8 eu_kernel_ver; // 37 = 0x04 ++ u32 _unknown_38; // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET) ++ u32 _unknown_3c; // 3c = 0x00000002 ++}; ++static_assert(sizeof(struct ithc_device_config) == 64); ++ ++#define RX_CODE_INPUT_REPORT 3 ++#define RX_CODE_FEATURE_REPORT 4 ++#define RX_CODE_REPORT_DESCRIPTOR 5 ++#define RX_CODE_RESET 7 ++ ++#define TX_CODE_SET_FEATURE 3 ++#define TX_CODE_GET_FEATURE 4 ++#define TX_CODE_OUTPUT_REPORT 5 ++#define TX_CODE_GET_REPORT_DESCRIPTOR 7 ++ ++static int ithc_set_device_enabled(struct ithc *ithc, bool enable) ++{ ++ u32 x = ithc->legacy_touch_cfg = ++ (ithc->legacy_touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | ++ DEVCFG_TOUCH_HID_REPORT_ENABLE | ++ (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_POWER_STATE(3) : 0); ++ return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, ++ offsetof(struct ithc_device_config, touch_cfg), sizeof(x), &x); ++} ++ ++int ithc_legacy_init(struct ithc *ithc) ++{ ++ // Since we don't yet know which SPI config the device wants, use default speed and mode ++ // initially for reading config data. ++ CHECK(ithc_set_spi_config, ithc, 2, true, SPI_MODE_SINGLE, SPI_MODE_SINGLE); ++ ++ // Setting the following bit seems to make reading the config more reliable. ++ bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31); ++ ++ // Setting this bit may be necessary on some ADL devices. ++ switch (ithc->pci->device) { ++ case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1: ++ case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2: ++ case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1: ++ case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2: ++ bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_5); ++ break; ++ } ++ ++ // Take the touch device out of reset. ++ bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0); ++ CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, 0); ++ for (int retries = 0; ; retries++) { ++ ithc_log_regs(ithc); ++ bitsl_set(&ithc->regs->control_bits, CONTROL_NRESET); ++ if (!waitl(ithc, &ithc->regs->irq_cause, 0xf, 2)) ++ break; ++ if (retries > 5) { ++ pci_err(ithc->pci, "failed to reset device, irq_cause = 0x%08x\n", ++ readl(&ithc->regs->irq_cause)); ++ return -ETIMEDOUT; ++ } ++ pci_warn(ithc->pci, "invalid irq_cause, retrying reset\n"); ++ bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0); ++ if (msleep_interruptible(1000)) ++ return -EINTR; ++ } ++ ithc_log_regs(ithc); ++ ++ CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_READY, DMA_RX_STATUS_READY); ++ ++ // Read configuration data. ++ u32 spi_cfg; ++ for (int retries = 0; ; retries++) { ++ ithc_log_regs(ithc); ++ struct ithc_device_config config = { 0 }; ++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof(config), &config); ++ u32 *p = (void *)&config; ++ pci_info(ithc->pci, "config: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", ++ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); ++ if (config.device_id == DEVCFG_DEVICE_ID_TIC) { ++ spi_cfg = config.spi_config; ++ ithc->vendor_id = config.vendor_id; ++ ithc->product_id = config.product_id; ++ ithc->product_rev = config.revision; ++ ithc->max_rx_size = DEVCFG_DMA_RX_SIZE(config.dma_buf_sizes); ++ ithc->max_tx_size = DEVCFG_DMA_TX_SIZE(config.dma_buf_sizes); ++ ithc->legacy_touch_cfg = config.touch_cfg; ++ ithc->have_config = true; ++ break; ++ } ++ if (retries > 10) { ++ pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", ++ config.device_id); ++ return -EIO; ++ } ++ pci_warn(ithc->pci, "failed to read config, retrying\n"); ++ if (msleep_interruptible(100)) ++ return -EINTR; ++ } ++ ithc_log_regs(ithc); ++ ++ // Apply SPI config and enable touch device. ++ CHECK_RET(ithc_set_spi_config, ithc, ++ DEVCFG_SPI_CLKDIV(spi_cfg), (spi_cfg & DEVCFG_SPI_CLKDIV_8) != 0, ++ spi_cfg & DEVCFG_SPI_SUPPORTS_QUAD ? SPI_MODE_QUAD : ++ spi_cfg & DEVCFG_SPI_SUPPORTS_DUAL ? SPI_MODE_DUAL : ++ SPI_MODE_SINGLE, ++ SPI_MODE_SINGLE); ++ CHECK_RET(ithc_set_device_enabled, ithc, true); ++ ithc_log_regs(ithc); ++ return 0; ++} ++ ++void ithc_legacy_exit(struct ithc *ithc) ++{ ++ CHECK(ithc_set_device_enabled, ithc, false); ++} ++ ++int ithc_legacy_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest) ++{ ++ const struct { ++ u32 code; ++ u32 data_size; ++ u32 _unknown[14]; ++ } *hdr = src; ++ ++ if (len < sizeof(*hdr)) ++ return -ENODATA; ++ // Note: RX data is not padded, even though TX data must be padded. ++ if (len != sizeof(*hdr) + hdr->data_size) ++ return -EMSGSIZE; ++ ++ dest->data = hdr + 1; ++ dest->size = hdr->data_size; ++ ++ switch (hdr->code) { ++ case RX_CODE_RESET: ++ // The THC sends a reset request when we need to reinitialize the device. ++ // This usually only happens if we send an invalid command or put the device ++ // in a bad state. ++ dest->type = ITHC_DATA_ERROR; ++ return 0; ++ case RX_CODE_REPORT_DESCRIPTOR: ++ // The descriptor is preceded by 8 nul bytes. ++ if (hdr->data_size < 8) ++ return -ENODATA; ++ dest->type = ITHC_DATA_REPORT_DESCRIPTOR; ++ dest->data = (char *)(hdr + 1) + 8; ++ dest->size = hdr->data_size - 8; ++ return 0; ++ case RX_CODE_INPUT_REPORT: ++ dest->type = ITHC_DATA_INPUT_REPORT; ++ return 0; ++ case RX_CODE_FEATURE_REPORT: ++ dest->type = ITHC_DATA_GET_FEATURE; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++ssize_t ithc_legacy_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, ++ size_t maxlen) ++{ ++ struct { ++ u32 code; ++ u32 data_size; ++ } *hdr = dest; ++ ++ size_t src_size = src->size; ++ const void *src_data = src->data; ++ const u64 get_report_desc_data = 0; ++ u32 code; ++ ++ switch (src->type) { ++ case ITHC_DATA_SET_FEATURE: ++ code = TX_CODE_SET_FEATURE; ++ break; ++ case ITHC_DATA_GET_FEATURE: ++ code = TX_CODE_GET_FEATURE; ++ break; ++ case ITHC_DATA_OUTPUT_REPORT: ++ code = TX_CODE_OUTPUT_REPORT; ++ break; ++ case ITHC_DATA_REPORT_DESCRIPTOR: ++ code = TX_CODE_GET_REPORT_DESCRIPTOR; ++ src_size = sizeof(get_report_desc_data); ++ src_data = &get_report_desc_data; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ // Data must be padded to next 4-byte boundary. ++ size_t padded = round_up(src_size, 4); ++ if (sizeof(*hdr) + padded > maxlen) ++ return -EOVERFLOW; ++ ++ // Fill the TX buffer with header and data. ++ hdr->code = code; ++ hdr->data_size = src_size; ++ memcpy_and_pad(hdr + 1, padded, src_data, src_size, 0); ++ ++ return sizeof(*hdr) + padded; ++} ++ +diff --git a/drivers/hid/ithc/ithc-legacy.h b/drivers/hid/ithc/ithc-legacy.h +new file mode 100644 +index 000000000000..28d692462072 +--- /dev/null ++++ b/drivers/hid/ithc/ithc-legacy.h +@@ -0,0 +1,8 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ ++ ++int ithc_legacy_init(struct ithc *ithc); ++void ithc_legacy_exit(struct ithc *ithc); ++int ithc_legacy_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest); ++ssize_t ithc_legacy_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, ++ size_t maxlen); ++ +diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c +index 87ed4aa70fda..2acf02e41d40 100644 +--- a/drivers/hid/ithc/ithc-main.c ++++ b/drivers/hid/ithc/ithc-main.c +@@ -5,28 +5,6 @@ + MODULE_DESCRIPTION("Intel Touch Host Controller driver"); + MODULE_LICENSE("Dual BSD/GPL"); + +-// Lakefield +-#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT1 0x98d0 +-#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT2 0x98d1 +-// Tiger Lake +-#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT1 0xa0d0 +-#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT2 0xa0d1 +-#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT1 0x43d0 +-#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT2 0x43d1 +-// Alder Lake +-#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1 0x7ad8 +-#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2 0x7ad9 +-#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1 0x51d0 +-#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2 0x51d1 +-#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1 0x54d0 +-#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2 0x54d1 +-// Raptor Lake +-#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT1 0x7a58 +-#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2 0x7a59 +-// Meteor Lake +-#define PCI_DEVICE_ID_INTEL_THC_MTL_PORT1 0x7e48 +-#define PCI_DEVICE_ID_INTEL_THC_MTL_PORT2 0x7e4a +- + static const struct pci_device_id ithc_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_LKF_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_LKF_PORT2) }, +@@ -66,15 +44,13 @@ static bool ithc_use_rx1 = true; + module_param_named(rx1, ithc_use_rx1, bool, 0); + MODULE_PARM_DESC(rx1, "Use DMA RX channel 1"); + +-// Values below 250 seem to work well on the SP7+. If this is set too high, you may observe cursor stuttering. +-static int ithc_dma_latency_us = 200; +-module_param_named(dma_latency_us, ithc_dma_latency_us, int, 0); +-MODULE_PARM_DESC(dma_latency_us, "Determines the CPU latency QoS value for DMA transfers (in microseconds), -1 to disable latency QoS"); ++static int ithc_active_ltr_us = -1; ++module_param_named(activeltr, ithc_active_ltr_us, int, 0); ++MODULE_PARM_DESC(activeltr, "Active LTR value override (in microseconds)"); + +-// Values above 1700 seem to work well on the SP7+. If this is set too low, you may observe cursor stuttering. +-static unsigned int ithc_dma_early_us = 2000; +-module_param_named(dma_early_us, ithc_dma_early_us, uint, 0); +-MODULE_PARM_DESC(dma_early_us, "Determines how early the CPU latency QoS value is applied before the next expected IRQ (in microseconds)"); ++static int ithc_idle_ltr_us = -1; ++module_param_named(idleltr, ithc_idle_ltr_us, int, 0); ++MODULE_PARM_DESC(idleltr, "Idle LTR value override (in microseconds)"); + + static bool ithc_log_regs_enabled = false; + module_param_named(logregs, ithc_log_regs_enabled, bool, 0); +@@ -82,44 +58,30 @@ MODULE_PARM_DESC(logregs, "Log changes in register values (for debugging)"); + + // Sysfs attributes + +-static bool ithc_is_config_valid(struct ithc *ithc) +-{ +- return ithc->config.device_id == DEVCFG_DEVICE_ID_TIC; +-} +- + static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) + { + struct ithc *ithc = dev_get_drvdata(dev); +- if (!ithc || !ithc_is_config_valid(ithc)) ++ if (!ithc || !ithc->have_config) + return -ENODEV; +- return sprintf(buf, "0x%04x", ithc->config.vendor_id); ++ return sprintf(buf, "0x%04x", ithc->vendor_id); + } + static DEVICE_ATTR_RO(vendor); + static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf) + { + struct ithc *ithc = dev_get_drvdata(dev); +- if (!ithc || !ithc_is_config_valid(ithc)) ++ if (!ithc || !ithc->have_config) + return -ENODEV; +- return sprintf(buf, "0x%04x", ithc->config.product_id); ++ return sprintf(buf, "0x%04x", ithc->product_id); + } + static DEVICE_ATTR_RO(product); + static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) + { + struct ithc *ithc = dev_get_drvdata(dev); +- if (!ithc || !ithc_is_config_valid(ithc)) ++ if (!ithc || !ithc->have_config) + return -ENODEV; +- return sprintf(buf, "%u", ithc->config.revision); ++ return sprintf(buf, "%u", ithc->product_rev); + } + static DEVICE_ATTR_RO(revision); +-static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) +-{ +- struct ithc *ithc = dev_get_drvdata(dev); +- if (!ithc || !ithc_is_config_valid(ithc)) +- return -ENODEV; +- u32 v = ithc->config.fw_version; +- return sprintf(buf, "%i.%i.%i.%i", v >> 24, v >> 16 & 0xff, v >> 8 & 0xff, v & 0xff); +-} +-static DEVICE_ATTR_RO(fw_version); + + static const struct attribute_group *ithc_attribute_groups[] = { + &(const struct attribute_group){ +@@ -128,185 +90,26 @@ static const struct attribute_group *ithc_attribute_groups[] = { + &dev_attr_vendor.attr, + &dev_attr_product.attr, + &dev_attr_revision.attr, +- &dev_attr_fw_version.attr, + NULL + }, + }, + NULL + }; + +-// HID setup +- +-static int ithc_hid_start(struct hid_device *hdev) { return 0; } +-static void ithc_hid_stop(struct hid_device *hdev) { } +-static int ithc_hid_open(struct hid_device *hdev) { return 0; } +-static void ithc_hid_close(struct hid_device *hdev) { } +- +-static int ithc_hid_parse(struct hid_device *hdev) +-{ +- struct ithc *ithc = hdev->driver_data; +- u64 val = 0; +- WRITE_ONCE(ithc->hid_parse_done, false); +- for (int retries = 0; ; retries++) { +- CHECK_RET(ithc_dma_tx, ithc, DMA_TX_CODE_GET_REPORT_DESCRIPTOR, sizeof(val), &val); +- if (wait_event_timeout(ithc->wait_hid_parse, READ_ONCE(ithc->hid_parse_done), +- msecs_to_jiffies(200))) +- return 0; +- if (retries > 5) { +- pci_err(ithc->pci, "failed to read report descriptor\n"); +- return -ETIMEDOUT; +- } +- pci_warn(ithc->pci, "failed to read report descriptor, retrying\n"); +- } +-} +- +-static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, +- size_t len, unsigned char rtype, int reqtype) +-{ +- struct ithc *ithc = hdev->driver_data; +- if (!buf || !len) +- return -EINVAL; +- u32 code; +- if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) { +- code = DMA_TX_CODE_OUTPUT_REPORT; +- } else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) { +- code = DMA_TX_CODE_SET_FEATURE; +- } else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) { +- code = DMA_TX_CODE_GET_FEATURE; +- } else { +- pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", +- rtype, reqtype, reportnum); +- return -EINVAL; +- } +- buf[0] = reportnum; +- +- if (reqtype == HID_REQ_GET_REPORT) { +- // Prepare for response. +- mutex_lock(&ithc->hid_get_feature_mutex); +- ithc->hid_get_feature_buf = buf; +- ithc->hid_get_feature_size = len; +- mutex_unlock(&ithc->hid_get_feature_mutex); +- +- // Transmit 'get feature' request. +- int r = CHECK(ithc_dma_tx, ithc, code, 1, buf); +- if (!r) { +- r = wait_event_interruptible_timeout(ithc->wait_hid_get_feature, +- !ithc->hid_get_feature_buf, msecs_to_jiffies(1000)); +- if (!r) +- r = -ETIMEDOUT; +- else if (r < 0) +- r = -EINTR; +- else +- r = 0; +- } +- +- // If everything went ok, the buffer has been filled with the response data. +- // Return the response size. +- mutex_lock(&ithc->hid_get_feature_mutex); +- ithc->hid_get_feature_buf = NULL; +- if (!r) +- r = ithc->hid_get_feature_size; +- mutex_unlock(&ithc->hid_get_feature_mutex); +- return r; +- } +- +- // 'Set feature', or 'output report'. These don't have a response. +- CHECK_RET(ithc_dma_tx, ithc, code, len, buf); +- return 0; +-} +- +-static struct hid_ll_driver ithc_ll_driver = { +- .start = ithc_hid_start, +- .stop = ithc_hid_stop, +- .open = ithc_hid_open, +- .close = ithc_hid_close, +- .parse = ithc_hid_parse, +- .raw_request = ithc_hid_raw_request, +-}; +- +-static void ithc_hid_devres_release(struct device *dev, void *res) +-{ +- struct hid_device **hidm = res; +- if (*hidm) +- hid_destroy_device(*hidm); +-} +- +-static int ithc_hid_init(struct ithc *ithc) +-{ +- struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof(*hidm), GFP_KERNEL); +- if (!hidm) +- return -ENOMEM; +- devres_add(&ithc->pci->dev, hidm); +- struct hid_device *hid = hid_allocate_device(); +- if (IS_ERR(hid)) +- return PTR_ERR(hid); +- *hidm = hid; +- +- strscpy(hid->name, DEVFULLNAME, sizeof(hid->name)); +- strscpy(hid->phys, ithc->phys, sizeof(hid->phys)); +- hid->ll_driver = &ithc_ll_driver; +- hid->bus = BUS_PCI; +- hid->vendor = ithc->config.vendor_id; +- hid->product = ithc->config.product_id; +- hid->version = 0x100; +- hid->dev.parent = &ithc->pci->dev; +- hid->driver_data = ithc; +- +- ithc->hid = hid; +- return 0; +-} +- + // Interrupts/polling + +-static enum hrtimer_restart ithc_activity_start_timer_callback(struct hrtimer *t) +-{ +- struct ithc *ithc = container_of(t, struct ithc, activity_start_timer); +- ithc_set_active(ithc, ithc_dma_early_us * 2 + USEC_PER_MSEC); +- return HRTIMER_NORESTART; +-} +- +-static enum hrtimer_restart ithc_activity_end_timer_callback(struct hrtimer *t) +-{ +- struct ithc *ithc = container_of(t, struct ithc, activity_end_timer); +- cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE); +- return HRTIMER_NORESTART; +-} +- +-void ithc_set_active(struct ithc *ithc, unsigned int duration_us) +-{ +- if (ithc_dma_latency_us < 0) +- return; +- // When CPU usage is very low, the CPU can enter various low power states (C2-C10). +- // This disrupts DMA, causing truncated DMA messages. ERROR_FLAG_DMA_RX_TIMEOUT will be +- // set when this happens. The amount of truncated messages can become very high, resulting +- // in user-visible effects (laggy/stuttering cursor). To avoid this, we use a CPU latency +- // QoS request to prevent the CPU from entering low power states during touch interactions. +- cpu_latency_qos_update_request(&ithc->activity_qos, ithc_dma_latency_us); +- hrtimer_start_range_ns(&ithc->activity_end_timer, +- ns_to_ktime(duration_us * NSEC_PER_USEC), duration_us * NSEC_PER_USEC, HRTIMER_MODE_REL); +-} +- +-static int ithc_set_device_enabled(struct ithc *ithc, bool enable) +-{ +- u32 x = ithc->config.touch_cfg = +- (ithc->config.touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | DEVCFG_TOUCH_UNKNOWN_2 | +- (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_UNKNOWN_3 | DEVCFG_TOUCH_UNKNOWN_4 : 0); +- return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, +- offsetof(struct ithc_device_config, touch_cfg), sizeof(x), &x); +-} +- + static void ithc_disable_interrupts(struct ithc *ithc) + { + writel(0, &ithc->regs->error_control); + bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_IRQ, 0); +- bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_UNKNOWN_4 | DMA_RX_CONTROL_IRQ_DATA, 0); +- bitsb(&ithc->regs->dma_rx[1].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_UNKNOWN_4 | DMA_RX_CONTROL_IRQ_DATA, 0); ++ bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_READY | DMA_RX_CONTROL_IRQ_DATA, 0); ++ bitsb(&ithc->regs->dma_rx[1].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_READY | DMA_RX_CONTROL_IRQ_DATA, 0); + bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_IRQ, 0); + } + + static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned int channel) + { +- writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA, ++ writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_READY | DMA_RX_STATUS_HAVE_DATA, + &ithc->regs->dma_rx[channel].status); + } + +@@ -325,39 +128,22 @@ static void ithc_process(struct ithc *ithc) + { + ithc_log_regs(ithc); + ++ // The THC automatically transitions from LTR idle to active at the start of a DMA transfer. ++ // It does not appear to automatically go back to idle, so we switch it back here, since ++ // the DMA transfer should be complete. ++ ithc_set_ltr_idle(ithc); ++ + bool rx0 = ithc_use_rx0 && (readl(&ithc->regs->dma_rx[0].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0; + bool rx1 = ithc_use_rx1 && (readl(&ithc->regs->dma_rx[1].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0; + +- // Track time between DMA rx transfers, so we can try to predict when we need to enable CPU latency QoS for the next transfer +- ktime_t t = ktime_get(); +- ktime_t dt = ktime_sub(t, ithc->last_rx_time); +- if (rx0 || rx1) { +- ithc->last_rx_time = t; +- if (dt > ms_to_ktime(100)) { +- ithc->cur_rx_seq_count = 0; +- ithc->cur_rx_seq_errors = 0; +- } +- ithc->cur_rx_seq_count++; +- if (!ithc_use_polling && ithc_dma_latency_us >= 0) { +- // Disable QoS, since the DMA transfer has completed (we re-enable it after a delay below) +- cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE); +- hrtimer_try_to_cancel(&ithc->activity_end_timer); +- } +- } +- + // Read and clear error bits + u32 err = readl(&ithc->regs->error_flags); + if (err) { + writel(err, &ithc->regs->error_flags); + if (err & ~ERROR_FLAG_DMA_RX_TIMEOUT) + pci_err(ithc->pci, "error flags: 0x%08x\n", err); +- if (err & ERROR_FLAG_DMA_RX_TIMEOUT) { +- // Only log an error if we see a significant number of these errors. +- ithc->cur_rx_seq_errors++; +- if (ithc->cur_rx_seq_errors && ithc->cur_rx_seq_errors % 50 == 0 && ithc->cur_rx_seq_errors > ithc->cur_rx_seq_count / 10) +- pci_err(ithc->pci, "High number of DMA RX timeouts/errors (%u/%u, dt=%lldus). Try adjusting dma_early_us and/or dma_latency_us.\n", +- ithc->cur_rx_seq_errors, ithc->cur_rx_seq_count, ktime_to_us(dt)); +- } ++ if (err & ERROR_FLAG_DMA_RX_TIMEOUT) ++ pci_err(ithc->pci, "DMA RX timeout/error (try decreasing activeltr/idleltr if this happens frequently)\n"); + } + + // Process DMA rx +@@ -372,12 +158,6 @@ static void ithc_process(struct ithc *ithc) + ithc_dma_rx(ithc, 1); + } + +- // Start timer to re-enable QoS for next rx, but only if we've seen an ERROR_FLAG_DMA_RX_TIMEOUT +- if ((rx0 || rx1) && !ithc_use_polling && ithc_dma_latency_us >= 0 && ithc->cur_rx_seq_errors > 0) { +- ktime_t expires = ktime_add(t, ktime_sub_us(dt, ithc_dma_early_us)); +- hrtimer_start_range_ns(&ithc->activity_start_timer, expires, 10 * NSEC_PER_USEC, HRTIMER_MODE_ABS); +- } +- + ithc_log_regs(ithc); + } + +@@ -403,12 +183,8 @@ static int ithc_poll_thread(void *arg) + ithc_process(ithc); + // Decrease polling interval to 20ms if we received data, otherwise slowly + // increase it up to 200ms. +- if (n != ithc->dma_rx[1].num_received) { +- ithc_set_active(ithc, 100 * USEC_PER_MSEC); +- sleep = 20; +- } else { +- sleep = min(200u, sleep + (sleep >> 4) + 1); +- } ++ sleep = n != ithc->dma_rx[1].num_received ? 20 ++ : min(200u, sleep + (sleep >> 4) + 1); + msleep_interruptible(sleep); + } + return 0; +@@ -431,73 +207,44 @@ static void ithc_disable(struct ithc *ithc) + + static int ithc_init_device(struct ithc *ithc) + { ++ // Read ACPI config for QuickSPI mode ++ struct ithc_acpi_config cfg = { 0 }; ++ CHECK_RET(ithc_read_acpi_config, ithc, &cfg); ++ if (!cfg.has_config) ++ pci_info(ithc->pci, "no ACPI config, using legacy mode\n"); ++ else ++ ithc_print_acpi_config(ithc, &cfg); ++ ithc->use_quickspi = cfg.has_config; ++ ++ // Shut down device + ithc_log_regs(ithc); + bool was_enabled = (readl(&ithc->regs->control_bits) & CONTROL_NRESET) != 0; + ithc_disable(ithc); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_READY, CONTROL_READY); +- +- // Since we don't yet know which SPI config the device wants, use default speed and mode +- // initially for reading config data. +- ithc_set_spi_config(ithc, 10, 0); +- +- // Setting the following bit seems to make reading the config more reliable. +- bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000); ++ ithc_log_regs(ithc); + + // If the device was previously enabled, wait a bit to make sure it's fully shut down. + if (was_enabled) + if (msleep_interruptible(100)) + return -EINTR; + +- // Take the touch device out of reset. +- bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0); +- CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, 0); +- for (int retries = 0; ; retries++) { +- ithc_log_regs(ithc); +- bitsl_set(&ithc->regs->control_bits, CONTROL_NRESET); +- if (!waitl(ithc, &ithc->regs->state, 0xf, 2)) +- break; +- if (retries > 5) { +- pci_err(ithc->pci, "failed to reset device, state = 0x%08x\n", readl(&ithc->regs->state)); +- return -ETIMEDOUT; +- } +- pci_warn(ithc->pci, "invalid state, retrying reset\n"); +- bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0); +- if (msleep_interruptible(1000)) +- return -EINTR; +- } +- ithc_log_regs(ithc); ++ // Set Latency Tolerance Reporting config. The device will automatically ++ // apply these values depending on whether it is active or idle. ++ // If active value is too high, DMA buffer data can become truncated. ++ // By default, we set the active LTR value to 100us, and idle to 100ms. ++ u64 active_ltr_ns = ithc_active_ltr_us >= 0 ? (u64)ithc_active_ltr_us * 1000 ++ : cfg.has_config && cfg.has_active_ltr ? (u64)cfg.active_ltr << 10 ++ : 100 * 1000; ++ u64 idle_ltr_ns = ithc_idle_ltr_us >= 0 ? (u64)ithc_idle_ltr_us * 1000 ++ : cfg.has_config && cfg.has_idle_ltr ? (u64)cfg.idle_ltr << 10 ++ : 100 * 1000 * 1000; ++ ithc_set_ltr_config(ithc, active_ltr_ns, idle_ltr_ns); ++ ++ if (ithc->use_quickspi) ++ CHECK_RET(ithc_quickspi_init, ithc, &cfg); ++ else ++ CHECK_RET(ithc_legacy_init, ithc); + +- // Waiting for the following status bit makes reading config much more reliable, +- // however the official driver does not seem to do this... +- CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_UNKNOWN_4, DMA_RX_STATUS_UNKNOWN_4); +- +- // Read configuration data. +- for (int retries = 0; ; retries++) { +- ithc_log_regs(ithc); +- memset(&ithc->config, 0, sizeof(ithc->config)); +- CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof(ithc->config), &ithc->config); +- u32 *p = (void *)&ithc->config; +- pci_info(ithc->pci, "config: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", +- p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); +- if (ithc_is_config_valid(ithc)) +- break; +- if (retries > 10) { +- pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", +- ithc->config.device_id); +- return -EIO; +- } +- pci_warn(ithc->pci, "failed to read config, retrying\n"); +- if (msleep_interruptible(100)) +- return -EINTR; +- } +- ithc_log_regs(ithc); +- +- // Apply SPI config and enable touch device. +- CHECK_RET(ithc_set_spi_config, ithc, +- DEVCFG_SPI_MAX_FREQ(ithc->config.spi_config), +- DEVCFG_SPI_MODE(ithc->config.spi_config)); +- CHECK_RET(ithc_set_device_enabled, ithc, true); +- ithc_log_regs(ithc); + return 0; + } + +@@ -527,11 +274,11 @@ static void ithc_stop(void *res) + CHECK(kthread_stop, ithc->poll_thread); + if (ithc->irq >= 0) + disable_irq(ithc->irq); +- CHECK(ithc_set_device_enabled, ithc, false); ++ if (ithc->use_quickspi) ++ ithc_quickspi_exit(ithc); ++ else ++ ithc_legacy_exit(ithc); + ithc_disable(ithc); +- hrtimer_cancel(&ithc->activity_start_timer); +- hrtimer_cancel(&ithc->activity_end_timer); +- cpu_latency_qos_remove_request(&ithc->activity_qos); + + // Clear DMA config. + for (unsigned int i = 0; i < 2; i++) { +@@ -570,9 +317,6 @@ static int ithc_start(struct pci_dev *pci) + ithc->irq = -1; + ithc->pci = pci; + snprintf(ithc->phys, sizeof(ithc->phys), "pci-%s/" DEVNAME, pci_name(pci)); +- init_waitqueue_head(&ithc->wait_hid_parse); +- init_waitqueue_head(&ithc->wait_hid_get_feature); +- mutex_init(&ithc->hid_get_feature_mutex); + pci_set_drvdata(pci, ithc); + CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_clear_drvdata, pci); + if (ithc_log_regs_enabled) +@@ -596,6 +340,9 @@ static int ithc_start(struct pci_dev *pci) + + // Initialize THC and touch device. + CHECK_RET(ithc_init_device, ithc); ++ ++ // Initialize HID and DMA. ++ CHECK_RET(ithc_hid_init, ithc); + CHECK(devm_device_add_groups, &pci->dev, ithc_attribute_groups); + if (ithc_use_rx0) + CHECK_RET(ithc_dma_rx_init, ithc, 0); +@@ -603,18 +350,10 @@ static int ithc_start(struct pci_dev *pci) + CHECK_RET(ithc_dma_rx_init, ithc, 1); + CHECK_RET(ithc_dma_tx_init, ithc); + +- cpu_latency_qos_add_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE); +- hrtimer_init(&ithc->activity_start_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); +- ithc->activity_start_timer.function = ithc_activity_start_timer_callback; +- hrtimer_init(&ithc->activity_end_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +- ithc->activity_end_timer.function = ithc_activity_end_timer_callback; +- + // Add ithc_stop() callback AFTER setting up DMA buffers, so that polling/irqs/DMA are + // disabled BEFORE the buffers are freed. + CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_stop, ithc); + +- CHECK_RET(ithc_hid_init, ithc); +- + // Start polling/IRQ. + if (ithc_use_polling) { + pci_info(pci, "using polling instead of irq\n"); +@@ -637,9 +376,11 @@ static int ithc_start(struct pci_dev *pci) + + // hid_add_device() can only be called after irq/polling is started and DMA is enabled, + // because it calls ithc_hid_parse() which reads the report descriptor via DMA. +- CHECK_RET(hid_add_device, ithc->hid); ++ CHECK_RET(hid_add_device, ithc->hid.dev); ++ ++ CHECK(ithc_debug_init_device, ithc); + +- CHECK(ithc_debug_init, ithc); ++ ithc_set_ltr_idle(ithc); + + pci_dbg(pci, "started\n"); + return 0; +@@ -710,17 +451,20 @@ static struct pci_driver ithc_driver = { + .thaw = ithc_thaw, + .restore = ithc_restore, + }, ++ .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, + //.dev_groups = ithc_attribute_groups, // could use this (since 5.14), however the attributes won't have valid values until config has been read anyway + }; + + static int __init ithc_init(void) + { ++ ithc_debug_init_module(); + return pci_register_driver(&ithc_driver); + } + + static void __exit ithc_exit(void) + { + pci_unregister_driver(&ithc_driver); ++ ithc_debug_exit_module(); + } + + module_init(ithc_init); +diff --git a/drivers/hid/ithc/ithc-quickspi.c b/drivers/hid/ithc/ithc-quickspi.c +new file mode 100644 +index 000000000000..760e55ead078 +--- /dev/null ++++ b/drivers/hid/ithc/ithc-quickspi.c +@@ -0,0 +1,578 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause ++ ++// Some public THC/QuickSPI documentation can be found in: ++// - Intel Firmware Support Package repo: https://github.com/intel/FSP ++// - HID over SPI (HIDSPI) spec: https://www.microsoft.com/en-us/download/details.aspx?id=103325 ++ ++#include "ithc.h" ++ ++static const guid_t guid_hidspi = ++ GUID_INIT(0x6e2ac436, 0x0fcf, 0x41af, 0xa2, 0x65, 0xb3, 0x2a, 0x22, 0x0d, 0xcf, 0xab); ++static const guid_t guid_thc_quickspi = ++ GUID_INIT(0x300d35b7, 0xac20, 0x413e, 0x8e, 0x9c, 0x92, 0xe4, 0xda, 0xfd, 0x0a, 0xfe); ++static const guid_t guid_thc_ltr = ++ GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38); ++ ++// TODO The HIDSPI spec says revision should be 3. Should we try both? ++#define DSM_REV 2 ++ ++struct hidspi_header { ++ u8 type; ++ u16 len; ++ u8 id; ++} __packed; ++static_assert(sizeof(struct hidspi_header) == 4); ++ ++#define HIDSPI_INPUT_TYPE_DATA 1 ++#define HIDSPI_INPUT_TYPE_RESET_RESPONSE 3 ++#define HIDSPI_INPUT_TYPE_COMMAND_RESPONSE 4 ++#define HIDSPI_INPUT_TYPE_GET_FEATURE_RESPONSE 5 ++#define HIDSPI_INPUT_TYPE_DEVICE_DESCRIPTOR 7 ++#define HIDSPI_INPUT_TYPE_REPORT_DESCRIPTOR 8 ++#define HIDSPI_INPUT_TYPE_SET_FEATURE_RESPONSE 9 ++#define HIDSPI_INPUT_TYPE_OUTPUT_REPORT_RESPONSE 10 ++#define HIDSPI_INPUT_TYPE_GET_INPUT_REPORT_RESPONSE 11 ++ ++#define HIDSPI_OUTPUT_TYPE_DEVICE_DESCRIPTOR_REQUEST 1 ++#define HIDSPI_OUTPUT_TYPE_REPORT_DESCRIPTOR_REQUEST 2 ++#define HIDSPI_OUTPUT_TYPE_SET_FEATURE 3 ++#define HIDSPI_OUTPUT_TYPE_GET_FEATURE 4 ++#define HIDSPI_OUTPUT_TYPE_OUTPUT_REPORT 5 ++#define HIDSPI_OUTPUT_TYPE_INPUT_REPORT_REQUEST 6 ++#define HIDSPI_OUTPUT_TYPE_COMMAND 7 ++ ++struct hidspi_device_descriptor { ++ u16 wDeviceDescLength; ++ u16 bcdVersion; ++ u16 wReportDescLength; ++ u16 wMaxInputLength; ++ u16 wMaxOutputLength; ++ u16 wMaxFragmentLength; ++ u16 wVendorID; ++ u16 wProductID; ++ u16 wVersionID; ++ u16 wFlags; ++ u32 dwReserved; ++}; ++static_assert(sizeof(struct hidspi_device_descriptor) == 24); ++ ++static int read_acpi_u32(struct ithc *ithc, const guid_t *guid, u32 func, u32 *dest) ++{ ++ acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); ++ union acpi_object *o = acpi_evaluate_dsm(handle, guid, DSM_REV, func, NULL); ++ if (!o) ++ return 0; ++ if (o->type != ACPI_TYPE_INTEGER) { ++ pci_err(ithc->pci, "DSM %pUl %u returned type %i instead of integer\n", ++ guid, func, o->type); ++ ACPI_FREE(o); ++ return -1; ++ } ++ pci_dbg(ithc->pci, "DSM %pUl %u = 0x%08x\n", guid, func, (u32)o->integer.value); ++ *dest = (u32)o->integer.value; ++ ACPI_FREE(o); ++ return 1; ++} ++ ++static int read_acpi_buf(struct ithc *ithc, const guid_t *guid, u32 func, size_t len, u8 *dest) ++{ ++ acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); ++ union acpi_object *o = acpi_evaluate_dsm(handle, guid, DSM_REV, func, NULL); ++ if (!o) ++ return 0; ++ if (o->type != ACPI_TYPE_BUFFER) { ++ pci_err(ithc->pci, "DSM %pUl %u returned type %i instead of buffer\n", ++ guid, func, o->type); ++ ACPI_FREE(o); ++ return -1; ++ } ++ if (o->buffer.length != len) { ++ pci_err(ithc->pci, "DSM %pUl %u returned len %u instead of %zu\n", ++ guid, func, o->buffer.length, len); ++ ACPI_FREE(o); ++ return -1; ++ } ++ memcpy(dest, o->buffer.pointer, len); ++ pci_dbg(ithc->pci, "DSM %pUl %u = 0x%02x\n", guid, func, dest[0]); ++ ACPI_FREE(o); ++ return 1; ++} ++ ++int ithc_read_acpi_config(struct ithc *ithc, struct ithc_acpi_config *cfg) ++{ ++ int r; ++ acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); ++ ++ cfg->has_config = acpi_check_dsm(handle, &guid_hidspi, DSM_REV, BIT(0)); ++ if (!cfg->has_config) ++ return 0; ++ ++ // HIDSPI settings ++ ++ r = read_acpi_u32(ithc, &guid_hidspi, 1, &cfg->input_report_header_address); ++ if (r < 0) ++ return r; ++ cfg->has_input_report_header_address = r > 0; ++ if (r > 0 && cfg->input_report_header_address > 0xffffff) { ++ pci_err(ithc->pci, "Invalid input report header address 0x%x\n", ++ cfg->input_report_header_address); ++ return -1; ++ } ++ ++ r = read_acpi_u32(ithc, &guid_hidspi, 2, &cfg->input_report_body_address); ++ if (r < 0) ++ return r; ++ cfg->has_input_report_body_address = r > 0; ++ if (r > 0 && cfg->input_report_body_address > 0xffffff) { ++ pci_err(ithc->pci, "Invalid input report body address 0x%x\n", ++ cfg->input_report_body_address); ++ return -1; ++ } ++ ++ r = read_acpi_u32(ithc, &guid_hidspi, 3, &cfg->output_report_body_address); ++ if (r < 0) ++ return r; ++ cfg->has_output_report_body_address = r > 0; ++ if (r > 0 && cfg->output_report_body_address > 0xffffff) { ++ pci_err(ithc->pci, "Invalid output report body address 0x%x\n", ++ cfg->output_report_body_address); ++ return -1; ++ } ++ ++ r = read_acpi_buf(ithc, &guid_hidspi, 4, sizeof(cfg->read_opcode), &cfg->read_opcode); ++ if (r < 0) ++ return r; ++ cfg->has_read_opcode = r > 0; ++ ++ r = read_acpi_buf(ithc, &guid_hidspi, 5, sizeof(cfg->write_opcode), &cfg->write_opcode); ++ if (r < 0) ++ return r; ++ cfg->has_write_opcode = r > 0; ++ ++ u32 flags; ++ r = read_acpi_u32(ithc, &guid_hidspi, 6, &flags); ++ if (r < 0) ++ return r; ++ cfg->has_read_mode = cfg->has_write_mode = r > 0; ++ if (r > 0) { ++ cfg->read_mode = (flags >> 14) & 3; ++ cfg->write_mode = flags & BIT(13) ? cfg->read_mode : SPI_MODE_SINGLE; ++ } ++ ++ // Quick SPI settings ++ ++ r = read_acpi_u32(ithc, &guid_thc_quickspi, 1, &cfg->spi_frequency); ++ if (r < 0) ++ return r; ++ cfg->has_spi_frequency = r > 0; ++ ++ r = read_acpi_u32(ithc, &guid_thc_quickspi, 2, &cfg->limit_packet_size); ++ if (r < 0) ++ return r; ++ cfg->has_limit_packet_size = r > 0; ++ ++ r = read_acpi_u32(ithc, &guid_thc_quickspi, 3, &cfg->tx_delay); ++ if (r < 0) ++ return r; ++ cfg->has_tx_delay = r > 0; ++ if (r > 0) ++ cfg->tx_delay &= 0xffff; ++ ++ // LTR settings ++ ++ r = read_acpi_u32(ithc, &guid_thc_ltr, 1, &cfg->active_ltr); ++ if (r < 0) ++ return r; ++ cfg->has_active_ltr = r > 0; ++ if (r > 0 && (!cfg->active_ltr || cfg->active_ltr > 0x3ff)) { ++ if (cfg->active_ltr != 0xffffffff) ++ pci_warn(ithc->pci, "Ignoring invalid active LTR value 0x%x\n", ++ cfg->active_ltr); ++ cfg->active_ltr = 500; ++ } ++ ++ r = read_acpi_u32(ithc, &guid_thc_ltr, 2, &cfg->idle_ltr); ++ if (r < 0) ++ return r; ++ cfg->has_idle_ltr = r > 0; ++ if (r > 0 && (!cfg->idle_ltr || cfg->idle_ltr > 0x3ff)) { ++ if (cfg->idle_ltr != 0xffffffff) ++ pci_warn(ithc->pci, "Ignoring invalid idle LTR value 0x%x\n", ++ cfg->idle_ltr); ++ cfg->idle_ltr = 500; ++ if (cfg->has_active_ltr && cfg->active_ltr > cfg->idle_ltr) ++ cfg->idle_ltr = cfg->active_ltr; ++ } ++ ++ return 0; ++} ++ ++void ithc_print_acpi_config(struct ithc *ithc, const struct ithc_acpi_config *cfg) ++{ ++ if (!cfg->has_config) { ++ pci_info(ithc->pci, "No ACPI config"); ++ return; ++ } ++ ++ char input_report_header_address[16] = "-"; ++ if (cfg->has_input_report_header_address) ++ sprintf(input_report_header_address, "0x%x", cfg->input_report_header_address); ++ char input_report_body_address[16] = "-"; ++ if (cfg->has_input_report_body_address) ++ sprintf(input_report_body_address, "0x%x", cfg->input_report_body_address); ++ char output_report_body_address[16] = "-"; ++ if (cfg->has_output_report_body_address) ++ sprintf(output_report_body_address, "0x%x", cfg->output_report_body_address); ++ char read_opcode[16] = "-"; ++ if (cfg->has_read_opcode) ++ sprintf(read_opcode, "0x%02x", cfg->read_opcode); ++ char write_opcode[16] = "-"; ++ if (cfg->has_write_opcode) ++ sprintf(write_opcode, "0x%02x", cfg->write_opcode); ++ char read_mode[16] = "-"; ++ if (cfg->has_read_mode) ++ sprintf(read_mode, "%i", cfg->read_mode); ++ char write_mode[16] = "-"; ++ if (cfg->has_write_mode) ++ sprintf(write_mode, "%i", cfg->write_mode); ++ char spi_frequency[16] = "-"; ++ if (cfg->has_spi_frequency) ++ sprintf(spi_frequency, "%u", cfg->spi_frequency); ++ char limit_packet_size[16] = "-"; ++ if (cfg->has_limit_packet_size) ++ sprintf(limit_packet_size, "%u", cfg->limit_packet_size); ++ char tx_delay[16] = "-"; ++ if (cfg->has_tx_delay) ++ sprintf(tx_delay, "%u", cfg->tx_delay); ++ char active_ltr[16] = "-"; ++ if (cfg->has_active_ltr) ++ sprintf(active_ltr, "%u", cfg->active_ltr); ++ char idle_ltr[16] = "-"; ++ if (cfg->has_idle_ltr) ++ sprintf(idle_ltr, "%u", cfg->idle_ltr); ++ ++ pci_info(ithc->pci, "ACPI config: InputHeaderAddr=%s InputBodyAddr=%s OutputBodyAddr=%s ReadOpcode=%s WriteOpcode=%s ReadMode=%s WriteMode=%s Frequency=%s LimitPacketSize=%s TxDelay=%s ActiveLTR=%s IdleLTR=%s\n", ++ input_report_header_address, input_report_body_address, output_report_body_address, ++ read_opcode, write_opcode, read_mode, write_mode, ++ spi_frequency, limit_packet_size, tx_delay, active_ltr, idle_ltr); ++} ++ ++static int ithc_quickspi_init_regs(struct ithc *ithc, const struct ithc_acpi_config *cfg) ++{ ++ pci_dbg(ithc->pci, "initializing QuickSPI registers\n"); ++ ++ // SPI frequency and mode ++ if (!cfg->has_spi_frequency || !cfg->spi_frequency) { ++ pci_err(ithc->pci, "Missing SPI frequency in configuration\n"); ++ return -EINVAL; ++ } ++ unsigned int clkdiv = DIV_ROUND_UP(SPI_CLK_FREQ_BASE, cfg->spi_frequency); ++ bool clkdiv8 = clkdiv > 7; ++ if (clkdiv8) ++ clkdiv = min(7u, DIV_ROUND_UP(clkdiv, 8u)); ++ if (!clkdiv) ++ clkdiv = 1; ++ CHECK_RET(ithc_set_spi_config, ithc, clkdiv, clkdiv8, ++ cfg->has_read_mode ? cfg->read_mode : SPI_MODE_SINGLE, ++ cfg->has_write_mode ? cfg->write_mode : SPI_MODE_SINGLE); ++ ++ // SPI addresses and opcodes ++ if (cfg->has_input_report_header_address) ++ writel(cfg->input_report_header_address, &ithc->regs->spi_header_addr); ++ if (cfg->has_input_report_body_address) ++ writel(cfg->input_report_body_address, &ithc->regs->dma_rx[0].spi_addr); ++ if (cfg->has_output_report_body_address) ++ writel(cfg->output_report_body_address, &ithc->regs->dma_tx.spi_addr); ++ ++ if (cfg->has_read_opcode) { ++ writeb(cfg->read_opcode, &ithc->regs->read_opcode); ++ writeb(cfg->read_opcode, &ithc->regs->read_opcode_single); ++ writeb(cfg->read_opcode, &ithc->regs->read_opcode_dual); ++ writeb(cfg->read_opcode, &ithc->regs->read_opcode_quad); ++ } ++ if (cfg->has_write_opcode) { ++ writeb(cfg->write_opcode, &ithc->regs->write_opcode); ++ writeb(cfg->write_opcode, &ithc->regs->write_opcode_single); ++ writeb(cfg->write_opcode, &ithc->regs->write_opcode_dual); ++ writeb(cfg->write_opcode, &ithc->regs->write_opcode_quad); ++ } ++ ithc_log_regs(ithc); ++ ++ // The rest... ++ bitsl(&ithc->regs->quickspi_config1, ++ QUICKSPI_CONFIG1_UNKNOWN_0(0xff) | QUICKSPI_CONFIG1_UNKNOWN_5(0xff) | ++ QUICKSPI_CONFIG1_UNKNOWN_10(0xff) | QUICKSPI_CONFIG1_UNKNOWN_16(0xffff), ++ QUICKSPI_CONFIG1_UNKNOWN_0(4) | QUICKSPI_CONFIG1_UNKNOWN_5(4) | ++ QUICKSPI_CONFIG1_UNKNOWN_10(22) | QUICKSPI_CONFIG1_UNKNOWN_16(2)); ++ ++ bitsl(&ithc->regs->quickspi_config2, ++ QUICKSPI_CONFIG2_UNKNOWN_0(0xff) | QUICKSPI_CONFIG2_UNKNOWN_5(0xff) | ++ QUICKSPI_CONFIG2_UNKNOWN_12(0xff), ++ QUICKSPI_CONFIG2_UNKNOWN_0(8) | QUICKSPI_CONFIG2_UNKNOWN_5(14) | ++ QUICKSPI_CONFIG2_UNKNOWN_12(2)); ++ ++ u32 pktsize = cfg->has_limit_packet_size && cfg->limit_packet_size == 1 ? 4 : 0x80; ++ bitsl(&ithc->regs->spi_config, ++ SPI_CONFIG_READ_PACKET_SIZE(0xfff) | SPI_CONFIG_WRITE_PACKET_SIZE(0xfff), ++ SPI_CONFIG_READ_PACKET_SIZE(pktsize) | SPI_CONFIG_WRITE_PACKET_SIZE(pktsize)); ++ ++ bitsl_set(&ithc->regs->quickspi_config2, ++ QUICKSPI_CONFIG2_UNKNOWN_16 | QUICKSPI_CONFIG2_UNKNOWN_17); ++ bitsl(&ithc->regs->quickspi_config2, ++ QUICKSPI_CONFIG2_DISABLE_READ_ADDRESS_INCREMENT | ++ QUICKSPI_CONFIG2_DISABLE_WRITE_ADDRESS_INCREMENT | ++ QUICKSPI_CONFIG2_ENABLE_WRITE_STREAMING_MODE, 0); ++ ++ return 0; ++} ++ ++static int wait_for_report(struct ithc *ithc) ++{ ++ CHECK_RET(waitl, ithc, &ithc->regs->dma_rx[0].status, ++ DMA_RX_STATUS_READY, DMA_RX_STATUS_READY); ++ writel(DMA_RX_STATUS_READY, &ithc->regs->dma_rx[0].status); ++ ++ u32 h = readl(&ithc->regs->input_header); ++ ithc_log_regs(ithc); ++ if (INPUT_HEADER_SYNC(h) != INPUT_HEADER_SYNC_VALUE ++ || INPUT_HEADER_VERSION(h) != INPUT_HEADER_VERSION_VALUE) { ++ pci_err(ithc->pci, "invalid input report frame header 0x%08x\n", h); ++ return -ENODATA; ++ } ++ return INPUT_HEADER_REPORT_LENGTH(h) * 4; ++} ++ ++static int ithc_quickspi_init_hidspi(struct ithc *ithc, const struct ithc_acpi_config *cfg) ++{ ++ pci_dbg(ithc->pci, "initializing HIDSPI\n"); ++ ++ // HIDSPI initialization sequence: ++ // "1. The host shall invoke the ACPI reset method to clear the device state." ++ acpi_status s = acpi_evaluate_object(ACPI_HANDLE(&ithc->pci->dev), "_RST", NULL, NULL); ++ if (ACPI_FAILURE(s)) { ++ pci_err(ithc->pci, "ACPI reset failed\n"); ++ return -EIO; ++ } ++ ++ bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0); ++ ++ // "2. Within 1 second, the device shall signal an interrupt and make available to the host ++ // an input report containing a device reset response." ++ int size = wait_for_report(ithc); ++ if (size < 0) ++ return size; ++ if (size < sizeof(struct hidspi_header)) { ++ pci_err(ithc->pci, "SPI data size too small for reset response (%u)\n", size); ++ return -EMSGSIZE; ++ } ++ ++ // "3. The host shall read the reset response from the device at the Input Report addresses ++ // specified in ACPI." ++ u32 in_addr = cfg->has_input_report_body_address ? cfg->input_report_body_address : 0x1000; ++ struct { ++ struct hidspi_header header; ++ union { ++ struct hidspi_device_descriptor device_desc; ++ u32 data[16]; ++ }; ++ } resp = { 0 }; ++ if (size > sizeof(resp)) { ++ pci_err(ithc->pci, "SPI data size for reset response too big (%u)\n", size); ++ return -EMSGSIZE; ++ } ++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, in_addr, size, &resp); ++ if (resp.header.type != HIDSPI_INPUT_TYPE_RESET_RESPONSE) { ++ pci_err(ithc->pci, "received type %i instead of reset response\n", resp.header.type); ++ return -ENOMSG; ++ } ++ ++ // "4. The host shall then write an Output Report to the device at the Output Report Address ++ // specified in ACPI, requesting the Device Descriptor from the device." ++ u32 out_addr = cfg->has_output_report_body_address ? cfg->output_report_body_address : 0x1000; ++ struct hidspi_header req = { .type = HIDSPI_OUTPUT_TYPE_DEVICE_DESCRIPTOR_REQUEST }; ++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_WRITE, out_addr, sizeof(req), &req); ++ ++ // "5. Within 1 second, the device shall signal an interrupt and make available to the host ++ // an input report containing the Device Descriptor." ++ size = wait_for_report(ithc); ++ if (size < 0) ++ return size; ++ if (size < sizeof(resp.header) + sizeof(resp.device_desc)) { ++ pci_err(ithc->pci, "SPI data size too small for device descriptor (%u)\n", size); ++ return -EMSGSIZE; ++ } ++ ++ // "6. The host shall read the Device Descriptor from the Input Report addresses specified ++ // in ACPI." ++ if (size > sizeof(resp)) { ++ pci_err(ithc->pci, "SPI data size for device descriptor too big (%u)\n", size); ++ return -EMSGSIZE; ++ } ++ memset(&resp, 0, sizeof(resp)); ++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, in_addr, size, &resp); ++ if (resp.header.type != HIDSPI_INPUT_TYPE_DEVICE_DESCRIPTOR) { ++ pci_err(ithc->pci, "received type %i instead of device descriptor\n", ++ resp.header.type); ++ return -ENOMSG; ++ } ++ struct hidspi_device_descriptor *d = &resp.device_desc; ++ if (resp.header.len < sizeof(*d)) { ++ pci_err(ithc->pci, "response too small for device descriptor (%u)\n", ++ resp.header.len); ++ return -EMSGSIZE; ++ } ++ if (d->wDeviceDescLength != sizeof(*d)) { ++ pci_err(ithc->pci, "invalid device descriptor length (%u)\n", ++ d->wDeviceDescLength); ++ return -EMSGSIZE; ++ } ++ ++ pci_info(ithc->pci, "Device descriptor: bcdVersion=0x%04x wReportDescLength=%u wMaxInputLength=%u wMaxOutputLength=%u wMaxFragmentLength=%u wVendorID=0x%04x wProductID=0x%04x wVersionID=0x%04x wFlags=0x%04x dwReserved=0x%08x\n", ++ d->bcdVersion, d->wReportDescLength, ++ d->wMaxInputLength, d->wMaxOutputLength, d->wMaxFragmentLength, ++ d->wVendorID, d->wProductID, d->wVersionID, ++ d->wFlags, d->dwReserved); ++ ++ ithc->vendor_id = d->wVendorID; ++ ithc->product_id = d->wProductID; ++ ithc->product_rev = d->wVersionID; ++ ithc->max_rx_size = max_t(u32, d->wMaxInputLength, ++ d->wReportDescLength + sizeof(struct hidspi_header)); ++ ithc->max_tx_size = d->wMaxOutputLength; ++ ithc->have_config = true; ++ ++ // "7. The device and host shall then enter their "Ready" states - where the device may ++ // begin sending Input Reports, and the device shall be prepared for Output Reports from ++ // the host." ++ ++ return 0; ++} ++ ++int ithc_quickspi_init(struct ithc *ithc, const struct ithc_acpi_config *cfg) ++{ ++ bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE); ++ CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED); ++ ++ ithc_log_regs(ithc); ++ CHECK_RET(ithc_quickspi_init_regs, ithc, cfg); ++ ithc_log_regs(ithc); ++ CHECK_RET(ithc_quickspi_init_hidspi, ithc, cfg); ++ ithc_log_regs(ithc); ++ ++ // This value is set to 2 in ithc_quickspi_init_regs(). It needs to be set to 1 here, ++ // otherwise DMA will not work. Maybe selects between DMA and PIO mode? ++ bitsl(&ithc->regs->quickspi_config1, ++ QUICKSPI_CONFIG1_UNKNOWN_16(0xffff), QUICKSPI_CONFIG1_UNKNOWN_16(1)); ++ ++ // TODO Do we need to set any of the following bits here? ++ //bitsb_set(&ithc->regs->dma_rx[1].control2, DMA_RX_CONTROL2_UNKNOWN_4); ++ //bitsb_set(&ithc->regs->dma_rx[0].control2, DMA_RX_CONTROL2_UNKNOWN_5); ++ //bitsb_set(&ithc->regs->dma_rx[1].control2, DMA_RX_CONTROL2_UNKNOWN_5); ++ //bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_3); ++ //bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31); ++ ++ ithc_log_regs(ithc); ++ ++ return 0; ++} ++ ++void ithc_quickspi_exit(struct ithc *ithc) ++{ ++ // TODO Should we send HIDSPI 'power off' command? ++ //struct hidspi_header h = { .type = HIDSPI_OUTPUT_TYPE_COMMAND, .id = 3, }; ++ //struct ithc_data d = { .type = ITHC_DATA_RAW, .data = &h, .size = sizeof(h) }; ++ //CHECK(ithc_dma_tx, ithc, &d); // or ithc_spi_command() ++} ++ ++int ithc_quickspi_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest) ++{ ++ const struct hidspi_header *hdr = src; ++ ++ if (len < sizeof(*hdr)) ++ return -ENODATA; ++ // TODO Do we need to handle HIDSPI packet fragmentation? ++ if (len < sizeof(*hdr) + hdr->len) ++ return -EMSGSIZE; ++ if (len > round_up(sizeof(*hdr) + hdr->len, 4)) ++ return -EMSGSIZE; ++ ++ switch (hdr->type) { ++ case HIDSPI_INPUT_TYPE_RESET_RESPONSE: ++ // TODO "When the device detects an error condition, it may interrupt and make ++ // available to the host an Input Report containing an unsolicited Reset Response. ++ // After receiving an unsolicited Reset Response, the host shall initiate the ++ // request procedure from step (4) in the [HIDSPI initialization] process." ++ dest->type = ITHC_DATA_ERROR; ++ return 0; ++ case HIDSPI_INPUT_TYPE_REPORT_DESCRIPTOR: ++ dest->type = ITHC_DATA_REPORT_DESCRIPTOR; ++ dest->data = hdr + 1; ++ dest->size = hdr->len; ++ return 0; ++ case HIDSPI_INPUT_TYPE_DATA: ++ case HIDSPI_INPUT_TYPE_GET_INPUT_REPORT_RESPONSE: ++ dest->type = ITHC_DATA_INPUT_REPORT; ++ dest->data = &hdr->id; ++ dest->size = hdr->len + 1; ++ return 0; ++ case HIDSPI_INPUT_TYPE_GET_FEATURE_RESPONSE: ++ dest->type = ITHC_DATA_GET_FEATURE; ++ dest->data = &hdr->id; ++ dest->size = hdr->len + 1; ++ return 0; ++ case HIDSPI_INPUT_TYPE_SET_FEATURE_RESPONSE: ++ case HIDSPI_INPUT_TYPE_OUTPUT_REPORT_RESPONSE: ++ dest->type = ITHC_DATA_IGNORE; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++ssize_t ithc_quickspi_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, ++ size_t maxlen) ++{ ++ struct hidspi_header *hdr = dest; ++ ++ size_t src_size = src->size; ++ const u8 *src_data = src->data; ++ u8 type; ++ ++ switch (src->type) { ++ case ITHC_DATA_SET_FEATURE: ++ type = HIDSPI_OUTPUT_TYPE_SET_FEATURE; ++ break; ++ case ITHC_DATA_GET_FEATURE: ++ type = HIDSPI_OUTPUT_TYPE_GET_FEATURE; ++ break; ++ case ITHC_DATA_OUTPUT_REPORT: ++ type = HIDSPI_OUTPUT_TYPE_OUTPUT_REPORT; ++ break; ++ case ITHC_DATA_REPORT_DESCRIPTOR: ++ type = HIDSPI_OUTPUT_TYPE_REPORT_DESCRIPTOR_REQUEST; ++ src_size = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ u8 id = 0; ++ if (src_size) { ++ id = *src_data++; ++ src_size--; ++ } ++ ++ // Data must be padded to next 4-byte boundary. ++ size_t padded = round_up(src_size, 4); ++ if (sizeof(*hdr) + padded > maxlen) ++ return -EOVERFLOW; ++ ++ // Fill the TX buffer with header and data. ++ hdr->type = type; ++ hdr->len = (u16)src_size; ++ hdr->id = id; ++ memcpy_and_pad(hdr + 1, padded, src_data, src_size, 0); ++ ++ return sizeof(*hdr) + padded; ++} ++ +diff --git a/drivers/hid/ithc/ithc-quickspi.h b/drivers/hid/ithc/ithc-quickspi.h +new file mode 100644 +index 000000000000..74d882f6b2f0 +--- /dev/null ++++ b/drivers/hid/ithc/ithc-quickspi.h +@@ -0,0 +1,39 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ ++ ++struct ithc_acpi_config { ++ bool has_config: 1; ++ bool has_input_report_header_address: 1; ++ bool has_input_report_body_address: 1; ++ bool has_output_report_body_address: 1; ++ bool has_read_opcode: 1; ++ bool has_write_opcode: 1; ++ bool has_read_mode: 1; ++ bool has_write_mode: 1; ++ bool has_spi_frequency: 1; ++ bool has_limit_packet_size: 1; ++ bool has_tx_delay: 1; ++ bool has_active_ltr: 1; ++ bool has_idle_ltr: 1; ++ u32 input_report_header_address; ++ u32 input_report_body_address; ++ u32 output_report_body_address; ++ u8 read_opcode; ++ u8 write_opcode; ++ u8 read_mode; ++ u8 write_mode; ++ u32 spi_frequency; ++ u32 limit_packet_size; ++ u32 tx_delay; // us/10 // TODO use? ++ u32 active_ltr; // ns/1024 ++ u32 idle_ltr; // ns/1024 ++}; ++ ++int ithc_read_acpi_config(struct ithc *ithc, struct ithc_acpi_config *cfg); ++void ithc_print_acpi_config(struct ithc *ithc, const struct ithc_acpi_config *cfg); ++ ++int ithc_quickspi_init(struct ithc *ithc, const struct ithc_acpi_config *cfg); ++void ithc_quickspi_exit(struct ithc *ithc); ++int ithc_quickspi_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest); ++ssize_t ithc_quickspi_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, ++ size_t maxlen); ++ +diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c +index e058721886e3..c0f13506af20 100644 +--- a/drivers/hid/ithc/ithc-regs.c ++++ b/drivers/hid/ithc/ithc-regs.c +@@ -22,46 +22,104 @@ void bitsb(__iomem u8 *reg, u8 mask, u8 val) + + int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val) + { ++ ithc_log_regs(ithc); + pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", + reg_num(reg), mask, val); + u32 x; + if (readl_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) { ++ ithc_log_regs(ithc); + pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", + reg_num(reg), mask, val); + return -ETIMEDOUT; + } ++ ithc_log_regs(ithc); + pci_dbg(ithc->pci, "done waiting\n"); + return 0; + } + + int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val) + { ++ ithc_log_regs(ithc); + pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", + reg_num(reg), mask, val); + u8 x; + if (readb_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) { ++ ithc_log_regs(ithc); + pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", + reg_num(reg), mask, val); + return -ETIMEDOUT; + } ++ ithc_log_regs(ithc); + pci_dbg(ithc->pci, "done waiting\n"); + return 0; + } + +-int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode) ++static void calc_ltr(u64 *ns, unsigned int *val, unsigned int *scale) + { +- pci_dbg(ithc->pci, "setting SPI speed to %i, mode %i\n", speed, mode); +- if (mode == 3) +- mode = 2; ++ unsigned int s = 0; ++ u64 v = *ns; ++ while (v > 0x3ff) { ++ s++; ++ v >>= 5; ++ } ++ if (s > 5) { ++ s = 5; ++ v = 0x3ff; ++ } ++ *val = v; ++ *scale = s; ++ *ns = v << (5 * s); ++} ++ ++void ithc_set_ltr_config(struct ithc *ithc, u64 active_ltr_ns, u64 idle_ltr_ns) ++{ ++ unsigned int active_val, active_scale, idle_val, idle_scale; ++ calc_ltr(&active_ltr_ns, &active_val, &active_scale); ++ calc_ltr(&idle_ltr_ns, &idle_val, &idle_scale); ++ pci_dbg(ithc->pci, "setting active LTR value to %llu ns, idle LTR value to %llu ns\n", ++ active_ltr_ns, idle_ltr_ns); ++ writel(LTR_CONFIG_ENABLE_ACTIVE | LTR_CONFIG_ENABLE_IDLE | LTR_CONFIG_APPLY | ++ LTR_CONFIG_ACTIVE_LTR_SCALE(active_scale) | LTR_CONFIG_ACTIVE_LTR_VALUE(active_val) | ++ LTR_CONFIG_IDLE_LTR_SCALE(idle_scale) | LTR_CONFIG_IDLE_LTR_VALUE(idle_val), ++ &ithc->regs->ltr_config); ++} ++ ++void ithc_set_ltr_idle(struct ithc *ithc) ++{ ++ u32 ltr = readl(&ithc->regs->ltr_config); ++ switch (ltr & (LTR_CONFIG_STATUS_ACTIVE | LTR_CONFIG_STATUS_IDLE)) { ++ case LTR_CONFIG_STATUS_IDLE: ++ break; ++ case LTR_CONFIG_STATUS_ACTIVE: ++ writel(ltr | LTR_CONFIG_TOGGLE | LTR_CONFIG_APPLY, &ithc->regs->ltr_config); ++ break; ++ default: ++ pci_err(ithc->pci, "invalid LTR state 0x%08x\n", ltr); ++ break; ++ } ++} ++ ++int ithc_set_spi_config(struct ithc *ithc, u8 clkdiv, bool clkdiv8, u8 read_mode, u8 write_mode) ++{ ++ if (clkdiv == 0 || clkdiv > 7 || read_mode > SPI_MODE_QUAD || write_mode > SPI_MODE_QUAD) ++ return -EINVAL; ++ static const char * const modes[] = { "single", "dual", "quad" }; ++ pci_dbg(ithc->pci, "setting SPI frequency to %i Hz, %s read, %s write\n", ++ SPI_CLK_FREQ_BASE / (clkdiv * (clkdiv8 ? 8 : 1)), ++ modes[read_mode], modes[write_mode]); + bitsl(&ithc->regs->spi_config, +- SPI_CONFIG_MODE(0xff) | SPI_CONFIG_SPEED(0xff) | SPI_CONFIG_UNKNOWN_18(0xff) | SPI_CONFIG_SPEED2(0xff), +- SPI_CONFIG_MODE(mode) | SPI_CONFIG_SPEED(speed) | SPI_CONFIG_UNKNOWN_18(0) | SPI_CONFIG_SPEED2(speed)); ++ SPI_CONFIG_READ_MODE(0xff) | SPI_CONFIG_READ_CLKDIV(0xff) | ++ SPI_CONFIG_WRITE_MODE(0xff) | SPI_CONFIG_WRITE_CLKDIV(0xff) | ++ SPI_CONFIG_CLKDIV_8, ++ SPI_CONFIG_READ_MODE(read_mode) | SPI_CONFIG_READ_CLKDIV(clkdiv) | ++ SPI_CONFIG_WRITE_MODE(write_mode) | SPI_CONFIG_WRITE_CLKDIV(clkdiv) | ++ (clkdiv8 ? SPI_CONFIG_CLKDIV_8 : 0)); + return 0; + } + + int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data) + { +- pci_dbg(ithc->pci, "SPI command %u, size %u, offset %u\n", command, size, offset); ++ pci_dbg(ithc->pci, "SPI command %u, size %u, offset 0x%x\n", command, size, offset); + if (size > sizeof(ithc->regs->spi_cmd.data)) + return -EINVAL; + +diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h +index d4007d9e2bac..a9d236454644 100644 +--- a/drivers/hid/ithc/ithc-regs.h ++++ b/drivers/hid/ithc/ithc-regs.h +@@ -1,14 +1,34 @@ + /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + ++#define LTR_CONFIG_ENABLE_ACTIVE BIT(0) ++#define LTR_CONFIG_TOGGLE BIT(1) ++#define LTR_CONFIG_ENABLE_IDLE BIT(2) ++#define LTR_CONFIG_APPLY BIT(3) ++#define LTR_CONFIG_IDLE_LTR_SCALE(x) (((x) & 7) << 4) ++#define LTR_CONFIG_IDLE_LTR_VALUE(x) (((x) & 0x3ff) << 7) ++#define LTR_CONFIG_ACTIVE_LTR_SCALE(x) (((x) & 7) << 17) ++#define LTR_CONFIG_ACTIVE_LTR_VALUE(x) (((x) & 0x3ff) << 20) ++#define LTR_CONFIG_STATUS_ACTIVE BIT(30) ++#define LTR_CONFIG_STATUS_IDLE BIT(31) ++ + #define CONTROL_QUIESCE BIT(1) + #define CONTROL_IS_QUIESCED BIT(2) + #define CONTROL_NRESET BIT(3) ++#define CONTROL_UNKNOWN_24(x) (((x) & 3) << 24) + #define CONTROL_READY BIT(29) + +-#define SPI_CONFIG_MODE(x) (((x) & 3) << 2) +-#define SPI_CONFIG_SPEED(x) (((x) & 7) << 4) +-#define SPI_CONFIG_UNKNOWN_18(x) (((x) & 3) << 18) +-#define SPI_CONFIG_SPEED2(x) (((x) & 0xf) << 20) // high bit = high speed mode? ++#define SPI_CONFIG_READ_MODE(x) (((x) & 3) << 2) ++#define SPI_CONFIG_READ_CLKDIV(x) (((x) & 7) << 4) ++#define SPI_CONFIG_READ_PACKET_SIZE(x) (((x) & 0x1ff) << 7) ++#define SPI_CONFIG_WRITE_MODE(x) (((x) & 3) << 18) ++#define SPI_CONFIG_WRITE_CLKDIV(x) (((x) & 7) << 20) ++#define SPI_CONFIG_CLKDIV_8 BIT(23) // additionally divide clk by 8, for both read and write ++#define SPI_CONFIG_WRITE_PACKET_SIZE(x) (((x) & 0xff) << 24) ++ ++#define SPI_CLK_FREQ_BASE 125000000 ++#define SPI_MODE_SINGLE 0 ++#define SPI_MODE_DUAL 1 ++#define SPI_MODE_QUAD 2 + + #define ERROR_CONTROL_UNKNOWN_0 BIT(0) + #define ERROR_CONTROL_DISABLE_DMA BIT(1) // clears DMA_RX_CONTROL_ENABLE when a DMA error occurs +@@ -53,33 +73,71 @@ + #define DMA_TX_STATUS_UNKNOWN_2 BIT(2) + #define DMA_TX_STATUS_UNKNOWN_3 BIT(3) // busy? + ++#define INPUT_HEADER_VERSION(x) ((x) & 0xf) ++#define INPUT_HEADER_REPORT_LENGTH(x) (((x) >> 8) & 0x3fff) ++#define INPUT_HEADER_SYNC(x) ((x) >> 24) ++#define INPUT_HEADER_VERSION_VALUE 3 ++#define INPUT_HEADER_SYNC_VALUE 0x5a ++ ++#define QUICKSPI_CONFIG1_UNKNOWN_0(x) (((x) & 0x1f) << 0) ++#define QUICKSPI_CONFIG1_UNKNOWN_5(x) (((x) & 0x1f) << 5) ++#define QUICKSPI_CONFIG1_UNKNOWN_10(x) (((x) & 0x1f) << 10) ++#define QUICKSPI_CONFIG1_UNKNOWN_16(x) (((x) & 0xffff) << 16) ++ ++#define QUICKSPI_CONFIG2_UNKNOWN_0(x) (((x) & 0x1f) << 0) ++#define QUICKSPI_CONFIG2_UNKNOWN_5(x) (((x) & 0x1f) << 5) ++#define QUICKSPI_CONFIG2_UNKNOWN_12(x) (((x) & 0xf) << 12) ++#define QUICKSPI_CONFIG2_UNKNOWN_16 BIT(16) ++#define QUICKSPI_CONFIG2_UNKNOWN_17 BIT(17) ++#define QUICKSPI_CONFIG2_DISABLE_READ_ADDRESS_INCREMENT BIT(24) ++#define QUICKSPI_CONFIG2_DISABLE_WRITE_ADDRESS_INCREMENT BIT(25) ++#define QUICKSPI_CONFIG2_ENABLE_WRITE_STREAMING_MODE BIT(27) ++#define QUICKSPI_CONFIG2_IRQ_POLARITY BIT(28) ++ + #define DMA_RX_CONTROL_ENABLE BIT(0) + #define DMA_RX_CONTROL_IRQ_UNKNOWN_1 BIT(1) // rx1 only? + #define DMA_RX_CONTROL_IRQ_ERROR BIT(3) // rx1 only? +-#define DMA_RX_CONTROL_IRQ_UNKNOWN_4 BIT(4) // rx0 only? ++#define DMA_RX_CONTROL_IRQ_READY BIT(4) // rx0 only + #define DMA_RX_CONTROL_IRQ_DATA BIT(5) + ++#define DMA_RX_CONTROL2_UNKNOWN_4 BIT(4) // rx1 only? + #define DMA_RX_CONTROL2_UNKNOWN_5 BIT(5) // rx0 only? + #define DMA_RX_CONTROL2_RESET BIT(7) // resets ringbuffer indices + + #define DMA_RX_WRAP_FLAG BIT(7) + + #define DMA_RX_STATUS_ERROR BIT(3) +-#define DMA_RX_STATUS_UNKNOWN_4 BIT(4) // set in rx0 after using CONTROL_NRESET when it becomes possible to read config (can take >100ms) ++#define DMA_RX_STATUS_READY BIT(4) // set in rx0 after using CONTROL_NRESET when it becomes possible to read config (can take >100ms) + #define DMA_RX_STATUS_HAVE_DATA BIT(5) + #define DMA_RX_STATUS_ENABLED BIT(8) + ++#define INIT_UNKNOWN_GUC_2 BIT(2) ++#define INIT_UNKNOWN_3 BIT(3) ++#define INIT_UNKNOWN_GUC_4 BIT(4) ++#define INIT_UNKNOWN_5 BIT(5) ++#define INIT_UNKNOWN_31 BIT(31) ++ + // COUNTER_RESET can be written to counter registers to reset them to zero. However, in some cases this can mess up the THC. + #define COUNTER_RESET BIT(31) + + struct ithc_registers { +- /* 0000 */ u32 _unknown_0000[1024]; ++ /* 0000 */ u32 _unknown_0000[5]; ++ /* 0014 */ u32 ltr_config; ++ /* 0018 */ u32 _unknown_0018[1018]; + /* 1000 */ u32 _unknown_1000; + /* 1004 */ u32 _unknown_1004; + /* 1008 */ u32 control_bits; + /* 100c */ u32 _unknown_100c; + /* 1010 */ u32 spi_config; +- /* 1014 */ u32 _unknown_1014[3]; ++ /* 1014 */ u8 read_opcode; // maybe for header? ++ /* 1015 */ u8 read_opcode_quad; ++ /* 1016 */ u8 read_opcode_dual; ++ /* 1017 */ u8 read_opcode_single; ++ /* 1018 */ u8 write_opcode; // not used? ++ /* 1019 */ u8 write_opcode_quad; ++ /* 101a */ u8 write_opcode_dual; ++ /* 101b */ u8 write_opcode_single; ++ /* 101c */ u32 _unknown_101c; + /* 1020 */ u32 error_control; + /* 1024 */ u32 error_status; // write to clear + /* 1028 */ u32 error_flags; // write to clear +@@ -100,12 +158,19 @@ struct ithc_registers { + /* 109a */ u8 _unknown_109a; + /* 109b */ u8 num_prds; + /* 109c */ u32 status; // write to clear ++ /* 10a0 */ u32 _unknown_10a0[5]; ++ /* 10b4 */ u32 spi_addr; + } dma_tx; +- /* 10a0 */ u32 _unknown_10a0[7]; +- /* 10bc */ u32 state; // is 0xe0000402 (dev config val 0) after CONTROL_NRESET, 0xe0000461 after first touch, 0xe0000401 after DMA_RX_CODE_RESET ++ /* 10b8 */ u32 spi_header_addr; ++ union { ++ /* 10bc */ u32 irq_cause; // in legacy THC mode ++ /* 10bc */ u32 input_header; // in QuickSPI mode (see HIDSPI spec) ++ }; + /* 10c0 */ u32 _unknown_10c0[8]; + /* 10e0 */ u32 _unknown_10e0_counters[3]; +- /* 10ec */ u32 _unknown_10ec[5]; ++ /* 10ec */ u32 quickspi_config1; ++ /* 10f0 */ u32 quickspi_config2; ++ /* 10f4 */ u32 _unknown_10f4[3]; + struct { + /* 1100/1200 */ u64 addr; // cannot be written with writeq(), must use lo_hi_writeq() + /* 1108/1208 */ u8 num_bufs; +@@ -120,70 +185,30 @@ struct ithc_registers { + /* 1118/1218 */ u64 _unknown_1118_guc_addr; + /* 1120/1220 */ u32 _unknown_1120_guc; + /* 1124/1224 */ u32 _unknown_1124_guc; +- /* 1128/1228 */ u32 unknown_init_bits; // bit 2 = guc related, bit 3 = rx1 related, bit 4 = guc related ++ /* 1128/1228 */ u32 init_unknown; + /* 112c/122c */ u32 _unknown_112c; + /* 1130/1230 */ u64 _unknown_1130_guc_addr; + /* 1138/1238 */ u32 _unknown_1138_guc; + /* 113c/123c */ u32 _unknown_113c; + /* 1140/1240 */ u32 _unknown_1140_guc; +- /* 1144/1244 */ u32 _unknown_1144[23]; ++ /* 1144/1244 */ u32 _unknown_1144[11]; ++ /* 1170/1270 */ u32 spi_addr; ++ /* 1174/1274 */ u32 _unknown_1174[11]; + /* 11a0/12a0 */ u32 _unknown_11a0_counters[6]; + /* 11b8/12b8 */ u32 _unknown_11b8[18]; + } dma_rx[2]; + }; + static_assert(sizeof(struct ithc_registers) == 0x1300); + +-#define DEVCFG_DMA_RX_SIZE(x) ((((x) & 0x3fff) + 1) << 6) +-#define DEVCFG_DMA_TX_SIZE(x) (((((x) >> 14) & 0x3ff) + 1) << 6) +- +-#define DEVCFG_TOUCH_MASK 0x3f +-#define DEVCFG_TOUCH_ENABLE BIT(0) +-#define DEVCFG_TOUCH_UNKNOWN_1 BIT(1) +-#define DEVCFG_TOUCH_UNKNOWN_2 BIT(2) +-#define DEVCFG_TOUCH_UNKNOWN_3 BIT(3) +-#define DEVCFG_TOUCH_UNKNOWN_4 BIT(4) +-#define DEVCFG_TOUCH_UNKNOWN_5 BIT(5) +-#define DEVCFG_TOUCH_UNKNOWN_6 BIT(6) +- +-#define DEVCFG_DEVICE_ID_TIC 0x43495424 // "$TIC" +- +-#define DEVCFG_SPI_MAX_FREQ(x) (((x) >> 1) & 0xf) // high bit = use high speed mode? +-#define DEVCFG_SPI_MODE(x) (((x) >> 6) & 3) +-#define DEVCFG_SPI_UNKNOWN_8(x) (((x) >> 8) & 0x3f) +-#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) // TODO implement heartbeat +-#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((x) >> 21) & 7) +-#define DEVCFG_SPI_UNKNOWN_25 BIT(25) +-#define DEVCFG_SPI_UNKNOWN_26 BIT(26) +-#define DEVCFG_SPI_UNKNOWN_27 BIT(27) +-#define DEVCFG_SPI_DELAY(x) (((x) >> 28) & 7) // TODO use this +-#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this? +- +-struct ithc_device_config { // (Example values are from an SP7+.) +- u32 _unknown_00; // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET) +- u32 _unknown_04; // 04 = 0x00000000 +- u32 dma_buf_sizes; // 08 = 0x000a00ff +- u32 touch_cfg; // 0c = 0x0000001c +- u32 _unknown_10; // 10 = 0x0000001c +- u32 device_id; // 14 = 0x43495424 = "$TIC" +- u32 spi_config; // 18 = 0xfda00a2e +- u16 vendor_id; // 1c = 0x045e = Microsoft Corp. +- u16 product_id; // 1e = 0x0c1a +- u32 revision; // 20 = 0x00000001 +- u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139 (this value looks more random on newer devices) +- u32 _unknown_28; // 28 = 0x00000000 +- u32 fw_mode; // 2c = 0x00000000 (for fw update?) +- u32 _unknown_30; // 30 = 0x00000000 +- u32 _unknown_34; // 34 = 0x0404035e (u8,u8,u8,u8 = version?) +- u32 _unknown_38; // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET) +- u32 _unknown_3c; // 3c = 0x00000002 +-}; +- + void bitsl(__iomem u32 *reg, u32 mask, u32 val); + void bitsb(__iomem u8 *reg, u8 mask, u8 val); + #define bitsl_set(reg, x) bitsl(reg, x, x) + #define bitsb_set(reg, x) bitsb(reg, x, x) + int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val); + int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val); +-int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode); ++ ++void ithc_set_ltr_config(struct ithc *ithc, u64 active_ltr_ns, u64 idle_ltr_ns); ++void ithc_set_ltr_idle(struct ithc *ithc); ++int ithc_set_spi_config(struct ithc *ithc, u8 clkdiv, bool clkdiv8, u8 read_mode, u8 write_mode); + int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data); + +diff --git a/drivers/hid/ithc/ithc.h b/drivers/hid/ithc/ithc.h +index 028e55a4ec53..e90c38044432 100644 +--- a/drivers/hid/ithc/ithc.h ++++ b/drivers/hid/ithc/ithc.h +@@ -1,20 +1,19 @@ + /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +-#include +-#include +-#include ++#include ++#include ++#include + #include ++#include + #include +-#include ++#include + #include + #include +-#include + #include + #include +-#include ++#include ++#include + #include +-#include +-#include + + #define DEVNAME "ithc" + #define DEVFULLNAME "Intel Touch Host Controller" +@@ -27,10 +26,37 @@ + + #define NUM_RX_BUF 16 + ++// PCI device IDs: ++// Lakefield ++#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT1 0x98d0 ++#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT2 0x98d1 ++// Tiger Lake ++#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT1 0xa0d0 ++#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT2 0xa0d1 ++#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT1 0x43d0 ++#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT2 0x43d1 ++// Alder Lake ++#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1 0x7ad8 ++#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2 0x7ad9 ++#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1 0x51d0 ++#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2 0x51d1 ++#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1 0x54d0 ++#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2 0x54d1 ++// Raptor Lake ++#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT1 0x7a58 ++#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2 0x7a59 ++// Meteor Lake ++#define PCI_DEVICE_ID_INTEL_THC_MTL_PORT1 0x7e48 ++#define PCI_DEVICE_ID_INTEL_THC_MTL_PORT2 0x7e4a ++ + struct ithc; + + #include "ithc-regs.h" ++#include "ithc-hid.h" + #include "ithc-dma.h" ++#include "ithc-legacy.h" ++#include "ithc-quickspi.h" ++#include "ithc-debug.h" + + struct ithc { + char phys[32]; +@@ -38,30 +64,21 @@ struct ithc { + int irq; + struct task_struct *poll_thread; + +- struct pm_qos_request activity_qos; +- struct hrtimer activity_start_timer; +- struct hrtimer activity_end_timer; +- ktime_t last_rx_time; +- unsigned int cur_rx_seq_count; +- unsigned int cur_rx_seq_errors; +- +- struct hid_device *hid; +- bool hid_parse_done; +- wait_queue_head_t wait_hid_parse; +- wait_queue_head_t wait_hid_get_feature; +- struct mutex hid_get_feature_mutex; +- void *hid_get_feature_buf; +- size_t hid_get_feature_size; +- + struct ithc_registers __iomem *regs; + struct ithc_registers *prev_regs; // for debugging +- struct ithc_device_config config; + struct ithc_dma_rx dma_rx[2]; + struct ithc_dma_tx dma_tx; ++ struct ithc_hid hid; ++ ++ bool use_quickspi; ++ bool have_config; ++ u16 vendor_id; ++ u16 product_id; ++ u32 product_rev; ++ u32 max_rx_size; ++ u32 max_tx_size; ++ u32 legacy_touch_cfg; + }; + + int ithc_reset(struct ithc *ithc); +-void ithc_set_active(struct ithc *ithc, unsigned int duration_us); +-int ithc_debug_init(struct ithc *ithc); +-void ithc_log_regs(struct ithc *ithc); + +-- +2.44.0 + diff --git a/sys-kernel/surface-sources/files/6.8/0006-surface-sam.patch b/sys-kernel/surface-sources/files/6.8/0006-surface-sam.patch index 6080890..fd1a2f8 100644 --- a/sys-kernel/surface-sources/files/6.8/0006-surface-sam.patch +++ b/sys-kernel/surface-sources/files/6.8/0006-surface-sam.patch @@ -1162,3 +1162,57 @@ index a5a3941b3f43..e54d0a8f7daa 100644 -- 2.44.0 +From 63781a056a0f06d5cc38d7755e0753c7ec08c51c Mon Sep 17 00:00:00 2001 +From: Maximilian Luz +Date: Fri, 19 Apr 2024 20:41:47 +0200 +Subject: [PATCH] platform/surface: aggregator: Fix warning when controller is + destroyed in probe + +There is a small window in ssam_serial_hub_probe() where the controller +is initialized but has not been started yet. Specifically, between +ssam_controller_init() and ssam_controller_start(). Any failure in this +window, for example caused by a failure of serdev_device_open(), +currently results in an incorrect warning being emitted. + +In particular, any failure in this window results in the controller +being destroyed via ssam_controller_destroy(). This function checks the +state of the controller and, in an attempt to validate that the +controller has been cleanly shut down before we try and deallocate any +resources, emits a warning if that state is not SSAM_CONTROLLER_STOPPED. + +However, since we have only just initialized the controller and have not +yet started it, its state is SSAM_CONTROLLER_INITIALIZED. Note that this +is the only point at which the controller has this state, as it will +change after we start the controller with ssam_controller_start() and +never revert back. Further, at this point no communication has taken +place and the sender and receiver threads have not been started yet (and +we may not even have an open serdev device either). + +Therefore, it is perfectly safe to call ssam_controller_destroy() with a +state of SSAM_CONTROLLER_INITIALIZED. This, however, means that the +warning currently being emitted is incorrect. Fix it by extending the +check. + +Fixes: c167b9c7e3d6 ("platform/surface: Add Surface Aggregator subsystem") +Signed-off-by: Maximilian Luz +--- + drivers/platform/surface/aggregator/controller.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c +index 7fc602e01487..7e89f547999b 100644 +--- a/drivers/platform/surface/aggregator/controller.c ++++ b/drivers/platform/surface/aggregator/controller.c +@@ -1354,7 +1354,8 @@ void ssam_controller_destroy(struct ssam_controller *ctrl) + if (ctrl->state == SSAM_CONTROLLER_UNINITIALIZED) + return; + +- WARN_ON(ctrl->state != SSAM_CONTROLLER_STOPPED); ++ WARN_ON(ctrl->state != SSAM_CONTROLLER_STOPPED && ++ ctrl->state != SSAM_CONTROLLER_INITIALIZED); + + /* + * Note: New events could still have been received after the previous +-- +2.44.0 +