From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6DDCA37DEB6 for ; Tue, 9 Jun 2026 04:41:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980089; cv=none; b=gQunkCKULYGMim6teSf5oTGB9CasAMxncL15brvXtJ3FOeJr1AFpqqcccqDgC5bDjdmjGAEG2qTP0Ouxz3X9ktHhmQ3XKhzW/hOVXhYYRpoymEY2uFC2V/SZbGh/L+2mkOjNmKK8fOG7Cy03ieSFzt7GmJVAA9JR3mnjPcwFYTs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980089; c=relaxed/simple; bh=JA2M7AiLUuSc6veCi/kbJrYplg8DlHt3oDIafgdkMi8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=R9BzAdVjYVfPt7fjM84/n4GbRMalN7qNTnw0W6W3nwyO/zwuZzq5je4KX5yln5zPVnSgUghwgPfnMhUdl+kx31PESyoOD+PveX9xIzyfWf7qpAXS5v6yhyBtDXYJtUi2enzo0Cq+ae1H1H3k3/0GLz9P4Ax5HA2uSFAYEeaLV/0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=FtNIrr5b; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="FtNIrr5b" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2c0c35980fdso51406635ad.2 for ; Mon, 08 Jun 2026 21:41:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980086; x=1781584886; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=eA0puiI12gkUUrChccEFoedZkqg321TG7vWV3+7W2Ns=; b=FtNIrr5bpJw5cCz9yjGqXFQxfbfSh3Ro21BPtakPw+eYYgW1GCRW2n+jFCcGA9ohoR 7mjyGbCQrtBVq8BhEqKd9fqKyN5DBV7ka7zdhHI+gQ6j/3y+ZCrW85jrmjax2BCqvTXN vmWpQll0TOTG36Wgs1criDWj9c8+lDH/r17S4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980086; x=1781584886; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=eA0puiI12gkUUrChccEFoedZkqg321TG7vWV3+7W2Ns=; b=TXzTZnUJtIPeBAxGZZvYOFVP+TzSsW931xTvJznCHvvAmzTPYkt1P6hzBOZBC4l9Ty koXepHKY6NXUyNeU1LnFhTHV7q4RET5IRq+Qeh7U3cvNYypFfdCRdKAeGhvLGFZHaiwU aQhn5faqMOMTleMhRqaiK0L0L1++g2qBLnW3jmVn+SlNf1iGD2OafFQZU2KMrn6LpGYo SAg7Ob8LzftaQ03B/WRquC79YR+MxC/ohDDWqhp7q02B5K4jiEB0jM8CBo361O/3oYck 1QXBlARCcXlaUcfW5NSDg4XieEltKc4kB8QxFxhZk7yb3dI+nI/6/oH+On6ysJAmq+b+ nf/A== X-Forwarded-Encrypted: i=1; AFNElJ+xQGFYK/Dw4SjWjuMYn6mdBZ2sCHhrOvzd5rtj9WauvCMHb3csBY3StIvxzRgfboyREptvUPSkNpRgscc=@vger.kernel.org X-Gm-Message-State: AOJu0YwVF3z1ni3tZfdCxJ9UgZFss9pJorBJidI/NedFrJWxmFYR+Mm5 JEbWAw17UbNXeUnjxPMfbX3NIKBz69S/BQ2m99jrEleBsb6oaBTN25x/rzHL8nI23w== X-Gm-Gg: Acq92OGgkjhdNTB0RGoN4UkoAnLrtG2Clz4QpLnMPE1z5P6FycvneA+p2wO2WgF1LcF lGhaWMIeOoai1AEj/Me8AWUEpJfrJA6C/qjJqZzmj+vu3logo89ufXziCQf5hUN1aE6eValiKOH vzyjfwKQyPE42lnqtEOFoKjFfOdIlkU//yFg2XVH0hPeXG8FcJOrs04dh9xH8SbHAHpotKq1G6C ZEdm5xtpo+2HNEyJ8v5UpHbP428S+8HEdjOPw2Y0LBtNK7qWkIzMPokD7f/O2g5PJMJw5r7VeK3 HfR5S5isY6Zz9NGjHSCCxKjCreiBddk1A/mYTGmUq7KooGictZGc7CYjck3Zr2HrhJCf86YqHO7 rbjC6usyfEwPNu/32zxv7cu7SpF5JBa5tRmG7VIVyf0SdC4n86Lhj1B1b99hWxGS/CeE4+cyZus UYetABqQ5UITZpRFxcB0QOXIuJCPQJSolTx+oPDWidQqIe/fC8dn5OnSNP3Cw/ZVspK+auWZ8D5 i+K5op54+u5CRoHBvNvD+QNbAAAD4KqvOd/HUpy2Jtx X-Received: by 2002:a17:903:41d1:b0:2c0:af09:f3c7 with SMTP id d9443c01a7336-2c1e85c0cbamr219282855ad.30.1780980085929; Mon, 08 Jun 2026 21:41:25 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:25 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:40:52 +0000 Subject: [PATCH v4 01/11] Documentation: Correction in HID output_report callback description. Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-1-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Jarrett Schultz , Dmitry Antipov X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=1213; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=WgXF5HKmPVI1MEtrLNcqmUizKiLvnC1/DeVVEsJVV4k=; b=MRn8ThmipL8aQeFe4El0wwHcRed1esJP/XYC9aYnkuywJVUaIVxu3P8Moai80Jt3iW7OzC0El WxYiULYHaqZCIsSH0OhJG6kcJ1FO8J7LYW+q6zH+Xa0pPw0DZ8OiK0e X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= From: Jarrett Schultz Originally output_report callback was described as must-be asynchronous, but that is not the case in some implementations, namely i2c-hid. Correct the documentation to say that it may be asynchronous. Signed-off-by: Dmitry Antipov Reviewed-by: Dmitry Torokhov Signed-off-by: Jingyuan Liang --- Documentation/hid/hid-transport.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/hid/hid-transport.rst b/Documentation/hid/hid-tr= ansport.rst index 6f1692da296c..2008cf432af1 100644 --- a/Documentation/hid/hid-transport.rst +++ b/Documentation/hid/hid-transport.rst @@ -327,8 +327,8 @@ The available HID callbacks are: =20 Send raw output report via intr channel. Used by some HID device drivers which require high throughput for outgoing requests on the intr channel= . This - must not cause SET_REPORT calls! This must be implemented as asynchrono= us - output report on the intr channel! + must not cause SET_REPORT calls! This call might be asynchronous, so the + caller should not expect an immediate response! =20 :: =20 --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EC99B380FC9 for ; Tue, 9 Jun 2026 04:41:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980090; cv=none; b=rBEpB3poFEJN5PbLJVqhgPnPwwYFTE/PgtzeXv60VkL7hMfMOyLpk4JWaeMM4L6+SOCbSbUFmoTGChaz0cJnakKVVTh6eM8PaCHHwu98Fx9c5coosAuUudOrfJJt32a0R10KbtTX2VB7UXyQSXWLnb2XKgXNDUJgCTiOgRqckdA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980090; c=relaxed/simple; bh=YyDrdUBLBq9b2tgOr7KWFqA4UmGZ8/EpPqG9WEWTYis=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ptI/QmKqRSlUIBrnewqKpMOc9fR4NaOpr5vVakHhSJD0W2biHsToQWi5ctA1LEptrl094NOorJXUNPZUCUL/Mmg+atC298L22s+aNiiV7ZnGXFqKTQb68CT2kpXpAiKRlutmMtg1CjQ40IjTGMEiMuo18WWTCZgAXZLPkp2PF70= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=D+V0JQ0D; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="D+V0JQ0D" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-2c0bd02d97eso57403245ad.2 for ; Mon, 08 Jun 2026 21:41:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980086; x=1781584886; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=k+QTRbcmVdI1fYK90qnB4ou4kNd5iswSYfzc4M5l2Vk=; b=D+V0JQ0DLMA45PrrUEvXpjMKRk1q9n6NUrbF606c+xIf8aFjbyHaaEQ6UhvCa8wudS o3f0BAP2E3pbtz+8rrrt6/3PvKYciKSNNXLbeeQOustIJunIP9+yU4uf6c91ibY6Edup EiiV9RulG6ScTLTIxL2zT4HMssa42loVdIkgA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980086; x=1781584886; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=k+QTRbcmVdI1fYK90qnB4ou4kNd5iswSYfzc4M5l2Vk=; b=eycqz2zzbCCuOg7sZjyw3USxrRwfaEdKRRBgAZml4C+RPzBbGxJl1xtp2Cw6yBjY9v 6H87Wy6vvzIwsiwNF0HyTuCmZM2kJY0Zzxo6sit3bNxlfZ91B00df4BwSs8CNjL8F0U3 c8SJtCEqBaFzCAuDL8tdYptCHGZpITnv6V+ZI3tjWXuYro8xDCv8zMxiSH8z7BS8UMcU nSeFOgv8ZTq0edhMeHTOrN4ko4ivYz2MLIXK6/GjToelyIisP4vuYRTZ5vPUDEslzL6b 9rhcSpFsVm6vBiuGy/xybYEn14RO6M+aqq6kIuivPUpx/QAOYA+LnEsSmPaelvVDFTlh dvPw== X-Forwarded-Encrypted: i=1; AFNElJ9cRU7yZr4ZgSYBOl527V8Np3AarlG/ai9Evc0Xadefw81Un/z7CzqhM83KIuWWwfo25SPcHPV1LEy6x2U=@vger.kernel.org X-Gm-Message-State: AOJu0Yy5jQOw4z2V5vL3WGXoesly8BmaNhxhqkpc8zbCzWgqtl9E9F+4 Uoe4AcgLbhntHUqOR84nFk7jJPUzMvo0nt+pvSTHYwJEkiztuaSRgFcScYYffK9LcQ== X-Gm-Gg: Acq92OEFqtmkCJkUJA3bLAKfxXG3iTL8h8FlrxxZ58uBLxG17F+wmpyaZUYPFo5ghPX Be5GMUxtrEnwa7V8LAIY3Z3WsxHm6P0yjEfn4EAYRhol6N7MYtcJdSEAvdNa9yt5BFNMiGcEtqd ZrFEPw4wLKaSl+G/+0V0rirZwnvEzaNRTzmkPsQjFZ0/nCZPh3ur/qfp3ocHqkW42IH6a6DnBvi S0ktjRoklyBMd/yztAmsuml+z4cLJO316Y2dRPe/99Pkp75YYIvn6cPAHWYsLRo3uwWoo13IGDA iM0XmvJAjINvUvtsFobyuZj5xi8PKaURQOTcRUsyHgtwNWJ3b0MDLybqfnDdRYFbOf5YuQMjLLT pmnfQNEzgQBoPzkXQEVYBpiR92RE24pFjk4jSPHf2IW5sVWcoitSEJSPQHQqt5sNKkc5RnlAEXT VNlP5oHHUPH/8vgMDXEN1J4lQPjURhfrv3Esfm0gxJZQqKE4LfhLFrlxh615iVbLXGcjNUCEs6R SZEVqI8Zi65K/LqavE2DordN3j31BBeq1VzoCkpS2iC X-Received: by 2002:a17:902:ef08:b0:2b9:6458:1a2c with SMTP id d9443c01a7336-2c1e820e30bmr230474455ad.13.1780980086496; Mon, 08 Jun 2026 21:41:26 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:26 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:40:53 +0000 Subject: [PATCH v4 02/11] HID: Add BUS_SPI support and define HID_SPI_DEVICE macro Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-2-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Jarrett Schultz , Dmitry Antipov X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=1402; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=c6BUXxKRAZp/K8L1Tqkgw56/wp/8GYCQXvql08oS5sc=; b=QyqWiN5wvJWa+DIAzGfwDwT3/7SoEQiPhD1MUJ5W+zX6UPWtdkUNM/6NIrOZhKqZHvKUaVIHE z6+3hgKEB/ECPZ/StEBid/W5+8uPBCG8+qB9iIubXF951pTpnJYNq9l X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= From: Jarrett Schultz If connecting a hid_device with bus field indicating BUS_SPI print out "SPI" in the debug print. Macro sets the bus field to BUS_SPI and uses arguments to set vendor product fields. Signed-off-by: Dmitry Antipov Reviewed-by: Dmitry Torokhov Signed-off-by: Jingyuan Liang --- drivers/hid/hid-core.c | 3 +++ include/linux/hid.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index a5b3a8ca2fcb..813c9c743ccd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2316,6 +2316,9 @@ int hid_connect(struct hid_device *hdev, unsigned int= connect_mask) case BUS_I2C: bus =3D "I2C"; break; + case BUS_SPI: + bus =3D "SPI"; + break; case BUS_SDW: bus =3D "SOUNDWIRE"; break; diff --git a/include/linux/hid.h b/include/linux/hid.h index dce862cafbbd..957f322a0ebd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -786,6 +786,8 @@ struct hid_descriptor { .bus =3D BUS_BLUETOOTH, .vendor =3D (ven), .product =3D (prod) #define HID_I2C_DEVICE(ven, prod) \ .bus =3D BUS_I2C, .vendor =3D (ven), .product =3D (prod) +#define HID_SPI_DEVICE(ven, prod) \ + .bus =3D BUS_SPI, .vendor =3D (ven), .product =3D (prod) =20 #define HID_REPORT_ID(rep) \ .report_type =3D (rep) --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AC9C23C09E4 for ; Tue, 9 Jun 2026 04:41:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980092; cv=none; b=VCRfkXaPULDIWwGuZqtDaZjgW5g1AdWcA2skD5c1EofEpFID3gskZMRNUNSFKO1GHb/fxIMeaBvEVsfnmH84UnTSQ7W7S0qUkIZKdRhuNfWU4m8CKEWj9xWy9CDncUZCWoknyyJjOds0ZUWH3dHOg0Vsw9rwlfXVt6EdPzgiNeg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980092; c=relaxed/simple; bh=1PmMFWa+4Uc2cG3vazlaFXkv17PoaHFqa/UZ4G0vpxk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sXSua+af+Arak1KjsyEv1WdDn/vIN7oOOyOsoJhxLP5ARVKEsQU11QS86F65hpd1009vLJi6Mc4N1vKvHCcMOfE/1U+ttAMnOn5+d0Vegbj4IAjHmzuBkKLiyCMEZ0WUMZ5zRd3eLV2Jb8QkfjkSB2WqpFDQFSMMVQnak1/m/RI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=NYc8z+G3; arc=none smtp.client-ip=209.85.214.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="NYc8z+G3" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2c0c3543590so33874755ad.2 for ; Mon, 08 Jun 2026 21:41:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980087; x=1781584887; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=VbL9o+HKDyj5no9OHzF6vn3WvXZ4hfH3COgoBu9DwFc=; b=NYc8z+G3GT1/jt+G7wZ6hts4j+CX22Gujbg/7LoxXrMdcDnAmScIjmDQNCk8DsP8dj twXE0/y7lOniVy/cAYb90cSa/JzncVqmAPRzETQZvRElgX+qR9pS7E0yvuxLfvLHKIE3 D6r0+Qps/pkQsEVX+pglLHKWIqXsAlovp28pw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980087; x=1781584887; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=VbL9o+HKDyj5no9OHzF6vn3WvXZ4hfH3COgoBu9DwFc=; b=Op8klqsRjDkz+5qXpeVPSxLlsgCU0UIYb1WOzfFDWsQp533OvuMdo/ZpbBzjnD+Fhf Mmqai7o1BnBx0POIlDG3yDcd05zQNgaPHRsLrU72ViO+iuocakFzPZejbWRYKsnuzr2/ qEHTrr2IuOsLX6MnBXk0dqOSKoKWqahaKgPOw4Ho4BJccrM+keHuBn3iscSoi4N2WEUj H7GZg3fIpwDP9FBaoz2BMsoK/jSFO5H8CqMuC+DPDPw78NJ5n4iB+WMP1Ek8FBaLC9YX REezzDj5tsATtpKW5o2t7eg2aNmom0dDJ9iCQQyPRgCUORNTPlJ5qEKRdeceBxDNaYUo E1CQ== X-Forwarded-Encrypted: i=1; AFNElJ/o7Ha/tRunKU4GvRHcAWvIYnOrsS2KnzkQPTesGXRlDtYcpdro/DAChnXr3Y/qE3NlOF9UhfRuwoHsxnI=@vger.kernel.org X-Gm-Message-State: AOJu0YwqVFuoU/eVmjGHUPVF5wa/aH4kgMhUOoOQjsCBZlQomItIyWtL iNtmmJc/dq0/CPpfONGnrg2/kk0MTbguCL8kR1lCxZOKtqulLJEPnM9fda6lyjw5VQ== X-Gm-Gg: Acq92OFqZ4rbIzatCseuDUpd9Z+Sa+zb5JRl/ISYb84o/lxErJTDdSMQwPkAsnlL/pt s/CrCDSnLAABB7snMY4nubbCm19f9CGmgEX9AN9bX6Ta0zs5XJ2hai1X9riZMxjZwC7D5ofUYXy B5kGbyucMMEqGt/+AfKybScxbPk0Bvz75dZ59gu+wG2kHmpOtu6kG0FjXa86VFpt5bGaGmXJii+ OohDQsF4uQK+vNpx2DJlX4tG0w9lt5cDaxdxIkQEhzWXk+I1B6asCq8JuNzx87vLxPZEDrfbNev XcQ8GcIzG4KjIOonMsQ//yznrIqcIwFazxVT6qt91ZWy1EzXfvf7bZCKTavIilrVsOz+4Q4m1O8 aJcva0B4qLm2EIi6wBF9XmVRv/FWQBlgdua5rOYxd1LnXjiYt5W2Fcu7kZcSryjGe6FmXD537IL pYwiCvzHc2nO9y+WcJdeDkMK5Yn8eGkcmiCOkEzol/S5tv6JauVCi887a9SXt3/tP0REgHaK+vt fKh0aVBV23oEeSW1J9mhXwyS3pTK7c9/k9pfcGxFNjx X-Received: by 2002:a17:903:4407:b0:2c1:d49c:8398 with SMTP id d9443c01a7336-2c1e78df934mr210656475ad.8.1780980087101; Mon, 08 Jun 2026 21:41:27 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:26 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:40:54 +0000 Subject: [PATCH v4 03/11] HID: spi-hid: add transport driver skeleton for HID over SPI bus Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-3-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Angela Czubak , Dmitry Antipov X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=8516; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=8bBMUiMsVviaZgwuzHHEU3X1NGILbYyeA8p+dlIeuvM=; b=xUHJTZWf2/3aRrO9eyO+0GWkwrsElzBwxx80EM+S+nBUGj8ysPWKIWDTrfy5CAomOBK6nGr7k MnlNW59l7syA3Bh8EN0sb3IlX4VtzW92U8Sk76xezBmY/+Ik+YU7y38 X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= From: Angela Czubak Create spi-hid folder and add Kconfig and Makefile for spi-hid driver. Add basic device structure, definitions, and probe/remove functions. Signed-off-by: Dmitry Antipov Signed-off-by: Angela Czubak Signed-off-by: Jingyuan Liang --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 2 + drivers/hid/spi-hid/Kconfig | 15 +++ drivers/hid/spi-hid/Makefile | 9 ++ drivers/hid/spi-hid/spi-hid-core.c | 213 +++++++++++++++++++++++++++++++++= ++++ 5 files changed, 241 insertions(+) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 920a64b66b25..c6ae23bfb75d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1434,6 +1434,8 @@ source "drivers/hid/bpf/Kconfig" =20 source "drivers/hid/i2c-hid/Kconfig" =20 +source "drivers/hid/spi-hid/Kconfig" + source "drivers/hid/intel-ish-hid/Kconfig" =20 source "drivers/hid/amd-sfh-hid/Kconfig" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 361a7daedeb8..6b43e789b39a 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -169,6 +169,8 @@ obj-$(CONFIG_USB_KBD) +=3D usbhid/ =20 obj-$(CONFIG_I2C_HID_CORE) +=3D i2c-hid/ =20 +obj-$(CONFIG_SPI_HID_CORE) +=3D spi-hid/ + obj-$(CONFIG_INTEL_ISH_HID) +=3D intel-ish-hid/ =20 obj-$(CONFIG_AMD_SFH_HID) +=3D amd-sfh-hid/ diff --git a/drivers/hid/spi-hid/Kconfig b/drivers/hid/spi-hid/Kconfig new file mode 100644 index 000000000000..836fdefe8345 --- /dev/null +++ b/drivers/hid/spi-hid/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (c) 2021 Microsoft Corporation +# + +menuconfig SPI_HID + tristate "SPI HID support" + default y + depends on SPI + +if SPI_HID + +config SPI_HID_CORE + tristate +endif diff --git a/drivers/hid/spi-hid/Makefile b/drivers/hid/spi-hid/Makefile new file mode 100644 index 000000000000..92e24cddbfc2 --- /dev/null +++ b/drivers/hid/spi-hid/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the SPI HID input drivers +# +# Copyright (c) 2021 Microsoft Corporation +# + +obj-$(CONFIG_SPI_HID_CORE) +=3D spi-hid.o +spi-hid-objs =3D spi-hid-core.o diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c new file mode 100644 index 000000000000..02a7608c4b88 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HID over SPI protocol implementation + * + * Copyright (c) 2021 Microsoft Corporation + * Copyright (c) 2026 Google LLC + * + * This code is partly based on "HID over I2C protocol implementation: + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012 Red Hat, Inc + * + * which in turn is partly based on "USB HID support for Linux": + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2= , Inc + * Copyright (c) 2007-2008 Oliver Neukum + * Copyright (c) 2006-2010 Jiri Kosina + */ + +#include +#include +#include +#include +#include +#include +#include + +/* struct spi_hid_conf - Conf provided to the core */ +struct spi_hid_conf { + u32 input_report_header_address; + u32 input_report_body_address; + u32 output_report_address; + u8 read_opcode; + u8 write_opcode; +}; + +/** + * struct spihid_ops - Ops provided to the core + * @power_up: do sequencing to power up the device + * @power_down: do sequencing to power down the device + * @assert_reset: do sequencing to assert the reset line + * @deassert_reset: do sequencing to deassert the reset line + * @sleep_minimal_reset_delay: minimal sleep delay during reset + */ +struct spihid_ops { + int (*power_up)(struct spihid_ops *ops); + int (*power_down)(struct spihid_ops *ops); + int (*assert_reset)(struct spihid_ops *ops); + int (*deassert_reset)(struct spihid_ops *ops); + void (*sleep_minimal_reset_delay)(struct spihid_ops *ops); +}; + +/* Driver context */ +struct spi_hid { + struct spi_device *spi; /* spi device. */ + struct hid_device *hid; /* pointer to corresponding HID dev. */ + + struct spihid_ops *ops; + struct spi_hid_conf *conf; + + enum hidspi_power_state power_state; + + u32 regulator_error_count; + int regulator_last_error; + u32 bus_error_count; + int bus_last_error; + u32 dir_count; /* device initiated reset count. */ +}; + +static const char *spi_hid_power_mode_string(enum hidspi_power_state power= _state) +{ + switch (power_state) { + case HIDSPI_ON: + return "d0"; + case HIDSPI_SLEEP: + return "d2"; + case HIDSPI_OFF: + return "d3"; + default: + return "unknown"; + } +} + +static irqreturn_t spi_hid_dev_irq(int irq, void *_shid) +{ + return IRQ_HANDLED; +} + +static ssize_t bus_error_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_hid *shid =3D dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u (%d)\n", + shid->bus_error_count, shid->bus_last_error); +} +static DEVICE_ATTR_RO(bus_error_count); + +static ssize_t regulator_error_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct spi_hid *shid =3D dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u (%d)\n", + shid->regulator_error_count, + shid->regulator_last_error); +} +static DEVICE_ATTR_RO(regulator_error_count); + +static ssize_t device_initiated_reset_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct spi_hid *shid =3D dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", shid->dir_count); +} +static DEVICE_ATTR_RO(device_initiated_reset_count); + +static struct attribute *spi_hid_attrs[] =3D { + &dev_attr_bus_error_count.attr, + &dev_attr_regulator_error_count.attr, + &dev_attr_device_initiated_reset_count.attr, + NULL /* Terminator */ +}; + +static const struct attribute_group spi_hid_group =3D { + .attrs =3D spi_hid_attrs, +}; + +const struct attribute_group *spi_hid_groups[] =3D { + &spi_hid_group, + NULL +}; +EXPORT_SYMBOL_GPL(spi_hid_groups); + +int spi_hid_core_probe(struct spi_device *spi, struct spihid_ops *ops, + struct spi_hid_conf *conf) +{ + struct device *dev =3D &spi->dev; + struct spi_hid *shid; + int error; + + if (spi->irq <=3D 0) + return dev_err_probe(dev, spi->irq ?: -EINVAL, "Missing IRQ\n"); + + shid =3D devm_kzalloc(dev, sizeof(*shid), GFP_KERNEL); + if (!shid) + return -ENOMEM; + + shid->spi =3D spi; + shid->power_state =3D HIDSPI_ON; + shid->ops =3D ops; + shid->conf =3D conf; + + spi_set_drvdata(spi, shid); + + /* + * At the end of probe we initialize the device: + * 0) assert reset, bias the interrupt line + * 1) sleep minimal reset delay + * 2) request IRQ + * 3) power up the device + * 4) deassert reset (high) + * After this we expect an IRQ with a reset response. + */ + + shid->ops->assert_reset(shid->ops); + + shid->ops->sleep_minimal_reset_delay(shid->ops); + + error =3D devm_request_threaded_irq(dev, spi->irq, NULL, spi_hid_dev_irq, + IRQF_ONESHOT, dev_name(&spi->dev), shid); + if (error) { + dev_err(dev, "%s: unable to request threaded IRQ\n", __func__); + return error; + } + + error =3D shid->ops->power_up(shid->ops); + if (error) { + dev_err(dev, "%s: could not power up\n", __func__); + return error; + } + + shid->ops->deassert_reset(shid->ops); + + dev_dbg(dev, "%s: d3 -> %s\n", __func__, + spi_hid_power_mode_string(shid->power_state)); + + return 0; +} +EXPORT_SYMBOL_GPL(spi_hid_core_probe); + +void spi_hid_core_remove(struct spi_device *spi) +{ + struct spi_hid *shid =3D spi_get_drvdata(spi); + struct device *dev =3D &spi->dev; + int error; + + shid->ops->assert_reset(shid->ops); + error =3D shid->ops->power_down(shid->ops); + if (error) + dev_err(dev, "failed to disable regulator\n"); +} +EXPORT_SYMBOL_GPL(spi_hid_core_remove); + +MODULE_DESCRIPTION("HID over SPI transport driver"); +MODULE_AUTHOR("Dmitry Antipov "); +MODULE_LICENSE("GPL"); --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7D8CA3BF660 for ; Tue, 9 Jun 2026 04:41:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980096; cv=none; b=Akb+r9QCWrRWFM/EFJGXBDdEeyFOHxWKxzJepCzYOkGsy2IkwwgWBg1JivaD4RSTsUscKHGvsu1pc4ab06uk2GvDVTCGZuIo0yOJPjsMtwf/m5Dei6ALJgBwB0db9BVTQ11Ou8+EsskX/5M5YymcDoYf3273X4bfb4x+51YyH3A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980096; c=relaxed/simple; bh=0fiT4mr7+EvL0R+wO38NzAsTi8BrTYbnYuxvTVi/+V8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BG05YlFogzeJ15bJ7HVaGV6R7uqXT7/WvBAmpN6sMmRXBOErTu4Dqo92eML1ZzMqEK8aIl4RF7YdewRtrCGIhtV3VyNC8veSmu+s3sxyFW3/gg/JJRYrx4agqrM85FQCmpivSVNcx1NXkRCI8+sjzEUYbOY8mNuCEAyYG6Ndfls= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=HwzuHR0e; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="HwzuHR0e" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-2c0aa420401so39079655ad.3 for ; Mon, 08 Jun 2026 21:41:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980088; x=1781584888; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=BfoJnBmgbOHWHkNR16loOnW5aF/CWhqGHBZuh9Ih0aA=; b=HwzuHR0eg25v1BbtW6F4b3KGCkOrqfiy4G9Y4OGKVOgBl52pfdcaOdm7wuWQI02H4V 2EhU1nQ1lXl2zVrnkPXsOZx0YdIZAypSPwdpyOw44x4wbx1Y7K30uZfoWKhSaDMtOdqJ jNfT2GC2bCle9xMfGXLrOeiLh4XkZ6o0tj4p0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980088; x=1781584888; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=BfoJnBmgbOHWHkNR16loOnW5aF/CWhqGHBZuh9Ih0aA=; b=Hl7IndjGnqFcAzet7BtpqVypconm/9PNR3JHVVjjsLtKwXzAuVnwcTI+f/e3J21OXf 5jish7EgQiLJojj7EM8RuFey1voDUooYVitUuw7NqscJW5BenZ5IWw94EnrV0NJWDtFg PMoy2tCPZND0njFq19q4eQFKPvIf6PuHBK3rZUBbHhHCLcEEGKOxjATpayLx2KWWqbZe b9AYGOKxZ7T+0UT9qOHdPGmtRKfyPShuOPOhMCXwZhLgwx5De7nZs7Cy0QpkUNXvQKlb jfFzvRE/6glQcYFDkJVAAsE7gev2LGFn7QRO8QltkCbRS57+amGSdA5EVIRmADzEIvM/ qD4Q== X-Forwarded-Encrypted: i=1; AFNElJ+pOkWjYxlyD3jTX3WBOK6w5jWCMT4ADePlvNy32GX/XqzmZFcx4raRG+tDmPw9OikePdq3+qJw5vG32ak=@vger.kernel.org X-Gm-Message-State: AOJu0Yy7hqudQ3QqpdDip5Nq5YvRCVD3P6zBo5UCRQXWUV3jpN9MWyYa Zj8Vn39XvlkBwDUbs8VV3/rUrjxEuZgenrBk2iffu2MdI3korV+9zDjOQLVq/F3OcA== X-Gm-Gg: Acq92OEYH9VQHf1q+oOt2G5NIrqbUWOuMGpYqg2/ga66kW1XknIPPCvRmEbojSjTXWd QrA46cZwO4kfcO8a88YKcZti7Lxazso48f+DjlB5cKB4uOLgw5ZRSXR6X1xG7PpxFCnJ9+TKugc TfqFkOhqGXj1ATiSJ47yTajrGewe8Hqvkcs+dB4UNGwaQdbB8Qi+pxr+8KaPnEMO48ft5CJ6VFx OE7eLevVmE05uej1rp/Z4UHTpBj6fFwxMOl2Dc8Y9WpJDKpryQ1NZEdPxmhDvnKreswo0R26qX8 fMVYUqKtl+7Lq1mY+aTV20ZVjytxeUI4z8TIinnyCrpvU3xqmg462miep48HvU/G7Z6zF/uqiul BWzNeQWjVtmH8u4VQH+5m2OgsTzRA3cddWF0gDfG/btHUtjCZf1crnFkXjw0FidfJJqM5rJcAA6 YE8izt0xgt3ddiNbvGzLOgQAEA+KsAMonGdfIpt15D8sHn9kwF14i/2pV+eCeQ5SA9fKtizLSXZ HKeBLtxN0vUZAJ09ThHlI4V1d1q82z/pVPOjd4WwHfIjaV3TEt7NhU= X-Received: by 2002:a17:903:1965:b0:2bf:172d:ef7e with SMTP id d9443c01a7336-2c1e8346fefmr206336495ad.34.1780980087810; Mon, 08 Jun 2026 21:41:27 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:27 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:40:55 +0000 Subject: [PATCH v4 04/11] HID: spi-hid: add spi-hid driver HID layer Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-4-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Dmitry Antipov , Angela Czubak X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=17398; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=0fiT4mr7+EvL0R+wO38NzAsTi8BrTYbnYuxvTVi/+V8=; b=yBgodQRlJbsoJ1YDg8Tq2TeWZZtl7xodBQfqxyvgzbRv2U4ivH0PnK+gBgSZgvT2oFE9De09f NOA0CpGquL+AeEuvPXEU2jJ6clV7OGsBpMCuLMt6ku2oLmsuZ1Ikn78 X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= Add HID low level driver callbacks to register SPI as a HID driver, and an external touch device as a HID device. Signed-off-by: Dmitry Antipov Signed-off-by: Angela Czubak Signed-off-by: Jingyuan Liang --- drivers/hid/spi-hid/spi-hid-core.c | 563 +++++++++++++++++++++++++++++++++= ++++ 1 file changed, 563 insertions(+) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 02a7608c4b88..72c2e1ce3e8d 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -20,13 +20,69 @@ * Copyright (c) 2006-2010 Jiri Kosina */ =20 +#include +#include #include +#include #include #include #include +#include #include +#include #include #include +#include +#include +#include + +#define SPI_HID_OUTPUT_REPORT_CONTENT_ID_DESC_REQUEST 0x00 + +#define SPI_HID_RESP_TIMEOUT 1000 + +/* Protocol message size constants */ +#define SPI_HID_OUTPUT_HEADER_LEN 8 + +/* flags */ +/* + * ready flag indicates that the FW is ready to accept commands and + * requests. The FW becomes ready after sending the report descriptor. + */ +#define SPI_HID_READY 0 + +/* Raw input buffer with data from the bus */ +struct spi_hid_input_buf { + u8 header[HIDSPI_INPUT_HEADER_SIZE]; + u8 body[HIDSPI_INPUT_BODY_HEADER_SIZE]; + u8 content[]; +}; + +/* Raw output report buffer to be put on the bus */ +struct spi_hid_output_buf { + u8 header[SPI_HID_OUTPUT_HEADER_LEN]; + u8 content[]; +}; + +/* Data necessary to send an output report */ +struct spi_hid_output_report { + u8 report_type; + u16 content_length; + u8 content_id; + u8 *content; +}; + +/* Processed data from a device descriptor */ +struct spi_hid_device_descriptor { + u16 hid_version; + u16 report_descriptor_length; + u16 max_input_length; + u16 max_output_length; + u16 max_fragment_length; + u16 vendor_id; + u16 product_id; + u16 version_id; + u8 no_output_report_ack; +}; =20 /* struct spi_hid_conf - Conf provided to the core */ struct spi_hid_conf { @@ -61,8 +117,26 @@ struct spi_hid { struct spihid_ops *ops; struct spi_hid_conf *conf; =20 + struct spi_hid_device_descriptor desc; /* HID device descriptor. */ + struct spi_hid_output_buf *output; /* Output buffer. */ + struct spi_hid_input_buf *input; /* Input buffer. */ + struct spi_hid_input_buf *response; /* Response buffer. */ + + u16 response_length; + u16 bufsize; + enum hidspi_power_state power_state; =20 + u8 reset_attempts; /* The number of reset attempts. */ + + unsigned long flags; /* device flags. */ + + /* Control lock to make sure one output transaction at a time. */ + struct mutex output_lock; + struct completion output_done; + + u32 report_descriptor_crc32; /* HID report descriptor crc32 checksum. */ + u32 regulator_error_count; int regulator_last_error; u32 bus_error_count; @@ -70,6 +144,33 @@ struct spi_hid { u32 dir_count; /* device initiated reset count. */ }; =20 +static struct hid_ll_driver spi_hid_ll_driver; + +static void spi_hid_populate_output_header(u8 *buf, + const struct spi_hid_conf *conf, + const struct spi_hid_output_report *report) +{ + buf[0] =3D conf->write_opcode; + put_unaligned_be24(conf->output_report_address, &buf[1]); + buf[4] =3D report->report_type; + put_unaligned_le16(report->content_length, &buf[5]); + buf[7] =3D report->content_id; +} + +static int spi_hid_output(struct spi_hid *shid, const void *buf, u16 lengt= h) +{ + int error; + + error =3D spi_write(shid->spi, buf, length); + + if (error) { + shid->bus_error_count++; + shid->bus_last_error =3D error; + } + + return error; +} + static const char *spi_hid_power_mode_string(enum hidspi_power_state power= _state) { switch (power_state) { @@ -84,11 +185,455 @@ static const char *spi_hid_power_mode_string(enum hid= spi_power_state power_state } } =20 +static void spi_hid_stop_hid(struct spi_hid *shid) +{ + struct hid_device *hid =3D shid->hid; + + shid->hid =3D NULL; + clear_bit(SPI_HID_READY, &shid->flags); + + if (hid) + hid_destroy_device(hid); +} + +static int __spi_hid_send_output_report(struct spi_hid *shid, + struct spi_hid_output_report *report) +{ + struct spi_hid_output_buf *buf =3D shid->output; + struct device *dev =3D &shid->spi->dev; + u16 report_length; + u16 padded_length; + u8 padding; + int error; + + if (report->content_length > shid->desc.max_output_length || + report->content_length > shid->bufsize) { + dev_err(dev, "Output report too big, content_length 0x%x\n", + report->content_length); + return -E2BIG; + } + + spi_hid_populate_output_header(buf->header, shid->conf, report); + + if (report->content_length) + memcpy(&buf->content, report->content, report->content_length); + + report_length =3D sizeof(buf->header) + report->content_length; + padded_length =3D round_up(report_length, 4); + padding =3D padded_length - report_length; + memset(&buf->content[report->content_length], 0, padding); + + error =3D spi_hid_output(shid, buf, padded_length); + if (error) + dev_err(dev, "Failed output transfer: %d\n", error); + + return error; +} + +static int spi_hid_send_output_report(struct spi_hid *shid, + struct spi_hid_output_report *report) +{ + guard(mutex)(&shid->output_lock); + return __spi_hid_send_output_report(shid, report); +} + +static int spi_hid_sync_request(struct spi_hid *shid, + struct spi_hid_output_report *report) +{ + struct device *dev =3D &shid->spi->dev; + int error; + + guard(mutex)(&shid->output_lock); + + reinit_completion(&shid->output_done); + + error =3D __spi_hid_send_output_report(shid, report); + if (error) + return error; + + error =3D wait_for_completion_interruptible_timeout(&shid->output_done, + msecs_to_jiffies(SPI_HID_RESP_TIMEOUT)); + if (error =3D=3D 0) { + dev_err(dev, "Response timed out\n"); + return -ETIMEDOUT; + } + if (error < 0) + return error; + + return 0; +} + +/* + * This function returns the length of the report descriptor, or a negative + * error code if something went wrong. + */ +static int spi_hid_report_descriptor_request(struct spi_hid *shid) +{ + struct device *dev =3D &shid->spi->dev; + struct spi_hid_output_report report =3D { + .report_type =3D REPORT_DESCRIPTOR, + .content_length =3D 0, + .content_id =3D SPI_HID_OUTPUT_REPORT_CONTENT_ID_DESC_REQUEST, + .content =3D NULL, + }; + int ret; + + ret =3D spi_hid_sync_request(shid, &report); + if (ret) { + dev_err(dev, + "Expected report descriptor not received: %d\n", ret); + return ret; + } + + ret =3D shid->response_length; + if (ret !=3D shid->desc.report_descriptor_length) { + ret =3D min_t(unsigned int, ret, shid->desc.report_descriptor_length); + dev_err(dev, "Received report descriptor length doesn't match device des= criptor field, using min of the two: %d\n", + ret); + } + + return ret; +} + +static int spi_hid_create_device(struct spi_hid *shid) +{ + struct hid_device *hid; + struct device *dev =3D &shid->spi->dev; + int error; + + hid =3D hid_allocate_device(); + error =3D PTR_ERR_OR_ZERO(hid); + if (error) { + dev_err(dev, "Failed to allocate hid device: %d\n", error); + return error; + } + + hid->driver_data =3D shid->spi; + hid->ll_driver =3D &spi_hid_ll_driver; + hid->dev.parent =3D &shid->spi->dev; + hid->bus =3D BUS_SPI; + hid->version =3D shid->desc.hid_version; + hid->vendor =3D shid->desc.vendor_id; + hid->product =3D shid->desc.product_id; + + snprintf(hid->name, sizeof(hid->name), "spi %04X:%04X", + hid->vendor, hid->product); + strscpy(hid->phys, dev_name(&shid->spi->dev), sizeof(hid->phys)); + + shid->hid =3D hid; + + error =3D hid_add_device(hid); + if (error) { + dev_err(dev, "Failed to add hid device: %d\n", error); + /* + * We likely got here because report descriptor request timed + * out. Let's disconnect and destroy the hid_device structure. + */ + spi_hid_stop_hid(shid); + return error; + } + + return 0; +} + +static int spi_hid_get_request(struct spi_hid *shid, u8 content_id) +{ + struct device *dev =3D &shid->spi->dev; + struct spi_hid_output_report report =3D { + .report_type =3D GET_FEATURE, + .content_length =3D 0, + .content_id =3D content_id, + .content =3D NULL, + }; + int error; + + error =3D spi_hid_sync_request(shid, &report); + if (error) { + dev_err(dev, + "Expected get request response not received! Error %d\n", + error); + return error; + } + + return 0; +} + +static int spi_hid_set_request(struct spi_hid *shid, u8 *arg_buf, u16 arg_= len, + u8 content_id) +{ + struct spi_hid_output_report report =3D { + .report_type =3D SET_FEATURE, + .content_length =3D arg_len, + .content_id =3D content_id, + .content =3D arg_buf, + }; + + return spi_hid_sync_request(shid, &report); +} + +/* This is a placeholder. Will be implemented in the next patch. */ static irqreturn_t spi_hid_dev_irq(int irq, void *_shid) { return IRQ_HANDLED; } =20 +static int spi_hid_alloc_buffers(struct spi_hid *shid, size_t report_size) +{ + struct device *dev =3D &shid->spi->dev; + int inbufsize =3D round_up(sizeof(shid->input->header) + + sizeof(shid->input->body) + report_size, 4); + int outbufsize =3D round_up(sizeof(shid->output->header) + report_size, 4= ); + void *tmp; + + tmp =3D devm_krealloc(dev, shid->output, outbufsize, GFP_KERNEL | __GFP_Z= ERO); + if (!tmp) + return -ENOMEM; + shid->output =3D tmp; + + tmp =3D devm_krealloc(dev, shid->input, inbufsize, GFP_KERNEL | __GFP_ZER= O); + if (!tmp) + return -ENOMEM; + shid->input =3D tmp; + + tmp =3D devm_krealloc(dev, shid->response, inbufsize, GFP_KERNEL | __GFP_= ZERO); + if (!tmp) + return -ENOMEM; + shid->response =3D tmp; + + if (!shid->output || !shid->input || !shid->response) + return -ENOMEM; + + shid->bufsize =3D report_size; + + return 0; +} + +static int spi_hid_get_report_length(struct hid_report *report) +{ + return DIV_ROUND_UP(report->size, 8) + + report->device->report_enum[report->type].numbered + 2; +} + +/* + * Traverse the supplied list of reports and find the longest + */ +static void spi_hid_find_max_report(struct hid_device *hid, u32 type, + u16 *max) +{ + struct hid_report *report; + u16 size; + + /* + * We should not rely on wMaxInputLength, as some devices may set it to + * a wrong length. + */ + list_for_each_entry(report, &hid->report_enum[type].report_list, list) { + size =3D spi_hid_get_report_length(report); + if (*max < size) + *max =3D size; + } +} + +/* hid_ll_driver interface functions */ + +static int spi_hid_ll_start(struct hid_device *hid) +{ + struct spi_device *spi =3D hid->driver_data; + struct spi_hid *shid =3D spi_get_drvdata(spi); + int error =3D 0; + u16 bufsize =3D 0; + + spi_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize); + spi_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize); + spi_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize); + + if (bufsize < HID_MIN_BUFFER_SIZE) { + dev_err(&spi->dev, + "HID_MIN_BUFFER_SIZE > max_input_length (%d)\n", + bufsize); + return -EINVAL; + } + + if (bufsize > shid->bufsize) { + guard(disable_irq)(&shid->spi->irq); + + error =3D spi_hid_alloc_buffers(shid, bufsize); + if (error) + return error; + } + + return 0; +} + +static void spi_hid_ll_stop(struct hid_device *hid) +{ + hid->claimed =3D 0; +} + +static int spi_hid_ll_open(struct hid_device *hid) +{ + struct spi_device *spi =3D hid->driver_data; + struct spi_hid *shid =3D spi_get_drvdata(spi); + + set_bit(SPI_HID_READY, &shid->flags); + return 0; +} + +static void spi_hid_ll_close(struct hid_device *hid) +{ + struct spi_device *spi =3D hid->driver_data; + struct spi_hid *shid =3D spi_get_drvdata(spi); + + clear_bit(SPI_HID_READY, &shid->flags); + shid->reset_attempts =3D 0; +} + +static int spi_hid_ll_power(struct hid_device *hid, int level) +{ + struct spi_device *spi =3D hid->driver_data; + struct spi_hid *shid =3D spi_get_drvdata(spi); + int error =3D 0; + + guard(mutex)(&shid->output_lock); + if (!shid->hid) + error =3D -ENODEV; + + return error; +} + +static int spi_hid_ll_parse(struct hid_device *hid) +{ + struct spi_device *spi =3D hid->driver_data; + struct spi_hid *shid =3D spi_get_drvdata(spi); + struct device *dev =3D &spi->dev; + unsigned int rsize =3D shid->desc.report_descriptor_length; + int error, len; + + if (rsize > HID_MAX_DESCRIPTOR_SIZE) { + dev_err(dev, + "Report descriptor size %d is greater than HID_MAX_DESCRIPTOR_SIZE %d\n= ", + rsize, HID_MAX_DESCRIPTOR_SIZE); + return -EINVAL; + } + + if (rsize > shid->bufsize) { + error =3D spi_hid_alloc_buffers(shid, rsize); + if (error) + return error; + } + + len =3D spi_hid_report_descriptor_request(shid); + if (len < 0) { + dev_err(dev, "Report descriptor request failed, %d\n", len); + return len; + } + + /* + * FIXME: below call returning 0 doesn't mean that the report descriptor + * is good. We might be caching a crc32 of a corrupted r. d. or who + * knows what the FW sent. Need to have a feedback loop about r. d. + * being ok and only then cache it. + */ + error =3D hid_parse_report(hid, (u8 *)shid->response->content, len); + if (error) { + dev_err(dev, "failed parsing report: %d\n", error); + return error; + } + shid->report_descriptor_crc32 =3D crc32_le(0, + (unsigned char const *)shid->response->content, + len); + + return 0; +} + +static int spi_hid_ll_raw_request(struct hid_device *hid, + unsigned char reportnum, __u8 *buf, + size_t len, unsigned char rtype, int reqtype) +{ + struct spi_device *spi =3D hid->driver_data; + struct spi_hid *shid =3D spi_get_drvdata(spi); + struct device *dev =3D &spi->dev; + int ret; + + switch (reqtype) { + case HID_REQ_SET_REPORT: + if (buf[0] !=3D reportnum) { + dev_err(dev, "report id mismatch\n"); + return -EINVAL; + } + + ret =3D spi_hid_set_request(shid, &buf[1], len - 1, + reportnum); + if (ret) { + dev_err(dev, "failed to set report\n"); + return ret; + } + + ret =3D len; + break; + case HID_REQ_GET_REPORT: + ret =3D spi_hid_get_request(shid, reportnum); + if (ret) { + dev_err(dev, "failed to get report\n"); + return ret; + } + + ret =3D min_t(size_t, len, + (shid->response->body[1] | (shid->response->body[2] << 8)) + 1); + buf[0] =3D shid->response->body[3]; + memcpy(&buf[1], &shid->response->content, ret); + break; + default: + dev_err(dev, "invalid request type\n"); + return -EIO; + } + + return ret; +} + +static int spi_hid_ll_output_report(struct hid_device *hid, __u8 *buf, + size_t len) +{ + struct spi_device *spi =3D hid->driver_data; + struct spi_hid *shid =3D spi_get_drvdata(spi); + struct device *dev =3D &spi->dev; + struct spi_hid_output_report report =3D { + .report_type =3D OUTPUT_REPORT, + .content_length =3D len - 1, + .content_id =3D buf[0], + .content =3D &buf[1], + }; + int error; + + if (!test_bit(SPI_HID_READY, &shid->flags)) { + dev_err(dev, "%s called in unready state\n", __func__); + return -ENODEV; + } + + if (shid->desc.no_output_report_ack) + error =3D spi_hid_send_output_report(shid, &report); + else + error =3D spi_hid_sync_request(shid, &report); + + if (error) { + dev_err(dev, "failed to send output report\n"); + return error; + } + + return len; +} + +static struct hid_ll_driver spi_hid_ll_driver =3D { + .start =3D spi_hid_ll_start, + .stop =3D spi_hid_ll_stop, + .open =3D spi_hid_ll_open, + .close =3D spi_hid_ll_close, + .power =3D spi_hid_ll_power, + .parse =3D spi_hid_ll_parse, + .output_report =3D spi_hid_ll_output_report, + .raw_request =3D spi_hid_ll_raw_request, +}; + static ssize_t bus_error_count_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -159,6 +704,18 @@ int spi_hid_core_probe(struct spi_device *spi, struct = spihid_ops *ops, =20 spi_set_drvdata(spi, shid); =20 + mutex_init(&shid->output_lock); + init_completion(&shid->output_done); + + /* + * we need to allocate the buffer without knowing the maximum + * size of the reports. Let's use SZ_2K, then we do the + * real computation later. + */ + error =3D spi_hid_alloc_buffers(shid, SZ_2K); + if (error) + return error; + /* * At the end of probe we initialize the device: * 0) assert reset, bias the interrupt line @@ -191,6 +748,10 @@ int spi_hid_core_probe(struct spi_device *spi, struct = spihid_ops *ops, dev_dbg(dev, "%s: d3 -> %s\n", __func__, spi_hid_power_mode_string(shid->power_state)); =20 + error =3D spi_hid_create_device(shid); + if (error) + return error; + return 0; } EXPORT_SYMBOL_GPL(spi_hid_core_probe); @@ -201,6 +762,8 @@ void spi_hid_core_remove(struct spi_device *spi) struct device *dev =3D &spi->dev; int error; =20 + spi_hid_stop_hid(shid); + shid->ops->assert_reset(shid->ops); error =3D shid->ops->power_down(shid->ops); if (error) --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 637BB3C3C10 for ; Tue, 9 Jun 2026 04:41:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980097; cv=none; b=gWox3es0Ann81Cw1shKXF7Gdt/pSfRRLKT/AlErL50gUXpR3Y8zG2xxOc4Xh8irnv5qTLRysCFYKpnXnOYv2JDxhmSsrbzYl0Q8rfzK2ScsJfECsoHQ4GHcpLhONH7FqwiNXDlaJWrfYIDpUmIdBNaVCLVJRSHI+hlCAhFPw+lg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980097; c=relaxed/simple; bh=rA4xp+cnv6Eiu6kF0v5FXO2/HzrzugGaddG2wnRnWoc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Uns1zHiEoholSwRSAq3V5yWgsAC5t4CpkimmyGOt3hlTar6MZfyS73Bd523r5C282R3F1kYpUfhuxrXkZsuTz2udbOkrpizzfm+h/uKFu+akhTAxLmIAHPY5d57B5imJdHDAuRqe1q+joxMA5nAAPwzwPLG5cvSVSTB1pYWia7Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=TWpEHj95; arc=none smtp.client-ip=209.85.214.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="TWpEHj95" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-2bf2e8ccca1so35860875ad.0 for ; Mon, 08 Jun 2026 21:41:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980089; x=1781584889; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=8yp9ujJvX2wJ0aXjXEb9nnrbNtX7xSc68UPamh1mznQ=; b=TWpEHj95nUlL5MHclmNku/XpR39uC2adYazyOZyxYyOoQ7Lf0yA0V0fZAVGaEVTMSD 32lOlNeyIEG8r5ptRw1lm8sme3NscwpTd9ZfG2t5EEeDU+3WuWLQtf8EOzAjr03cSMk3 XK0H656483c5dl42h8GXGNJxNWxhVqTIsafmM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980089; x=1781584889; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=8yp9ujJvX2wJ0aXjXEb9nnrbNtX7xSc68UPamh1mznQ=; b=mBE4LOTwk7Dglm5iYO9Li461km2HPIK2zWwg9LMSFCtZAriiG1dDz063ddsE3ChmJ/ Aq6gFTwLxmHhOsDQoE2h5XRUSf3JBY0bK3UdXM9+cLV4Z6jkpTsKCi6bphnO97IJhaJ/ X3gVxgf+imAAq/ss/l8TiZSiRl4a8MBpP45ofCKKozXF6ied9kUgrWe1t0UYZj5e4qPq 4O0P0CXNY5o3p6Cr/VSlayqpG6OhQqprva5YePlcfBt+PscNpBemtIuuKAkpfgDYY7mj Rt4IB5KfEm6g/jx8g+BZUAq03TkZCG9tzTDMPWJyC6r4TnFzrUJRxPDoizuwlzi8PG81 fAqQ== X-Forwarded-Encrypted: i=1; AFNElJ9sgL9A7ulZDdshX+Nw0BCbQjVhB07phelmQUaq6d06d/RHpVt2L8F3vEX6z2ED/uQejprxx0bjx7FwXTI=@vger.kernel.org X-Gm-Message-State: AOJu0YxWkweH2EjrZayNiXtCCTsXvASnJeI0I7whYGFjBMOXXc/IM2eB lZvKAIafsUX0yyLCPRHH25UoWUXOL1N5NXWKwSl+Mvs+oMMqw6XuSITx6CQhORZuIQ== X-Gm-Gg: Acq92OGAcL44feejdjntnCfQIQtxDTLUhSbbTokmKE4ggffRvBWaW7tb239gmNyEcOs PZcWdVjKOtpBY93suDnfywdVd20zVduSxTRplmGdoMj2rrdM/sj33YCknK6dq8fMC5sN3uAP3hx PfytbNPtaIvE+zOwjfeEsCkV3WKRjskmyZb0G0c9/3+Zlsuz0kMjA4cLQHXhDF0hiI1zRLg1I+/ nQlJNvY+3T+n9mx4iChNpUpv2Q/ZCM3G1Dc5gKEbFCSrYWAoGtcl0n0lKFZeuGm7PQ4B9R2WNFo R4+mF1mJFbe7EagzSvzmB+tw3zCCCo8q+J7OaRi3BZZRNRhSrLACSTB+/sD30dr/gslwMScpw2m JrS0/V9uI4O60Nayn8wYsudepQxHPUt3D0upqpyOtljSLsgJK4qvzlXJpdz82O4ex2cXzFJ/S0+ 9XTgkhB3ogJ5BECugqh5xvnY/17suq8vz6X4iFQ77GLhKwFYtxKN6VwAmzo5qWg00xP46p2ozKD g7/0x6o7kneDAnGzFeC2Vc/Mn7s7k/SCBJGHOqHOe+U X-Received: by 2002:a17:903:faf:b0:2c1:6715:a4ea with SMTP id d9443c01a7336-2c1e821f6cbmr202906315ad.30.1780980088572; Mon, 08 Jun 2026 21:41:28 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:28 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:40:56 +0000 Subject: [PATCH v4 05/11] HID: spi-hid: add HID SPI protocol implementation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-5-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Dmitry Antipov , Angela Czubak X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=24975; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=rA4xp+cnv6Eiu6kF0v5FXO2/HzrzugGaddG2wnRnWoc=; b=ER7L53oWosmRxcBQFpC6dvQb8aSyDQSNWBM8DrkQn4nELj43n6SF2m6ANwolRJIrYV1AtOpzR 0hmqlG0M4hhA871VNgxa/K189VAbDiZEV/qDnET/h56JCPpPe8Urzcw X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= This driver follows HID Over SPI Protocol Specification 1.0 available at https://www.microsoft.com/en-us/download/details.aspx?id=3D103325. The initial version of the driver does not support: 1) multi-fragment input reports, 2) sending GET_INPUT and COMMAND output report types and processing their respective acknowledge input reports, and 3) device sleep power state. Signed-off-by: Dmitry Antipov Signed-off-by: Angela Czubak Signed-off-by: Jingyuan Liang --- drivers/hid/spi-hid/spi-hid-core.c | 608 +++++++++++++++++++++++++++++++++= +++- 1 file changed, 592 insertions(+), 16 deletions(-) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 72c2e1ce3e8d..f6ea2d4365a7 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -20,14 +20,20 @@ * Copyright (c) 2006-2010 Jiri Kosina */ =20 +#include #include #include #include +#include #include #include #include +#include #include +#include #include +#include +#include #include #include #include @@ -35,12 +41,22 @@ #include #include #include +#include +#include + +/* Protocol constants */ +#define SPI_HID_READ_APPROVAL_CONSTANT 0xff +#define SPI_HID_INPUT_HEADER_SYNC_BYTE 0x5a +#define SPI_HID_INPUT_HEADER_VERSION 0x03 +#define SPI_HID_SUPPORTED_VERSION 0x0300 =20 #define SPI_HID_OUTPUT_REPORT_CONTENT_ID_DESC_REQUEST 0x00 =20 -#define SPI_HID_RESP_TIMEOUT 1000 +#define SPI_HID_MAX_RESET_ATTEMPTS 3 +#define SPI_HID_RESP_TIMEOUT 1000 =20 /* Protocol message size constants */ +#define SPI_HID_READ_APPROVAL_LEN 5 #define SPI_HID_OUTPUT_HEADER_LEN 8 =20 /* flags */ @@ -49,6 +65,22 @@ * requests. The FW becomes ready after sending the report descriptor. */ #define SPI_HID_READY 0 +/* + * refresh_in_progress is set to true while the refresh_device worker + * thread is destroying and recreating the hidraw device. When this flag + * is set to true, the ll_close and ll_open functions will not cause + * power state changes. + */ +#define SPI_HID_REFRESH_IN_PROGRESS 1 +/* + * reset_pending indicates that the device is being reset. When this flag + * is set to true, garbage interrupts triggered during reset will be + * dropped and will not cause error handling. + */ +#define SPI_HID_RESET_PENDING 2 +#define SPI_HID_RESET_RESPONSE 3 +#define SPI_HID_CREATE_DEVICE 4 +#define SPI_HID_ERROR 5 =20 /* Raw input buffer with data from the bus */ struct spi_hid_input_buf { @@ -57,6 +89,22 @@ struct spi_hid_input_buf { u8 content[]; }; =20 +/* Processed data from input report header */ +struct spi_hid_input_header { + u8 version; + u16 report_length; + u8 last_fragment_flag; + u8 sync_const; +}; + +/* Processed data from an input report */ +struct spi_hid_input_report { + u8 report_type; + u16 content_length; + u8 content_id; + u8 *content; +}; + /* Raw output report buffer to be put on the bus */ struct spi_hid_output_buf { u8 header[SPI_HID_OUTPUT_HEADER_LEN]; @@ -114,6 +162,9 @@ struct spi_hid { struct spi_device *spi; /* spi device. */ struct hid_device *hid; /* pointer to corresponding HID dev. */ =20 + struct spi_transfer input_transfer[2]; /* Transfer buffer for read and wr= ite. */ + struct spi_message input_message; /* used to execute a sequence of spi tr= ansfers. */ + struct spihid_ops *ops; struct spi_hid_conf *conf; =20 @@ -131,8 +182,15 @@ struct spi_hid { =20 unsigned long flags; /* device flags. */ =20 - /* Control lock to make sure one output transaction at a time. */ + struct work_struct reset_work; + + /* Control lock to ensure complete output transaction. */ struct mutex output_lock; + /* Power lock to make sure one power state change at a time. */ + struct mutex power_lock; + /* Protect bus I/O and shid->hid pointer lifecycle. */ + struct mutex io_lock; + struct completion output_done; =20 u32 report_descriptor_crc32; /* HID report descriptor crc32 checksum. */ @@ -142,10 +200,74 @@ struct spi_hid { u32 bus_error_count; int bus_last_error; u32 dir_count; /* device initiated reset count. */ + + /* DMA-safe transfer buffers */ + u8 read_approval_header[SPI_HID_READ_APPROVAL_LEN] ____cacheline_aligned; + u8 read_approval_body[SPI_HID_READ_APPROVAL_LEN]; }; =20 static struct hid_ll_driver spi_hid_ll_driver; =20 +static void spi_hid_populate_read_approvals(const struct spi_hid_conf *con= f, + u8 *header_buf, u8 *body_buf) +{ + header_buf[0] =3D conf->read_opcode; + put_unaligned_be24(conf->input_report_header_address, &header_buf[1]); + header_buf[4] =3D SPI_HID_READ_APPROVAL_CONSTANT; + + body_buf[0] =3D conf->read_opcode; + put_unaligned_be24(conf->input_report_body_address, &body_buf[1]); + body_buf[4] =3D SPI_HID_READ_APPROVAL_CONSTANT; +} + +static void spi_hid_parse_dev_desc(const struct hidspi_dev_descriptor *raw, + struct spi_hid_device_descriptor *desc) +{ + desc->hid_version =3D le16_to_cpu(raw->bcd_ver); + desc->report_descriptor_length =3D le16_to_cpu(raw->rep_desc_len); + desc->max_input_length =3D le16_to_cpu(raw->max_input_len); + desc->max_output_length =3D le16_to_cpu(raw->max_output_len); + + /* FIXME: multi-fragment not supported, field below not used */ + desc->max_fragment_length =3D le16_to_cpu(raw->max_frag_len); + + desc->vendor_id =3D le16_to_cpu(raw->vendor_id); + desc->product_id =3D le16_to_cpu(raw->product_id); + desc->version_id =3D le16_to_cpu(raw->version_id); + desc->no_output_report_ack =3D le16_to_cpu(raw->flags) & BIT(0); +} + +static void spi_hid_populate_input_header(const u8 *buf, + struct spi_hid_input_header *header) +{ + header->version =3D buf[0] & 0xf; + header->report_length =3D (get_unaligned_le16(&buf[1]) & 0x3fff) * 4; + header->last_fragment_flag =3D (buf[2] & 0x40) >> 6; + header->sync_const =3D buf[3]; +} + +static void spi_hid_populate_input_body(const u8 *buf, + struct input_report_body_header *body) +{ + body->input_report_type =3D buf[0]; + body->content_len =3D get_unaligned_le16(&buf[1]); + body->content_id =3D buf[3]; +} + +static void spi_hid_input_report_prepare(struct spi_hid_input_buf *buf, + struct spi_hid_input_report *report) +{ + struct spi_hid_input_header header; + struct input_report_body_header body; + + spi_hid_populate_input_header(buf->header, &header); + spi_hid_populate_input_body(buf->body, &body); + report->report_type =3D body.input_report_type; + report->content_length =3D body.content_len; + report->content_id =3D body.content_id; + report->content =3D buf->content; +} + static void spi_hid_populate_output_header(u8 *buf, const struct spi_hid_conf *conf, const struct spi_hid_output_report *report) @@ -157,6 +279,33 @@ static void spi_hid_populate_output_header(u8 *buf, buf[7] =3D report->content_id; } =20 +static int spi_hid_input_sync(struct spi_hid *shid, void *buf, u16 length, + bool is_header) +{ + int error; + + shid->input_transfer[0].tx_buf =3D is_header ? + shid->read_approval_header : + shid->read_approval_body; + shid->input_transfer[0].len =3D SPI_HID_READ_APPROVAL_LEN; + + shid->input_transfer[1].rx_buf =3D buf; + shid->input_transfer[1].len =3D length; + + spi_message_init_with_transfers(&shid->input_message, + shid->input_transfer, 2); + + error =3D spi_sync(shid->spi, &shid->input_message); + if (error) { + dev_err(&shid->spi->dev, "Error starting sync transfer: %d\n", error); + shid->bus_error_count++; + shid->bus_last_error =3D error; + return error; + } + + return 0; +} + static int spi_hid_output(struct spi_hid *shid, const void *buf, u16 lengt= h) { int error; @@ -187,15 +336,64 @@ static const char *spi_hid_power_mode_string(enum hid= spi_power_state power_state =20 static void spi_hid_stop_hid(struct spi_hid *shid) { - struct hid_device *hid =3D shid->hid; + struct hid_device *hid; =20 - shid->hid =3D NULL; - clear_bit(SPI_HID_READY, &shid->flags); + scoped_guard(mutex, &shid->io_lock) { + hid =3D shid->hid; + shid->hid =3D NULL; + clear_bit(SPI_HID_READY, &shid->flags); + } =20 if (hid) hid_destroy_device(hid); } =20 +static void spi_hid_error_handler(struct spi_hid *shid) +{ + struct device *dev =3D &shid->spi->dev; + int error; + + guard(mutex)(&shid->power_lock); + if (shid->power_state =3D=3D HIDSPI_OFF) + return; + + guard(disable_irq)(&shid->spi->irq); + + if (shid->reset_attempts++ >=3D SPI_HID_MAX_RESET_ATTEMPTS) { + dev_err(dev, "unresponsive device, aborting\n"); + spi_hid_stop_hid(shid); + shid->ops->assert_reset(shid->ops); + error =3D shid->ops->power_down(shid->ops); + if (error) { + dev_err(dev, "failed to disable regulator\n"); + shid->regulator_error_count++; + shid->regulator_last_error =3D error; + } + return; + } + + clear_bit(SPI_HID_READY, &shid->flags); + set_bit(SPI_HID_RESET_PENDING, &shid->flags); + + shid->ops->assert_reset(shid->ops); + + shid->power_state =3D HIDSPI_OFF; + + /* + * We want to cancel pending reset work as the device is being reset + * to recover from an error. cancel_work_sync will put us in a deadlock + * because this function is scheduled in 'reset_work' and we should + * avoid waiting for itself. + */ + cancel_work(&shid->reset_work); + + shid->ops->sleep_minimal_reset_delay(shid->ops); + + shid->power_state =3D HIDSPI_ON; + + shid->ops->deassert_reset(shid->ops); +} + static int __spi_hid_send_output_report(struct spi_hid *shid, struct spi_hid_output_report *report) { @@ -213,6 +411,7 @@ static int __spi_hid_send_output_report(struct spi_hid = *shid, return -E2BIG; } =20 + guard(mutex)(&shid->io_lock); spi_hid_populate_output_header(buf->header, shid->conf, report); =20 if (report->content_length) @@ -263,6 +462,88 @@ static int spi_hid_sync_request(struct spi_hid *shid, return 0; } =20 +/* + * Handle the reset response from the FW by sending a request for the devi= ce + * descriptor. + */ +static void spi_hid_reset_response(struct spi_hid *shid) +{ + struct device *dev =3D &shid->spi->dev; + struct spi_hid_output_report report =3D { + .report_type =3D DEVICE_DESCRIPTOR, + .content_length =3D 0x0, + .content_id =3D SPI_HID_OUTPUT_REPORT_CONTENT_ID_DESC_REQUEST, + .content =3D NULL, + }; + int error; + + if (test_bit(SPI_HID_READY, &shid->flags)) { + dev_err(dev, "Spontaneous FW reset!\n"); + clear_bit(SPI_HID_READY, &shid->flags); + shid->dir_count++; + } + + if (shid->power_state =3D=3D HIDSPI_OFF) + return; + + error =3D spi_hid_sync_request(shid, &report); + if (error) { + dev_WARN_ONCE(dev, true, + "Failed to send device descriptor request: %d\n", error); + set_bit(SPI_HID_ERROR, &shid->flags); + schedule_work(&shid->reset_work); + } +} + +static int spi_hid_input_report_handler(struct spi_hid *shid, + struct spi_hid_input_buf *buf) +{ + struct device *dev =3D &shid->spi->dev; + struct spi_hid_input_report r; + int error =3D 0; + + guard(mutex)(&shid->io_lock); + + if (!test_bit(SPI_HID_READY, &shid->flags) || + test_bit(SPI_HID_REFRESH_IN_PROGRESS, &shid->flags) || !shid->hid) { + dev_err(dev, "HID not ready\n"); + return 0; + } + + spi_hid_input_report_prepare(buf, &r); + + error =3D hid_input_report(shid->hid, HID_INPUT_REPORT, + r.content - 1, r.content_length + 1, 1); + + if (error =3D=3D -ENODEV || error =3D=3D -EBUSY) { + dev_err(dev, "ignoring report --> %d\n", error); + return 0; + } else if (error) { + dev_err(dev, "Bad input report: %d\n", error); + } + + return error; +} + +static void spi_hid_response_handler(struct spi_hid *shid, + struct input_report_body_header *body) +{ + shid->response_length =3D body->content_len; + /* completion_done returns 0 if there are waiters, otherwise 1 */ + if (completion_done(&shid->output_done)) { + dev_err(&shid->spi->dev, "Unexpected response report\n"); + } else { + if (body->input_report_type =3D=3D REPORT_DESCRIPTOR_RESPONSE || + body->input_report_type =3D=3D GET_FEATURE_RESPONSE) { + memcpy(shid->response->body, shid->input->body, + sizeof(shid->input->body)); + memcpy(shid->response->content, shid->input->content, + body->content_len); + } + complete(&shid->output_done); + } +} + /* * This function returns the length of the report descriptor, or a negative * error code if something went wrong. @@ -282,6 +563,8 @@ static int spi_hid_report_descriptor_request(struct spi= _hid *shid) if (ret) { dev_err(dev, "Expected report descriptor not received: %d\n", ret); + set_bit(SPI_HID_ERROR, &shid->flags); + schedule_work(&shid->reset_work); return ret; } =20 @@ -320,7 +603,9 @@ static int spi_hid_create_device(struct spi_hid *shid) hid->vendor, hid->product); strscpy(hid->phys, dev_name(&shid->spi->dev), sizeof(hid->phys)); =20 - shid->hid =3D hid; + scoped_guard(mutex, &shid->io_lock) { + shid->hid =3D hid; + } =20 error =3D hid_add_device(hid); if (error) { @@ -336,6 +621,208 @@ static int spi_hid_create_device(struct spi_hid *shid) return 0; } =20 +static void spi_hid_refresh_device(struct spi_hid *shid) +{ + struct device *dev =3D &shid->spi->dev; + u32 new_crc32 =3D 0; + int error =3D 0; + + error =3D spi_hid_report_descriptor_request(shid); + if (error < 0) { + dev_err(dev, + "%s: failed report descriptor request: %d\n", + __func__, error); + return; + } + new_crc32 =3D crc32_le(0, (unsigned char const *)shid->response->content, + (size_t)error); + + /* Same report descriptor, so no need to create a new hid device. */ + if (new_crc32 =3D=3D shid->report_descriptor_crc32) { + set_bit(SPI_HID_READY, &shid->flags); + return; + } + + shid->report_descriptor_crc32 =3D new_crc32; + + set_bit(SPI_HID_REFRESH_IN_PROGRESS, &shid->flags); + + spi_hid_stop_hid(shid); + + error =3D spi_hid_create_device(shid); + if (error) { + dev_err(dev, "%s: Failed to create hid device: %d\n", __func__, error); + return; + } + + clear_bit(SPI_HID_REFRESH_IN_PROGRESS, &shid->flags); +} + +static void spi_hid_reset_work(struct work_struct *work) +{ + struct spi_hid *shid =3D + container_of(work, struct spi_hid, reset_work); + struct device *dev =3D &shid->spi->dev; + int error =3D 0; + bool resched =3D false; + + if (test_and_clear_bit(SPI_HID_RESET_RESPONSE, &shid->flags)) { + spi_hid_reset_response(shid); + resched =3D true; + } else if (test_and_clear_bit(SPI_HID_CREATE_DEVICE, &shid->flags)) { + guard(mutex)(&shid->power_lock); + if (shid->power_state !=3D HIDSPI_OFF) { + if (!shid->hid) { + error =3D spi_hid_create_device(shid); + if (error) { + dev_err(dev, "%s: Failed to create hid device: %d\n", + __func__, error); + } + } else { + spi_hid_refresh_device(shid); + } + } else { + dev_err(dev, "%s: Powered off, returning\n", __func__); + } + resched =3D true; + } else if (test_and_clear_bit(SPI_HID_ERROR, &shid->flags)) { + spi_hid_error_handler(shid); + } + + /* + * If other flags are still pending, safely reschedule ourselves + * to process them in the next workqueue cycle. + */ + if (resched && (shid->flags & (BIT(SPI_HID_RESET_RESPONSE) | + BIT(SPI_HID_CREATE_DEVICE) | + BIT(SPI_HID_ERROR)))) { + schedule_work(&shid->reset_work); + } +} + +static int spi_hid_process_input_report(struct spi_hid *shid, + struct spi_hid_input_buf *buf) +{ + struct spi_hid_input_header header; + struct input_report_body_header body; + struct device *dev =3D &shid->spi->dev; + struct hidspi_dev_descriptor *raw; + + spi_hid_populate_input_header(buf->header, &header); + spi_hid_populate_input_body(buf->body, &body); + + if (HIDSPI_INPUT_BODY_SIZE(body.content_len) > header.report_length) { + dev_err(dev, "Bad body length %zu > %u\n", HIDSPI_INPUT_BODY_SIZE(body.c= ontent_len), + header.report_length); + return -EPROTO; + } + + switch (body.input_report_type) { + case DATA: + return spi_hid_input_report_handler(shid, buf); + case RESET_RESPONSE: + clear_bit(SPI_HID_RESET_PENDING, &shid->flags); + set_bit(SPI_HID_RESET_RESPONSE, &shid->flags); + schedule_work(&shid->reset_work); + break; + case DEVICE_DESCRIPTOR_RESPONSE: + /* Mark the completion done to avoid timeout */ + spi_hid_response_handler(shid, &body); + + /* Reset attempts at every device descriptor fetch */ + shid->reset_attempts =3D 0; + raw =3D (struct hidspi_dev_descriptor *)buf->content; + + /* Validate device descriptor length before parsing */ + if (body.content_len !=3D HIDSPI_DEVICE_DESCRIPTOR_SIZE) { + dev_err(dev, "Invalid content length %d, expected %zu\n", + body.content_len, + HIDSPI_DEVICE_DESCRIPTOR_SIZE); + return -EPROTO; + } + + if (le16_to_cpu(raw->dev_desc_len) !=3D + HIDSPI_DEVICE_DESCRIPTOR_SIZE) { + dev_err(dev, + "Invalid wDeviceDescLength %d, expected %zu\n", + le16_to_cpu(raw->dev_desc_len), + HIDSPI_DEVICE_DESCRIPTOR_SIZE); + return -EPROTO; + } + + spi_hid_parse_dev_desc(raw, &shid->desc); + + if (shid->desc.hid_version !=3D SPI_HID_SUPPORTED_VERSION) { + dev_err(dev, + "Unsupported device descriptor version %4x\n", + shid->desc.hid_version); + return -EPROTONOSUPPORT; + } + + set_bit(SPI_HID_CREATE_DEVICE, &shid->flags); + schedule_work(&shid->reset_work); + + break; + case OUTPUT_REPORT_RESPONSE: + if (shid->desc.no_output_report_ack) { + dev_err(dev, "Unexpected output report response\n"); + break; + } + fallthrough; + case GET_FEATURE_RESPONSE: + case SET_FEATURE_RESPONSE: + case REPORT_DESCRIPTOR_RESPONSE: + spi_hid_response_handler(shid, &body); + break; + /* + * FIXME: sending GET_INPUT and COMMAND reports not supported, thus + * throw away responses to those, they should never come. + */ + case GET_INPUT_REPORT_RESPONSE: + case COMMAND_RESPONSE: + dev_err(dev, "Not a supported report type: 0x%x\n", + body.input_report_type); + break; + default: + dev_err(dev, "Unknown input report: 0x%x\n", body.input_report_type); + return -EPROTO; + } + + return 0; +} + +static int spi_hid_bus_validate_header(struct spi_hid *shid, + struct spi_hid_input_header *header) +{ + struct device *dev =3D &shid->spi->dev; + + if (header->version !=3D SPI_HID_INPUT_HEADER_VERSION) { + dev_err(dev, "Unknown input report version (v 0x%x)\n", + header->version); + return -EINVAL; + } + + if (shid->desc.max_input_length !=3D 0 && + header->report_length > shid->desc.max_input_length) { + dev_err(dev, "Input report body size %u > max expected of %u\n", + header->report_length, shid->desc.max_input_length); + return -EMSGSIZE; + } + + if (header->last_fragment_flag !=3D 1) { + dev_err(dev, "Multi-fragment reports not supported\n"); + return -EOPNOTSUPP; + } + + if (header->sync_const !=3D SPI_HID_INPUT_HEADER_SYNC_BYTE) { + dev_err(dev, "Invalid input report sync constant (0x%x)\n", + header->sync_const); + return -EINVAL; + } + + return 0; +} + static int spi_hid_get_request(struct spi_hid *shid, u8 content_id) { struct device *dev =3D &shid->spi->dev; @@ -352,6 +839,8 @@ static int spi_hid_get_request(struct spi_hid *shid, u8= content_id) dev_err(dev, "Expected get request response not received! Error %d\n", error); + set_bit(SPI_HID_ERROR, &shid->flags); + schedule_work(&shid->reset_work); return error; } =20 @@ -371,9 +860,83 @@ static int spi_hid_set_request(struct spi_hid *shid, u= 8 *arg_buf, u16 arg_len, return spi_hid_sync_request(shid, &report); } =20 -/* This is a placeholder. Will be implemented in the next patch. */ static irqreturn_t spi_hid_dev_irq(int irq, void *_shid) { + struct spi_hid *shid =3D _shid; + struct device *dev =3D &shid->spi->dev; + struct spi_hid_input_header header; + int error =3D 0; + + scoped_guard(mutex, &shid->io_lock) { + error =3D spi_hid_input_sync(shid, shid->input->header, + sizeof(shid->input->header), true); + if (error) { + dev_err(dev, "Failed to transfer header: %d\n", error); + goto err; + } + + if (shid->power_state =3D=3D HIDSPI_OFF) { + dev_warn(dev, "Device is off after header was received\n"); + goto out; + } + + if (shid->input_message.status < 0) { + dev_warn(dev, "Error reading header: %d\n", + shid->input_message.status); + shid->bus_error_count++; + shid->bus_last_error =3D shid->input_message.status; + goto err; + } + + spi_hid_populate_input_header(shid->input->header, &header); + + error =3D spi_hid_bus_validate_header(shid, &header); + if (error) { + if (!test_bit(SPI_HID_RESET_PENDING, &shid->flags)) { + dev_err(dev, "Failed to validate header: %d\n", error); + print_hex_dump(KERN_ERR, "spi_hid: header buffer: ", + DUMP_PREFIX_NONE, 16, 1, shid->input->header, + sizeof(shid->input->header), false); + shid->bus_error_count++; + shid->bus_last_error =3D error; + goto err; + } + goto out; + } + + error =3D spi_hid_input_sync(shid, shid->input->body, header.report_leng= th, + false); + if (error) { + dev_err(dev, "Failed to transfer body: %d\n", error); + goto err; + } + + if (shid->power_state =3D=3D HIDSPI_OFF) { + dev_warn(dev, "Device is off after body was received\n"); + goto out; + } + + if (shid->input_message.status < 0) { + dev_warn(dev, "Error reading body: %d\n", + shid->input_message.status); + shid->bus_error_count++; + shid->bus_last_error =3D shid->input_message.status; + goto err; + } + } + + error =3D spi_hid_process_input_report(shid, shid->input); + if (error) { + dev_err(dev, "Failed to process input report: %d\n", error); + goto err; + } + +out: + return IRQ_HANDLED; + +err: + set_bit(SPI_HID_ERROR, &shid->flags); + schedule_work(&shid->reset_work); return IRQ_HANDLED; } =20 @@ -610,10 +1173,13 @@ static int spi_hid_ll_output_report(struct hid_devic= e *hid, __u8 *buf, return -ENODEV; } =20 - if (shid->desc.no_output_report_ack) - error =3D spi_hid_send_output_report(shid, &report); - else + if (shid->desc.no_output_report_ack) { + scoped_guard(mutex, &shid->output_lock) { + error =3D spi_hid_send_output_report(shid, &report); + } + } else { error =3D spi_hid_sync_request(shid, &report); + } =20 if (error) { dev_err(dev, "failed to send output report\n"); @@ -701,14 +1267,23 @@ int spi_hid_core_probe(struct spi_device *spi, struc= t spihid_ops *ops, shid->power_state =3D HIDSPI_ON; shid->ops =3D ops; shid->conf =3D conf; + set_bit(SPI_HID_RESET_PENDING, &shid->flags); =20 spi_set_drvdata(spi, shid); =20 + /* Using now populated conf let's pre-calculate the read approvals */ + spi_hid_populate_read_approvals(shid->conf, shid->read_approval_header, + shid->read_approval_body); + mutex_init(&shid->output_lock); + mutex_init(&shid->power_lock); + mutex_init(&shid->io_lock); init_completion(&shid->output_done); =20 + INIT_WORK(&shid->reset_work, spi_hid_reset_work); + /* - * we need to allocate the buffer without knowing the maximum + * We need to allocate the buffer without knowing the maximum * size of the reports. Let's use SZ_2K, then we do the * real computation later. */ @@ -731,7 +1306,7 @@ int spi_hid_core_probe(struct spi_device *spi, struct = spihid_ops *ops, shid->ops->sleep_minimal_reset_delay(shid->ops); =20 error =3D devm_request_threaded_irq(dev, spi->irq, NULL, spi_hid_dev_irq, - IRQF_ONESHOT, dev_name(&spi->dev), shid); + IRQF_ONESHOT | IRQF_NO_AUTOEN, dev_name(&spi->dev), shid); if (error) { dev_err(dev, "%s: unable to request threaded IRQ\n", __func__); return error; @@ -745,13 +1320,11 @@ int spi_hid_core_probe(struct spi_device *spi, struc= t spihid_ops *ops, =20 shid->ops->deassert_reset(shid->ops); =20 + enable_irq(spi->irq); + dev_dbg(dev, "%s: d3 -> %s\n", __func__, spi_hid_power_mode_string(shid->power_state)); =20 - error =3D spi_hid_create_device(shid); - if (error) - return error; - return 0; } EXPORT_SYMBOL_GPL(spi_hid_core_probe); @@ -762,6 +1335,9 @@ void spi_hid_core_remove(struct spi_device *spi) struct device *dev =3D &spi->dev; int error; =20 + disable_irq(spi->irq); + cancel_work_sync(&shid->reset_work); + spi_hid_stop_hid(shid); =20 shid->ops->assert_reset(shid->ops); --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 45F663C063A for ; Tue, 9 Jun 2026 04:41:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980096; cv=none; b=D4KwQ41eTXSp5udz/CgfmBdFIO0hzpA39AOlma+myXX4E7CIizHhCiZGtUDqNrgHiNEeHyla/JzT6Mu7znu5WnVwxCcxdspsyw4xycuVm7hN+KfB4stjXSbHjCdeR5AKKKHRBkyO6yZenJdLSPbCkVfD4eRyyEJ2uic7URc7P9g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980096; c=relaxed/simple; bh=BlGvsanwu50qjmp76cq5mafkU4hZQsG9ywTNB/b1kZc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XQ4XA/gNcAF9ULBXNi9ou9tb2raBN5aLj8CZ9UxJ3i1B5OB1HWfeuH8EcdGZUGrqBoNz4y4cnqOg2mazMtvG6a0j38KLGRWh24JcQJqDOrSTYMJrOQ9bj4Ti2Nz8uKvB+OeXY6/xy5Dj69Br69yF9d/AoTMDXCZdchz1Ep7D2Js= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=HT8r7sms; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="HT8r7sms" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2bf2247e38eso52400495ad.3 for ; Mon, 08 Jun 2026 21:41:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980089; x=1781584889; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=LtHkR4QMbVg9Yiazzyv4o9y9RsI0S+kRRsg11Y6mhFM=; b=HT8r7smsSQWJJ3goU9zgSlaaZA8vj2HxKm9OS4jPiHg1d/v5S3J5jJFxTL73f5/wuK cQyG7mep8yfReiSSzY8eeQCL8u33gSlVKJCnJZOxnqJxA5NguY0dynGkCkny9yZ3/lQ2 7HCu7/cCpH4cmu21Ts2jeZmcpwZu+EO4kHLrg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980089; x=1781584889; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=LtHkR4QMbVg9Yiazzyv4o9y9RsI0S+kRRsg11Y6mhFM=; b=AckiOvv9bJlk0Dr98kWMbU/RLQjfY4dYRLrYTW4szBzOU3p2kjZHq7KSIY4Bu7RUiz ijhJmN2md/t3chuJ/mEanuCcGWcWTh2hCLF1TyPdiuRGXpYfIN+1DWdOA6AS7mX9pQH0 5w/8rKN9qnprRgXBOIdgEZsZ9e0HyuQWnhjT2ijeNGKpnhMFxG+nC15AvcZ2xptmMuz0 SpZChdWlb9yQguGnNp6yzS3k13xItVIyWJhno/3XlCrfd2JXFFsy6vErjWko6hSQWlu+ DDbJXaJnxnvSgzhOgx8OoYKraHvmKpzSXocuN7o/Qj3QMPAYTKOszFFxcUxSfqney6iW ad7w== X-Forwarded-Encrypted: i=1; AFNElJ/s//qArFTzmr0L5LBa3iHOzMnurb4jbo9aoHWabR3O5nYU7bXI4gG4FC7sofjtUFyiWo6uxWRAjIvhA1E=@vger.kernel.org X-Gm-Message-State: AOJu0YxuuryY0A+05HnyeBzK8Zxpr/uP4LWOWD9UsBMKAxF4Mft5jeaI 9khEkr92iRkvC9ZTdMlhaXxZq+DMlpdowR1tt646F0adQRN5fTY/VPdxCEuTXLdkzA== X-Gm-Gg: Acq92OEN7vIu3fjWcNX5c3OCneRjUEzN9sHQkgwL9DFoUzmmMuQwxlVxGqs2Kad7qvE NLukZcGdri5f4eU1SnLjEC+RCRyd5do67qzhuIedVJGcEjL0fSl/gBXlnmw0yWjrvZWYhdN2y6e ZajsjlGjj9lLp3tuZsgEur1G1kgFADnBh/i6Krd9FqFXI8otGmTzHPw96ACbQEwHnZ+UaNP6M0d TDNkqChr+2vraWMFpJbDumUVopK7K6itpAAITi2uJmchQHppRY/Int8aVC/ddR8b+ZYYoHeF0H2 Jni55OZK+mLIFyAQCY7SsQ6Gzrt2s8slIwAaKsnVSuQrAkbxM7QNz+hh+ThAUnm4GPquR+4E4up coiFCE7GVU2Oc3c7xdMwUk3sIE9eN8UWlUxcZmBEJ8UwYUYBgcxxMO5Onbaoi8CMXeFmul0cY2v H14xgAuESG/Yo9kLnRHGv6Qe/KJupJkMb29TpScFZWmRQ2FtjVUfgz3vHQcLLrjeexQTCHyyRtd s0xZD1HDIeN0F/1sL8qMgViUZXaVCmpHM8NV8o07F+M X-Received: by 2002:a17:902:ea0a:b0:2c1:564b:4f47 with SMTP id d9443c01a7336-2c1e8495453mr223319605ad.26.1780980089367; Mon, 08 Jun 2026 21:41:29 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:28 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:40:57 +0000 Subject: [PATCH v4 06/11] HID: spi_hid: add spi_hid traces Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-6-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Dmitry Antipov , Angela Czubak X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=16909; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=BlGvsanwu50qjmp76cq5mafkU4hZQsG9ywTNB/b1kZc=; b=ipw3ciSYQ8XdReNlOZJyItVC4cJoj3cLleJfy3GvuTejr4XfNkmjbzsaHcg2cHXg2ZglEnQSo s0LLvf5sO5wCrL69ED4Xo7hxzr5O0Suv8diaDySECJf0T4rZz4d5FJ6 X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= Add traces for purposed of debugging spi_hid driver. Signed-off-by: Dmitry Antipov Signed-off-by: Angela Czubak Signed-off-by: Jingyuan Liang --- drivers/hid/spi-hid/Makefile | 1 + drivers/hid/spi-hid/spi-hid-core.c | 114 +++++++++--------------- drivers/hid/spi-hid/spi-hid-core.h | 91 +++++++++++++++++++ drivers/hid/spi-hid/spi-hid-trace.h | 169 ++++++++++++++++++++++++++++++++= ++++ 4 files changed, 300 insertions(+), 75 deletions(-) diff --git a/drivers/hid/spi-hid/Makefile b/drivers/hid/spi-hid/Makefile index 92e24cddbfc2..733e006df56e 100644 --- a/drivers/hid/spi-hid/Makefile +++ b/drivers/hid/spi-hid/Makefile @@ -7,3 +7,4 @@ =20 obj-$(CONFIG_SPI_HID_CORE) +=3D spi-hid.o spi-hid-objs =3D spi-hid-core.o +CFLAGS_spi-hid-core.o :=3D -I$(src) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index f6ea2d4365a7..ef527999d6dc 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -44,6 +44,11 @@ #include #include =20 +#include "spi-hid-core.h" + +#define CREATE_TRACE_POINTS +#include "spi-hid-trace.h" + /* Protocol constants */ #define SPI_HID_READ_APPROVAL_CONSTANT 0xff #define SPI_HID_INPUT_HEADER_SYNC_BYTE 0x5a @@ -82,13 +87,6 @@ #define SPI_HID_CREATE_DEVICE 4 #define SPI_HID_ERROR 5 =20 -/* Raw input buffer with data from the bus */ -struct spi_hid_input_buf { - u8 header[HIDSPI_INPUT_HEADER_SIZE]; - u8 body[HIDSPI_INPUT_BODY_HEADER_SIZE]; - u8 content[]; -}; - /* Processed data from input report header */ struct spi_hid_input_header { u8 version; @@ -105,12 +103,6 @@ struct spi_hid_input_report { u8 *content; }; =20 -/* Raw output report buffer to be put on the bus */ -struct spi_hid_output_buf { - u8 header[SPI_HID_OUTPUT_HEADER_LEN]; - u8 content[]; -}; - /* Data necessary to send an output report */ struct spi_hid_output_report { u8 report_type; @@ -119,19 +111,6 @@ struct spi_hid_output_report { u8 *content; }; =20 -/* Processed data from a device descriptor */ -struct spi_hid_device_descriptor { - u16 hid_version; - u16 report_descriptor_length; - u16 max_input_length; - u16 max_output_length; - u16 max_fragment_length; - u16 vendor_id; - u16 product_id; - u16 version_id; - u8 no_output_report_ack; -}; - /* struct spi_hid_conf - Conf provided to the core */ struct spi_hid_conf { u32 input_report_header_address; @@ -157,55 +136,6 @@ struct spihid_ops { void (*sleep_minimal_reset_delay)(struct spihid_ops *ops); }; =20 -/* Driver context */ -struct spi_hid { - struct spi_device *spi; /* spi device. */ - struct hid_device *hid; /* pointer to corresponding HID dev. */ - - struct spi_transfer input_transfer[2]; /* Transfer buffer for read and wr= ite. */ - struct spi_message input_message; /* used to execute a sequence of spi tr= ansfers. */ - - struct spihid_ops *ops; - struct spi_hid_conf *conf; - - struct spi_hid_device_descriptor desc; /* HID device descriptor. */ - struct spi_hid_output_buf *output; /* Output buffer. */ - struct spi_hid_input_buf *input; /* Input buffer. */ - struct spi_hid_input_buf *response; /* Response buffer. */ - - u16 response_length; - u16 bufsize; - - enum hidspi_power_state power_state; - - u8 reset_attempts; /* The number of reset attempts. */ - - unsigned long flags; /* device flags. */ - - struct work_struct reset_work; - - /* Control lock to ensure complete output transaction. */ - struct mutex output_lock; - /* Power lock to make sure one power state change at a time. */ - struct mutex power_lock; - /* Protect bus I/O and shid->hid pointer lifecycle. */ - struct mutex io_lock; - - struct completion output_done; - - u32 report_descriptor_crc32; /* HID report descriptor crc32 checksum. */ - - u32 regulator_error_count; - int regulator_last_error; - u32 bus_error_count; - int bus_last_error; - u32 dir_count; /* device initiated reset count. */ - - /* DMA-safe transfer buffers */ - u8 read_approval_header[SPI_HID_READ_APPROVAL_LEN] ____cacheline_aligned; - u8 read_approval_body[SPI_HID_READ_APPROVAL_LEN]; -}; - static struct hid_ll_driver spi_hid_ll_driver; =20 static void spi_hid_populate_read_approvals(const struct spi_hid_conf *con= f, @@ -295,6 +225,11 @@ static int spi_hid_input_sync(struct spi_hid *shid, vo= id *buf, u16 length, spi_message_init_with_transfers(&shid->input_message, shid->input_transfer, 2); =20 + trace_spi_hid_input_sync(shid, shid->input_transfer[0].tx_buf, + shid->input_transfer[0].len, + shid->input_transfer[1].rx_buf, + shid->input_transfer[1].len, 0); + error =3D spi_sync(shid->spi, &shid->input_message); if (error) { dev_err(&shid->spi->dev, "Error starting sync transfer: %d\n", error); @@ -353,6 +288,8 @@ static void spi_hid_error_handler(struct spi_hid *shid) struct device *dev =3D &shid->spi->dev; int error; =20 + trace_spi_hid_error_handler(shid); + guard(mutex)(&shid->power_lock); if (shid->power_state =3D=3D HIDSPI_OFF) return; @@ -477,6 +414,8 @@ static void spi_hid_reset_response(struct spi_hid *shid) }; int error; =20 + trace_spi_hid_reset_response(shid); + if (test_bit(SPI_HID_READY, &shid->flags)) { dev_err(dev, "Spontaneous FW reset!\n"); clear_bit(SPI_HID_READY, &shid->flags); @@ -503,6 +442,7 @@ static int spi_hid_input_report_handler(struct spi_hid = *shid, int error =3D 0; =20 guard(mutex)(&shid->io_lock); + trace_spi_hid_input_report_handler(shid); =20 if (!test_bit(SPI_HID_READY, &shid->flags) || test_bit(SPI_HID_REFRESH_IN_PROGRESS, &shid->flags) || !shid->hid) { @@ -528,6 +468,8 @@ static int spi_hid_input_report_handler(struct spi_hid = *shid, static void spi_hid_response_handler(struct spi_hid *shid, struct input_report_body_header *body) { + trace_spi_hid_response_handler(shid); + shid->response_length =3D body->content_len; /* completion_done returns 0 if there are waiters, otherwise 1 */ if (completion_done(&shid->output_done)) { @@ -584,6 +526,8 @@ static int spi_hid_create_device(struct spi_hid *shid) struct device *dev =3D &shid->spi->dev; int error; =20 + trace_spi_hid_create_device(shid); + hid =3D hid_allocate_device(); error =3D PTR_ERR_OR_ZERO(hid); if (error) { @@ -627,6 +571,8 @@ static void spi_hid_refresh_device(struct spi_hid *shid) u32 new_crc32 =3D 0; int error =3D 0; =20 + trace_spi_hid_refresh_device(shid); + error =3D spi_hid_report_descriptor_request(shid); if (error < 0) { dev_err(dev, @@ -708,6 +654,8 @@ static int spi_hid_process_input_report(struct spi_hid = *shid, struct device *dev =3D &shid->spi->dev; struct hidspi_dev_descriptor *raw; =20 + trace_spi_hid_process_input_report(shid); + spi_hid_populate_input_header(buf->header, &header); spi_hid_populate_input_body(buf->body, &body); =20 @@ -867,6 +815,9 @@ static irqreturn_t spi_hid_dev_irq(int irq, void *_shid) struct spi_hid_input_header header; int error =3D 0; =20 + trace_spi_hid_dev_irq(shid, irq); + trace_spi_hid_header_transfer(shid); + scoped_guard(mutex, &shid->io_lock) { error =3D spi_hid_input_sync(shid, shid->input->header, sizeof(shid->input->header), true); @@ -880,6 +831,13 @@ static irqreturn_t spi_hid_dev_irq(int irq, void *_shi= d) goto out; } =20 + trace_spi_hid_input_header_complete(shid, + shid->input_transfer[0].tx_buf, + shid->input_transfer[0].len, + shid->input_transfer[1].rx_buf, + shid->input_transfer[1].len, + shid->input_message.status); + if (shid->input_message.status < 0) { dev_warn(dev, "Error reading header: %d\n", shid->input_message.status); @@ -916,6 +874,12 @@ static irqreturn_t spi_hid_dev_irq(int irq, void *_shi= d) goto out; } =20 + trace_spi_hid_input_body_complete(shid, shid->input_transfer[0].tx_buf, + shid->input_transfer[0].len, + shid->input_transfer[1].rx_buf, + shid->input_transfer[1].len, + shid->input_message.status); + if (shid->input_message.status < 0) { dev_warn(dev, "Error reading body: %d\n", shid->input_message.status); diff --git a/drivers/hid/spi-hid/spi-hid-core.h b/drivers/hid/spi-hid/spi-h= id-core.h new file mode 100644 index 000000000000..293e2cfcfbf7 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-core.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Microsoft Corporation + * Copyright (c) 2026 Google LLC + */ + +#ifndef SPI_HID_CORE_H +#define SPI_HID_CORE_H + +#include +#include + +/* Protocol message size constants */ +#define SPI_HID_READ_APPROVAL_LEN 5 +#define SPI_HID_OUTPUT_HEADER_LEN 8 + +/* Raw input buffer with data from the bus */ +struct spi_hid_input_buf { + u8 header[HIDSPI_INPUT_HEADER_SIZE]; + u8 body[HIDSPI_INPUT_BODY_HEADER_SIZE]; + u8 content[]; +}; + +/* Raw output report buffer to be put on the bus */ +struct spi_hid_output_buf { + u8 header[SPI_HID_OUTPUT_HEADER_LEN]; + u8 content[]; +}; + +/* Processed data from a device descriptor */ +struct spi_hid_device_descriptor { + u16 hid_version; + u16 report_descriptor_length; + u16 max_input_length; + u16 max_output_length; + u16 max_fragment_length; + u16 vendor_id; + u16 product_id; + u16 version_id; + u8 no_output_report_ack; +}; + +/* Driver context */ +struct spi_hid { + struct spi_device *spi; /* spi device. */ + struct hid_device *hid; /* pointer to corresponding HID dev. */ + + struct spi_transfer input_transfer[2]; /* Transfer buffer for read and wr= ite. */ + struct spi_message input_message; /* used to execute a sequence of spi tr= ansfers. */ + + struct spihid_ops *ops; + struct spi_hid_conf *conf; + + struct spi_hid_device_descriptor desc; /* HID device descriptor. */ + struct spi_hid_output_buf *output; /* Output buffer. */ + struct spi_hid_input_buf *input; /* Input buffer. */ + struct spi_hid_input_buf *response; /* Response buffer. */ + + u16 response_length; + u16 bufsize; + + enum hidspi_power_state power_state; + + u8 reset_attempts; /* The number of reset attempts. */ + + unsigned long flags; /* device flags. */ + + struct work_struct reset_work; + + /* Control lock to ensure complete output transaction. */ + struct mutex output_lock; + /* Power lock to make sure one power state change at a time. */ + struct mutex power_lock; + /* I/O lock to prevent concurrent output writes during the input read. */ + struct mutex io_lock; + + struct completion output_done; + + u8 read_approval_header[SPI_HID_READ_APPROVAL_LEN]; + u8 read_approval_body[SPI_HID_READ_APPROVAL_LEN]; + + u32 report_descriptor_crc32; /* HID report descriptor crc32 checksum. */ + + u32 regulator_error_count; + int regulator_last_error; + u32 bus_error_count; + int bus_last_error; + u32 dir_count; /* device initiated reset count. */ +}; + +#endif /* SPI_HID_CORE_H */ diff --git a/drivers/hid/spi-hid/spi-hid-trace.h b/drivers/hid/spi-hid/spi-= hid-trace.h new file mode 100644 index 000000000000..841ec491826d --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-trace.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Microsoft Corporation + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM spi_hid + +#if !defined(_SPI_HID_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _SPI_HID_TRACE_H + +#include +#include +#include "spi-hid-core.h" + +DECLARE_EVENT_CLASS(spi_hid_transfer, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret), + + TP_STRUCT__entry( + __field(int, bus_num) + __field(int, chip_select) + __field(int, ret) + __dynamic_array(u8, rx_buf, rx_len) + __dynamic_array(u8, tx_buf, tx_len) + ), + + TP_fast_assign( + __entry->bus_num =3D shid->spi->controller->bus_num; + __entry->chip_select =3D spi_get_chipselect(shid->spi, 0); + __entry->ret =3D ret; + + memcpy(__get_dynamic_array(tx_buf), tx_buf, tx_len); + memcpy(__get_dynamic_array(rx_buf), rx_buf, rx_len); + ), + + TP_printk("spi%d.%d: len=3D%d tx=3D[%*phD] rx=3D[%*phD] --> %d", + __entry->bus_num, __entry->chip_select, + __get_dynamic_array_len(tx_buf) + __get_dynamic_array_len(rx_buf), + __get_dynamic_array_len(tx_buf), __get_dynamic_array(tx_buf), + __get_dynamic_array_len(rx_buf), __get_dynamic_array(rx_buf), + __entry->ret) +); + +DEFINE_EVENT(spi_hid_transfer, spi_hid_input_sync, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret)); + +DEFINE_EVENT(spi_hid_transfer, spi_hid_input_header_complete, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret)); + +DEFINE_EVENT(spi_hid_transfer, spi_hid_input_body_complete, + TP_PROTO(struct spi_hid *shid, const void *tx_buf, int tx_len, + const void *rx_buf, u16 rx_len, int ret), + TP_ARGS(shid, tx_buf, tx_len, rx_buf, rx_len, ret)); + +DECLARE_EVENT_CLASS(spi_hid_irq, + TP_PROTO(struct spi_hid *shid, int irq), + + TP_ARGS(shid, irq), + + TP_STRUCT__entry( + __field(int, bus_num) + __field(int, chip_select) + __field(int, irq) + ), + + TP_fast_assign( + __entry->bus_num =3D shid->spi->controller->bus_num; + __entry->chip_select =3D spi_get_chipselect(shid->spi, 0); + __entry->irq =3D irq; + ), + + TP_printk("spi%d.%d: IRQ %d", + __entry->bus_num, __entry->chip_select, __entry->irq) +); + +DEFINE_EVENT(spi_hid_irq, spi_hid_dev_irq, + TP_PROTO(struct spi_hid *shid, int irq), TP_ARGS(shid, irq)); + +DECLARE_EVENT_CLASS(spi_hid, + TP_PROTO(struct spi_hid *shid), + + TP_ARGS(shid), + + TP_STRUCT__entry( + __field(int, bus_num) + __field(int, chip_select) + __field(int, power_state) + __field(u32, flags) + + __field(int, vendor_id) + __field(int, product_id) + __field(int, max_input_length) + __field(int, max_output_length) + __field(u16, hid_version) + __field(u16, report_descriptor_length) + __field(u16, version_id) + ), + + TP_fast_assign( + __entry->bus_num =3D shid->spi->controller->bus_num; + __entry->chip_select =3D spi_get_chipselect(shid->spi, 0); + __entry->power_state =3D shid->power_state; + __entry->flags =3D shid->flags; + + __entry->vendor_id =3D shid->desc.vendor_id; + __entry->product_id =3D shid->desc.product_id; + __entry->max_input_length =3D shid->desc.max_input_length; + __entry->max_output_length =3D shid->desc.max_output_length; + __entry->hid_version =3D shid->desc.hid_version; + __entry->report_descriptor_length =3D + shid->desc.report_descriptor_length; + __entry->version_id =3D shid->desc.version_id; + ), + + TP_printk("spi%d.%d: (%04x:%04x v%d) HID v%d.%d state p:%d len i:%d o:%d = r:%d flags 0x%08x", + __entry->bus_num, __entry->chip_select, + __entry->vendor_id, __entry->product_id, __entry->version_id, + __entry->hid_version >> 8, __entry->hid_version & 0xff, + __entry->power_state, __entry->max_input_length, + __entry->max_output_length, __entry->report_descriptor_length, + __entry->flags) +); + +DEFINE_EVENT(spi_hid, spi_hid_header_transfer, TP_PROTO(struct spi_hid *sh= id), + TP_ARGS(shid)); + +DEFINE_EVENT(spi_hid, spi_hid_process_input_report, + TP_PROTO(struct spi_hid *shid), TP_ARGS(shid)); + +DEFINE_EVENT(spi_hid, spi_hid_input_report_handler, + TP_PROTO(struct spi_hid *shid), TP_ARGS(shid)); + +DEFINE_EVENT(spi_hid, spi_hid_reset_response, TP_PROTO(struct spi_hid *shi= d), + TP_ARGS(shid)); + +DEFINE_EVENT(spi_hid, spi_hid_create_device, TP_PROTO(struct spi_hid *shid= ), + TP_ARGS(shid)); + +DEFINE_EVENT(spi_hid, spi_hid_refresh_device, TP_PROTO(struct spi_hid *shi= d), + TP_ARGS(shid)); + +DEFINE_EVENT(spi_hid, spi_hid_response_handler, TP_PROTO(struct spi_hid *s= hid), + TP_ARGS(shid)); + +DEFINE_EVENT(spi_hid, spi_hid_error_handler, TP_PROTO(struct spi_hid *shid= ), + TP_ARGS(shid)); + +#endif /* _SPI_HID_TRACE_H */ + +/* + * The following must be outside the protection of the above #if block. + */ +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . + +/* + * It is required that the TRACE_INCLUDE_FILE be the same + * as this file without the ".h". + */ +#define TRACE_INCLUDE_FILE spi-hid-trace +#include --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f169.google.com (mail-pl1-f169.google.com [209.85.214.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 012813C6A56 for ; Tue, 9 Jun 2026 04:41:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980097; cv=none; b=YuMWLoV4l5CeeEsoc9uPvoorBBQCoWAUSzma3DYCE0xVwuNyrWpgnB05aIEFGqpjomDOMZMwUgo6I9oCXRW+TfmYDWHS3HD6dKkH1KpXPWwH5Tgj/ftx8/hK5J3td4Mtl5i5UeKMTJjobhCIhw2RsiFoddJeQfMnWO22CbD6VzA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980097; c=relaxed/simple; bh=hG0V40iV/Y7c7cNuDaz97ooDhxn+35MGXdTaPNVx1A0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BmLaeowOyabamZvNeC9WRX6ZsRZEKmHYs6tA8ExGAXBiZtdvkwrjdqP5Q5maVkvpfQg2xInNT2v1iNMK+/ZvfDO+4y6S9Vi2WiT8zHgUyoPAumlr9wKLmKIPHPeuKpRmdNJHz5Hfcn4YDHQYp+B5rT2R6JfQ2c8zf+Jf3mrpdYo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=L/Etf8Au; arc=none smtp.client-ip=209.85.214.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="L/Etf8Au" Received: by mail-pl1-f169.google.com with SMTP id d9443c01a7336-2bf36a6905cso35186305ad.3 for ; Mon, 08 Jun 2026 21:41:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980090; x=1781584890; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=gWXDu3/HcuOfVv64RI+wowNxmcFlPuxMZRAdD/JxsdM=; b=L/Etf8AubDbw55dAIGKYc0bI8QVgeaFQ9bjhrvxF95CGthaPgY7QmUe5obDqgm/IaB KrMnsR8y7p0NSEfVuv8g9yDuyWXDlqR9SvEQWwYDAoUKZ3Hbr5XwXFdVboVJvIR0fV5q yQ804bof3QcKwxRx499y6DqF+sKyxPpvT8bxA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980090; x=1781584890; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=gWXDu3/HcuOfVv64RI+wowNxmcFlPuxMZRAdD/JxsdM=; b=o1bOKZ+1xGQeVISQ4+V4++zBhsRNkA6YsFMfBuokAXoPexmOW1/MBHVCXLwTQLB9h/ /DNRuqj3dVPOlsjHDQ3pXQAmAdIlZsFS2NmXO8ESSFCzXrRrbnotn1fG6cnI1G3fTX9p jjvIj3QTJizZp/9ch5yqTajka0VWdutTjg9bflm9PFspwmzNValf1Xq+6blH8zMx99Ld ydLCUa05d5Mdm1CiKkSyRuDgAGhFdNf9tfqYF58m3sT7Tl+csJ9FGm/0aQsRhxnj/sYE RUF0/AEYns91XXrXtkoUPbLaLmBBZWVsLTp22q8rRXH1eI4Ob2jGHmFYwRaU3Zgbv0xC y6zw== X-Forwarded-Encrypted: i=1; AFNElJ81FrsKcPjFDvgbk1tjL1uG0NvuLJBPneesd/svqlcwtMRaYS6y5mEb8tcpawB7omHqufJ2pPdljAoQi8c=@vger.kernel.org X-Gm-Message-State: AOJu0YyeWYKEW4/C4zADm5P8D2G/jOf5o+LYIglu/GjKEyNj70LWxV+b OJPQaZPnsOjYudti2tNmBYEz+siMyW8Ik/jJuo0EXlYIf6ilhiz2hnVelZPG3DYFzA== X-Gm-Gg: Acq92OGdnaD5Jg6fHWc+T6DbrUfQ2G9Vh4NAw7iQlPzrZ0R7OMe7hYc6X6DmcAqNiFw eYuOdUUb7rRgEjZOBGfj7pcDXGZac1AJNCgMRQrt7QoHArFKQ1bf6SAnRrQhkWX+xZ8Lsv8JbBh 6KLbN4QCM7qWbK9jFxwg6HGxIgH/eHvE9fu3/+1BBQfQstQJfufqPzx5TJO2tXGoDtdvBk3h6+H WT7oddrNLLYVfVx2hzbdXKI51//SlmVn0bS6wDmbjyaZZhgcFXM3ficHr0FMoG2mBHgqAK50COL ak2cYUy+Q2mH3R47Ndopb4V4bMv2mF4TYcxdie7xjlBRMvqfSoGozywYB6vzMXWvrOPzOkLMVug cM25FOYQJrinJ5l4N795YrVgapNGi5YeJJeCvCihr9dHP5/Y2LTPjwHEhcmyk8pHt9yUPuAJrLx G5/MKTf5UssLrquXoKFz8nJ2seHQex1VbjLbjsn8IbbEQyD2LrBv7JnrUTT2kU8q7Xzijdfh044 LnrFr4oCloXTrMCoceWYeOsvKLSlDGggb1hoQn09dW0 X-Received: by 2002:a17:902:ebc3:b0:2c2:7baf:139f with SMTP id d9443c01a7336-2c2a1c9b30dmr15234975ad.30.1780980090158; Mon, 08 Jun 2026 21:41:30 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:29 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:40:58 +0000 Subject: [PATCH v4 07/11] HID: spi_hid: add ACPI support for SPI over HID Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-7-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Angela Czubak X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=12288; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=8M33xXmQDVVlf40o5Lxcmspv2S4S8yDzo/hCbMSduTg=; b=flz8ewSEJFt20YDARC8V5qyqpFQLht3Vfbh62au5eu6TsVv2Frut88X1L0shA0q8dQZ81B2d8 d/mxxN7vZjEAWse7Y/gqJmmUmYjNbqpwpgn9/7z9VDgjbMH4dYXUWx9 X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= From: Angela Czubak Detect SPI HID devices described in ACPI. Signed-off-by: Angela Czubak Reviewed-by: Dmitry Torokhov Signed-off-by: Jingyuan Liang --- drivers/hid/spi-hid/Kconfig | 15 +++ drivers/hid/spi-hid/Makefile | 1 + drivers/hid/spi-hid/spi-hid-acpi.c | 253 +++++++++++++++++++++++++++++++++= ++++ drivers/hid/spi-hid/spi-hid-core.c | 26 +--- drivers/hid/spi-hid/spi-hid.h | 45 +++++++ 5 files changed, 315 insertions(+), 25 deletions(-) diff --git a/drivers/hid/spi-hid/Kconfig b/drivers/hid/spi-hid/Kconfig index 836fdefe8345..114b1e00da39 100644 --- a/drivers/hid/spi-hid/Kconfig +++ b/drivers/hid/spi-hid/Kconfig @@ -10,6 +10,21 @@ menuconfig SPI_HID =20 if SPI_HID =20 +config SPI_HID_ACPI + tristate "HID over SPI transport layer ACPI driver" + depends on ACPI + select SPI_HID_CORE + help + Say Y here if you use a keyboard, a touchpad, a touchscreen, or any + other HID based devices which are connected to your computer via SPI. + This driver supports ACPI-based systems. + + If unsure, say N. + + This support is also available as a module. If so, the module + will be called spi-hid-acpi. It will also build/depend on the + module spi-hid. + config SPI_HID_CORE tristate endif diff --git a/drivers/hid/spi-hid/Makefile b/drivers/hid/spi-hid/Makefile index 733e006df56e..3ca326602643 100644 --- a/drivers/hid/spi-hid/Makefile +++ b/drivers/hid/spi-hid/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_SPI_HID_CORE) +=3D spi-hid.o spi-hid-objs =3D spi-hid-core.o CFLAGS_spi-hid-core.o :=3D -I$(src) +obj-$(CONFIG_SPI_HID_ACPI) +=3D spi-hid-acpi.o diff --git a/drivers/hid/spi-hid/spi-hid-acpi.c b/drivers/hid/spi-hid/spi-h= id-acpi.c new file mode 100644 index 000000000000..298e3ba44d8a --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-acpi.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HID over SPI protocol, ACPI related code + * + * Copyright (c) 2021 Microsoft Corporation + * Copyright (c) 2026 Google LLC + * + * This code was forked out of the HID over SPI core code, which is partia= lly + * based on "HID over I2C protocol implementation: + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012 Red Hat, Inc + * + * which in turn is partially based on "USB HID support for Linux": + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2,= Inc + * Copyright (c) 2007-2008 Oliver Neukum + * Copyright (c) 2006-2010 Jiri Kosina + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "spi-hid.h" + +/* Config structure is filled with data from ACPI */ +struct spi_hid_acpi_config { + struct spihid_ops ops; + + struct spi_hid_conf property_conf; + u32 post_power_on_delay_ms; + u32 minimal_reset_delay_ms; + struct acpi_device *adev; +}; + +/* HID SPI Device: 6e2ac436-0fcf41af-a265-b32a220dcfab */ +static guid_t spi_hid_guid =3D + GUID_INIT(0x6E2AC436, 0x0FCF, 0x41AF, + 0xA2, 0x65, 0xB3, 0x2A, 0x22, 0x0D, 0xCF, 0xAB); + +static int spi_hid_acpi_populate_config(struct spi_hid_acpi_config *conf, + struct acpi_device *adev) +{ + acpi_handle handle =3D acpi_device_handle(adev); + union acpi_object *obj; + + conf->adev =3D adev; + + /* Revision 3 for HID over SPI V1, see specification. */ + obj =3D acpi_evaluate_dsm_typed(handle, &spi_hid_guid, 3, 1, NULL, + ACPI_TYPE_INTEGER); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call to get HID input report header address failed"); + return -ENODEV; + } + conf->property_conf.input_report_header_address =3D obj->integer.value; + ACPI_FREE(obj); + + obj =3D acpi_evaluate_dsm_typed(handle, &spi_hid_guid, 3, 2, NULL, + ACPI_TYPE_INTEGER); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call to get HID input report body address failed"); + return -ENODEV; + } + conf->property_conf.input_report_body_address =3D obj->integer.value; + ACPI_FREE(obj); + + obj =3D acpi_evaluate_dsm_typed(handle, &spi_hid_guid, 3, 3, NULL, + ACPI_TYPE_INTEGER); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call to get HID output report header address failed"); + return -ENODEV; + } + conf->property_conf.output_report_address =3D obj->integer.value; + ACPI_FREE(obj); + + obj =3D acpi_evaluate_dsm_typed(handle, &spi_hid_guid, 3, 4, NULL, + ACPI_TYPE_BUFFER); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call to get HID read opcode failed"); + return -ENODEV; + } + if (obj->buffer.length =3D=3D 1) { + conf->property_conf.read_opcode =3D obj->buffer.pointer[0]; + } else { + acpi_handle_err(handle, + "Error _DSM call to get HID read opcode, too long buffer"); + ACPI_FREE(obj); + return -ENODEV; + } + ACPI_FREE(obj); + + obj =3D acpi_evaluate_dsm_typed(handle, &spi_hid_guid, 3, 5, NULL, + ACPI_TYPE_BUFFER); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call to get HID write opcode failed"); + return -ENODEV; + } + if (obj->buffer.length =3D=3D 1) { + conf->property_conf.write_opcode =3D obj->buffer.pointer[0]; + } else { + acpi_handle_err(handle, + "Error _DSM call to get HID write opcode, too long buffer"); + ACPI_FREE(obj); + return -ENODEV; + } + ACPI_FREE(obj); + + /* Value not provided in ACPI,*/ + conf->post_power_on_delay_ms =3D 5; + conf->minimal_reset_delay_ms =3D 150; + + if (!acpi_has_method(handle, "_RST")) { + acpi_handle_err(handle, "No reset method for acpi handle"); + return -EINVAL; + } + + /* FIXME: not reading hid-over-spi-flags, multi-SPI not supported */ + + return 0; +} + +static int spi_hid_acpi_power_none(struct spihid_ops *ops) +{ + return 0; +} + +static int spi_hid_acpi_power_down(struct spihid_ops *ops) +{ + struct spi_hid_acpi_config *conf =3D container_of(ops, + struct spi_hid_acpi_config, + ops); + + return acpi_device_set_power(conf->adev, ACPI_STATE_D3); +} + +static int spi_hid_acpi_power_up(struct spihid_ops *ops) +{ + struct spi_hid_acpi_config *conf =3D container_of(ops, + struct spi_hid_acpi_config, + ops); + int error; + + error =3D acpi_device_set_power(conf->adev, ACPI_STATE_D0); + if (error) { + dev_err(&conf->adev->dev, "Error could not power up ACPI device: %d.", e= rror); + return error; + } + + if (conf->post_power_on_delay_ms) + msleep(conf->post_power_on_delay_ms); + + return 0; +} + +static int spi_hid_acpi_assert_reset(struct spihid_ops *ops) +{ + return 0; +} + +static int spi_hid_acpi_deassert_reset(struct spihid_ops *ops) +{ + struct spi_hid_acpi_config *conf =3D container_of(ops, + struct spi_hid_acpi_config, + ops); + + return device_reset(&conf->adev->dev); +} + +static void spi_hid_acpi_sleep_minimal_reset_delay(struct spihid_ops *ops) +{ + struct spi_hid_acpi_config *conf =3D container_of(ops, + struct spi_hid_acpi_config, + ops); + fsleep(1000 * conf->minimal_reset_delay_ms); +} + +static int spi_hid_acpi_probe(struct spi_device *spi) +{ + struct device *dev =3D &spi->dev; + struct acpi_device *adev; + struct spi_hid_acpi_config *config; + int error; + + adev =3D ACPI_COMPANION(dev); + if (!adev) { + dev_err(dev, "Error could not get ACPI device."); + return -ENODEV; + } + + config =3D devm_kzalloc(dev, sizeof(struct spi_hid_acpi_config), + GFP_KERNEL); + if (!config) + return -ENOMEM; + + if (acpi_device_power_manageable(adev)) { + config->ops.power_up =3D spi_hid_acpi_power_up; + config->ops.power_down =3D spi_hid_acpi_power_down; + } else { + config->ops.power_up =3D spi_hid_acpi_power_none; + config->ops.power_down =3D spi_hid_acpi_power_none; + } + config->ops.assert_reset =3D spi_hid_acpi_assert_reset; + config->ops.deassert_reset =3D spi_hid_acpi_deassert_reset; + config->ops.sleep_minimal_reset_delay =3D + spi_hid_acpi_sleep_minimal_reset_delay; + + error =3D spi_hid_acpi_populate_config(config, adev); + if (error) { + dev_err(dev, "%s: unable to populate config data.", __func__); + return error; + } + + return spi_hid_core_probe(spi, &config->ops, &config->property_conf); +} + +static const struct acpi_device_id spi_hid_acpi_match[] =3D { + { "ACPI0C51", 0 }, + { "PNP0C51", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, spi_hid_acpi_match); + +static struct spi_driver spi_hid_acpi_driver =3D { + .driver =3D { + .name =3D "spi_hid_acpi", + .owner =3D THIS_MODULE, + .acpi_match_table =3D spi_hid_acpi_match, + .probe_type =3D PROBE_PREFER_ASYNCHRONOUS, + .dev_groups =3D spi_hid_groups, + }, + .probe =3D spi_hid_acpi_probe, + .remove =3D spi_hid_core_remove, +}; + +module_spi_driver(spi_hid_acpi_driver); + +MODULE_DESCRIPTION("HID over SPI ACPI transport driver"); +MODULE_AUTHOR("Angela Czubak "); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index ef527999d6dc..698e72102c11 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -44,6 +44,7 @@ #include #include =20 +#include "spi-hid.h" #include "spi-hid-core.h" =20 #define CREATE_TRACE_POINTS @@ -111,31 +112,6 @@ struct spi_hid_output_report { u8 *content; }; =20 -/* struct spi_hid_conf - Conf provided to the core */ -struct spi_hid_conf { - u32 input_report_header_address; - u32 input_report_body_address; - u32 output_report_address; - u8 read_opcode; - u8 write_opcode; -}; - -/** - * struct spihid_ops - Ops provided to the core - * @power_up: do sequencing to power up the device - * @power_down: do sequencing to power down the device - * @assert_reset: do sequencing to assert the reset line - * @deassert_reset: do sequencing to deassert the reset line - * @sleep_minimal_reset_delay: minimal sleep delay during reset - */ -struct spihid_ops { - int (*power_up)(struct spihid_ops *ops); - int (*power_down)(struct spihid_ops *ops); - int (*assert_reset)(struct spihid_ops *ops); - int (*deassert_reset)(struct spihid_ops *ops); - void (*sleep_minimal_reset_delay)(struct spihid_ops *ops); -}; - static struct hid_ll_driver spi_hid_ll_driver; =20 static void spi_hid_populate_read_approvals(const struct spi_hid_conf *con= f, diff --git a/drivers/hid/spi-hid/spi-hid.h b/drivers/hid/spi-hid/spi-hid.h new file mode 100644 index 000000000000..f5a5f4d54beb --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Microsoft Corporation + * Copyright (c) 2026 Google LLC + */ + +#ifndef SPI_HID_H +#define SPI_HID_H + +#include +#include + +/* struct spi_hid_conf - Conf provided to the core */ +struct spi_hid_conf { + u32 input_report_header_address; + u32 input_report_body_address; + u32 output_report_address; + u8 read_opcode; + u8 write_opcode; +}; + +/** + * struct spihid_ops - Ops provided to the core + * @power_up: do sequencing to power up the device + * @power_down: do sequencing to power down the device + * @assert_reset: do sequencing to assert the reset line + * @deassert_reset: do sequencing to deassert the reset line + * @sleep_minimal_reset_delay: minimal sleep delay during reset + */ +struct spihid_ops { + int (*power_up)(struct spihid_ops *ops); + int (*power_down)(struct spihid_ops *ops); + int (*assert_reset)(struct spihid_ops *ops); + int (*deassert_reset)(struct spihid_ops *ops); + void (*sleep_minimal_reset_delay)(struct spihid_ops *ops); +}; + +int spi_hid_core_probe(struct spi_device *spi, struct spihid_ops *ops, + struct spi_hid_conf *conf); + +void spi_hid_core_remove(struct spi_device *spi); + +extern const struct attribute_group *spi_hid_groups[]; + +#endif /* SPI_HID_H */ --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6A6623C76A0 for ; Tue, 9 Jun 2026 04:41:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980097; cv=none; b=ic/ybEAQNxJjGxxVIF6duEo/cBF3q+XKIJJRheYyP+cA+HTsUPpBDDLROrh62ZB5tbSvzsziAkI3m73k4BwDlYKwyL27uocQi/Sj4NCqhTHs188MujgGzfFamho5BJG4BQLTXNHrQii9JHnXo66jFhqE4qqmJHTE+7otihMNgPY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980097; c=relaxed/simple; bh=Vb2oD13O7ywdzG/T0HlJ6MUgHlO60MLDdBzUNUTG90A=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GQwZ70Y+KzArNzwFM3zBi2hS5kioYxmu2F3ss1Cz0QdjLE1GSxPShk10nKOZqy5d//3XJNUQ4JTzSA7A90LS57GmMTvfTBKf1O/7uOhpv0eR6nx5fweeTlEpY43p7dzcDNPrAk7dnEpPRcIuMCbU5cdrPyeGMwhWeaD62GqBGxM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=QTro43Uc; arc=none smtp.client-ip=209.85.214.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="QTro43Uc" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-2c27fc587ebso7041575ad.0 for ; Mon, 08 Jun 2026 21:41:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980091; x=1781584891; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=VYFp/76knKOA1HhYtRsRbNJyK9Pqb2WgwUlI8Hnm1hg=; b=QTro43UcbvSK6LSdi8b6qgE0XMWJmDZFwcwn5ttVkNcHxVWeUQywBBZ+KScM/2nkH+ 0nGVNe+XgCKBl4UYQVi6UFs2lmYqYzu2Na7VxyJNCw3I169Uj5oiG/a7tjHcFnV8gTi4 AiQIdtkHrn7FuyojqKNzFR2pEJ/j298GQ4Ctg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980091; x=1781584891; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=VYFp/76knKOA1HhYtRsRbNJyK9Pqb2WgwUlI8Hnm1hg=; b=O2/pdWyoauul5c7wB0spYqKCSW41eFdsTSHCmJuX5Djukb1/VN8Gr4345O9430hbhK LGMroeTpEvA076XoHoKDVLkEwRsZtUUvI1WldHy0dJJD1Oesz2KQeINL2Jrr+Vjcbnid SUOmBFGIcHJpsbQScHEceAlK/cqmp3MZSqdIUT1Pc9qCA/usNylsxhc1u42jZVzVhjZG xJ/eexS7PulSVyTnQETuZAit/SN1BaNU4hnTbuHwMhhz4Qo8iKDjnzOUNfIKHaJeQBpl 5aTCuSPGYtH38Rjxuc468EHSMP4d+ubxaPUcmBcuhIGgqgSyIgnrl630mONlkTOo44Ow t9bQ== X-Forwarded-Encrypted: i=1; AFNElJ/y8QuhnCsC/xvFYPK5eav2JIJL17v7c40lU0GXa74CQQDE8BUONgAO7OLvkkM0xJKE26TqZgxX269iUkA=@vger.kernel.org X-Gm-Message-State: AOJu0YwfIdPQF5TYUpY7zbjkgZez8nsV7HPst5UOGGP3K6yJdyeV/bcz Z9CIAY6a16Q50bcWWPwxuBMorBsDzmnmwULr4LysDW5oRo4kPZCKDiNArDs/FcaeHg== X-Gm-Gg: Acq92OE3JleF5S1qk4N3mJYdBmXCaP56WoZ2n/ozhwo7yHTH5OU9wXyqzjuKZqkhVwQ i/rE8qS8hewmrpLGcU9Ld2+l111Wl+z2+BGGjIYW+EnBRWqzL3F/b7QIX5r8Wz247pWNtrPDzOP 7iwaTAyLOoxK1hzTwnaQy2jSnuJAGKCiht3VsLqNjMUEimdCchYh/qf+XI82mcZe6txoZaudkKn 1FOT4yQwr9Td5IVKOSjQfeGQJQNwzu4aVSbVuB7K/Zr067o0ZYTtKn66Cd8xopRWXblQgx6bcRI AC4EtbNptDg06mQMxfATm7cmm9YVntUiRfFFmgCzE+G3HC781h2YmYMIoZnObNGfKc1KS6aF23+ aGK9G7ibwEuAHbw0HcKG6y7LVdK3mVYev3pIVZ5QwCiWe6oFFwA+uhzhepisMBayPbYOvsgXQ7i qVK1LSVXif+mNDrJ2bHamuC3z3iorE0yF5WxAnHJf4pkqBJndzdy1K/1GhShk117vLhr4YdfhSh +WiOsGRX7xbNr+a35tic2XIl3eGEcYCXquqNDadC+zS X-Received: by 2002:a17:903:2b0f:b0:2bf:7b62:a038 with SMTP id d9443c01a7336-2c1e79f68c3mr217080175ad.9.1780980090806; Mon, 08 Jun 2026 21:41:30 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:30 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:40:59 +0000 Subject: [PATCH v4 08/11] HID: spi_hid: add device tree support for SPI over HID Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-8-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Jarrett Schultz , Dmitry Antipov X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=8743; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=89Kf9TFsAKtFmeUp0H8Qav9xbhIboD9jdgsu+BgtC70=; b=wMGSNkqujA+k//Mj5/jfvXAJPRjl9LQx5yAQclBCa1vMu4lmziGyOVFhs6Q7VQZZA/7FYEOmX +d8nFqHkJhiCbS69jcFxUfZzCQPfTAjyr/5WBZVpjg3oQAAM0C+1KgA X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= From: Jarrett Schultz Signed-off-by: Dmitry Antipov Signed-off-by: Jingyuan Liang --- drivers/hid/spi-hid/Kconfig | 15 +++ drivers/hid/spi-hid/Makefile | 1 + drivers/hid/spi-hid/spi-hid-of.c | 246 +++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 262 insertions(+) diff --git a/drivers/hid/spi-hid/Kconfig b/drivers/hid/spi-hid/Kconfig index 114b1e00da39..76a2cd587a3e 100644 --- a/drivers/hid/spi-hid/Kconfig +++ b/drivers/hid/spi-hid/Kconfig @@ -25,6 +25,21 @@ config SPI_HID_ACPI will be called spi-hid-acpi. It will also build/depend on the module spi-hid. =20 +config SPI_HID_OF + tristate "HID over SPI transport layer Open Firmware driver" + depends on OF + select SPI_HID_CORE + help + Say Y here if you use a keyboard, a touchpad, a touchscreen, or any + other HID based devices which are connected to your computer via SPI. + This driver supports Open Firmware (Device Tree)-based systems. + + If unsure, say N. + + This support is also available as a module. If so, the module + will be called spi-hid-of. It will also build/depend on the + module spi-hid. + config SPI_HID_CORE tristate endif diff --git a/drivers/hid/spi-hid/Makefile b/drivers/hid/spi-hid/Makefile index 3ca326602643..31192e71edae 100644 --- a/drivers/hid/spi-hid/Makefile +++ b/drivers/hid/spi-hid/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_SPI_HID_CORE) +=3D spi-hid.o spi-hid-objs =3D spi-hid-core.o CFLAGS_spi-hid-core.o :=3D -I$(src) obj-$(CONFIG_SPI_HID_ACPI) +=3D spi-hid-acpi.o +obj-$(CONFIG_SPI_HID_OF) +=3D spi-hid-of.o diff --git a/drivers/hid/spi-hid/spi-hid-of.c b/drivers/hid/spi-hid/spi-hid= -of.c new file mode 100644 index 000000000000..ba7d5338f5d8 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-of.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HID over SPI protocol, Open Firmware related code + * + * Copyright (c) 2021 Microsoft Corporation + * + * This code was forked out of the HID over SPI core code, which is partia= lly + * based on "HID over I2C protocol implementation: + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012 Red Hat, Inc + * + * which in turn is partially based on "USB HID support for Linux": + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2,= Inc + * Copyright (c) 2007-2008 Oliver Neukum + * Copyright (c) 2006-2010 Jiri Kosina + */ + +#include +#include +#include +#include +#include + +#include "spi-hid.h" + +struct spi_hid_timing_data { + u32 post_power_on_delay_ms; + u32 minimal_reset_delay_ms; +}; + +/* Config structure is filled with data from Device Tree */ +struct spi_hid_of_config { + struct spihid_ops ops; + + struct spi_hid_conf property_conf; + const struct spi_hid_timing_data *timing_data; + + struct gpio_desc *reset_gpio; + struct regulator *supply; + bool supply_enabled; + u16 hid_over_spi_flags; +}; + +static const struct spi_hid_timing_data timing_data =3D { + .post_power_on_delay_ms =3D 10, + .minimal_reset_delay_ms =3D 100, +}; + +static int spi_hid_of_populate_config(struct spi_hid_of_config *conf, + struct device *dev) +{ + int error; + u32 val; + + error =3D device_property_read_u32(dev, "input-report-header-address", + &val); + if (error) { + dev_err(dev, "Input report header address not provided."); + return -ENODEV; + } + conf->property_conf.input_report_header_address =3D val; + + error =3D device_property_read_u32(dev, "input-report-body-address", &val= ); + if (error) { + dev_err(dev, "Input report body address not provided."); + return -ENODEV; + } + conf->property_conf.input_report_body_address =3D val; + + error =3D device_property_read_u32(dev, "output-report-address", &val); + if (error) { + dev_err(dev, "Output report address not provided."); + return -ENODEV; + } + conf->property_conf.output_report_address =3D val; + + error =3D device_property_read_u32(dev, "read-opcode", &val); + if (error) { + dev_err(dev, "Read opcode not provided."); + return -ENODEV; + } + conf->property_conf.read_opcode =3D val; + + error =3D device_property_read_u32(dev, "write-opcode", &val); + if (error) { + dev_err(dev, "Write opcode not provided."); + return -ENODEV; + } + conf->property_conf.write_opcode =3D val; + + conf->supply =3D devm_regulator_get(dev, "vdd"); + if (IS_ERR(conf->supply)) { + if (PTR_ERR(conf->supply) !=3D -EPROBE_DEFER) + dev_err(dev, "Failed to get regulator: %ld.", + PTR_ERR(conf->supply)); + return PTR_ERR(conf->supply); + } + conf->supply_enabled =3D false; + + conf->reset_gpio =3D devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(conf->reset_gpio)) { + dev_err(dev, "%s: error getting reset GPIO.", __func__); + return PTR_ERR(conf->reset_gpio); + } + + return 0; +} + +static int spi_hid_of_power_down(struct spihid_ops *ops) +{ + struct spi_hid_of_config *conf =3D container_of(ops, + struct spi_hid_of_config, + ops); + int error; + + if (!conf->supply_enabled) + return 0; + + error =3D regulator_disable(conf->supply); + if (error =3D=3D 0) + conf->supply_enabled =3D false; + + return error; +} + +static int spi_hid_of_power_up(struct spihid_ops *ops) +{ + struct spi_hid_of_config *conf =3D container_of(ops, + struct spi_hid_of_config, + ops); + int error; + + if (conf->supply_enabled) + return 0; + + error =3D regulator_enable(conf->supply); + + if (error =3D=3D 0) { + conf->supply_enabled =3D true; + fsleep(1000 * conf->timing_data->post_power_on_delay_ms); + } + + return error; +} + +static int spi_hid_of_assert_reset(struct spihid_ops *ops) +{ + struct spi_hid_of_config *conf =3D container_of(ops, + struct spi_hid_of_config, + ops); + + gpiod_set_value(conf->reset_gpio, 1); + return 0; +} + +static int spi_hid_of_deassert_reset(struct spihid_ops *ops) +{ + struct spi_hid_of_config *conf =3D container_of(ops, + struct spi_hid_of_config, + ops); + + gpiod_set_value(conf->reset_gpio, 0); + return 0; +} + +static void spi_hid_of_sleep_minimal_reset_delay(struct spihid_ops *ops) +{ + struct spi_hid_of_config *conf =3D container_of(ops, + struct spi_hid_of_config, + ops); + fsleep(1000 * conf->timing_data->minimal_reset_delay_ms); +} + +static int spi_hid_of_probe(struct spi_device *spi) +{ + struct device *dev =3D &spi->dev; + struct spi_hid_of_config *config; + int error; + + config =3D devm_kzalloc(dev, sizeof(struct spi_hid_of_config), + GFP_KERNEL); + if (!config) + return -ENOMEM; + + config->ops.power_up =3D spi_hid_of_power_up; + config->ops.power_down =3D spi_hid_of_power_down; + config->ops.assert_reset =3D spi_hid_of_assert_reset; + config->ops.deassert_reset =3D spi_hid_of_deassert_reset; + config->ops.sleep_minimal_reset_delay =3D + spi_hid_of_sleep_minimal_reset_delay; + + config->timing_data =3D device_get_match_data(dev); + if (!config->timing_data) + config->timing_data =3D &timing_data; + + /* + * FIXME: hid_over_spi_flags could be retrieved from spi mode. + * It is always 0 because multi-SPI not supported. + */ + config->hid_over_spi_flags =3D 0; + + error =3D spi_hid_of_populate_config(config, dev); + if (error) { + dev_err(dev, "%s: unable to populate config data.", __func__); + return error; + } + + return spi_hid_core_probe(spi, &config->ops, &config->property_conf); +} + +static const struct of_device_id spi_hid_of_match[] =3D { + { .compatible =3D "hid-over-spi", .data =3D &timing_data }, + {} +}; +MODULE_DEVICE_TABLE(of, spi_hid_of_match); + +static const struct spi_device_id spi_hid_of_id_table[] =3D { + { "hid", 0 }, + { "hid-over-spi", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, spi_hid_of_id_table); + +static struct spi_driver spi_hid_of_driver =3D { + .driver =3D { + .name =3D "spi_hid_of", + .owner =3D THIS_MODULE, + .of_match_table =3D spi_hid_of_match, + .probe_type =3D PROBE_PREFER_ASYNCHRONOUS, + .dev_groups =3D spi_hid_groups, + }, + .probe =3D spi_hid_of_probe, + .remove =3D spi_hid_core_remove, + .id_table =3D spi_hid_of_id_table, +}; + +module_spi_driver(spi_hid_of_driver); + +MODULE_DESCRIPTION("HID over SPI OF transport driver"); +MODULE_AUTHOR("Dmitry Antipov "); +MODULE_LICENSE("GPL"); --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1C1833C8731 for ; Tue, 9 Jun 2026 04:41:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980098; cv=none; b=Y3zcc58OaVRmOYm3l4C35eJhhnjfr2V13xyimY4ehXAd5P4s/phnIhs7VxGN1xP7AuKDsg4NFoYUJDJeKXS4i3ePdCNeA+5ZOKEnC4N1uBdffyd2WZW+7IVcpjkq3VvgJ1xS/41BCvO5oE2lVASCSLmoggMdDRJRy1wcgS1VAVg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980098; c=relaxed/simple; bh=xvf6N522rgUwygII2E7FT/nV/spP4Oa82Sd6W1WQORk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BMwXCwq2txNF8ySo0oE44d3W9oKQyfWSt4gwuMKGV+RFqwv2WM8q4NuiB35z8TLXsqP1EkgWx/3vOE5UJbuSoTsVBoZzGUyjXGlw8M1S5VIY7MAzFDSXmEKhzuQifeYiQyxPeQ3jI0LKU/kI93RH8yYMbYknAGMlJh2P5q8fRoQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=kyJX4vS9; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="kyJX4vS9" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2bf36a6905cso35186445ad.3 for ; Mon, 08 Jun 2026 21:41:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980091; x=1781584891; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=mrAqGvulCN1vkXijFyEkWAFvyvd9/cpcDbYQDOTqkK4=; b=kyJX4vS9fBPUICTwn+z4Iy6rPgELLKzGMg8FcLqwj83n+XTrJS9ZJuD3oj8qrMMZ6/ CYbOyEo0VVYtACqW1/zsMSRnpEU2ahHdHt3ei2L2Rai+Q285CctGfmE2lqvKgZ+twc19 Zw4qIN/PCEQaUlzG2aecc9aREo3lcZgItRTdQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980091; x=1781584891; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=mrAqGvulCN1vkXijFyEkWAFvyvd9/cpcDbYQDOTqkK4=; b=rpTsPM0VxpV7EF45lUDrS5qGtHPRTa5G24ZyDR4jk/MUg3y+MKSH2xKEtfzZd0whv/ h+HB2xe4yeulNWKXmIi5LLWVUAK1VwaactXXz5JrU843uHEOVI5sqVVHwQ9GU6gcRIJx SM+URORY6BWTIOvNpwYuhdnV0zuO2gqAQP2w4qZsj2UGMuEimKmef3yw+L5NgAvtXuqx viDn6vW05sRQb/tigveEbyYXqS1Zwq8okmrClr31BnuK04nAoTU2T/muC5gIsC2gxSez pb7S/VOeOjRp6a1NL4JVIkQNoTGy6Rm1P4yayND++EH1pPhkmzxZsHc5N2fTQfEGOYve KOUg== X-Forwarded-Encrypted: i=1; AFNElJ9GDIqoLqKCvDrFB4wFCm2/OktNiFlEKJAbZbmFFXzz8/XtPHihmKQInd8/SHCpnBLVLTt0o3ABTBGNoig=@vger.kernel.org X-Gm-Message-State: AOJu0YzGDzDlA3a9g9hAAVTTVTefj+i1GDx0FbimrBsmyW0V6m1pCm0d QNZhbNudFHTFLK3lcNRNOMZtEZos4w7HLMNFfvgFmjoXXEm6yfo7TElb9OuB1rSBhQ== X-Gm-Gg: Acq92OHEr5lIa85Ad+3k9izJvPNOyZOzrsrkVUhqEPYzWscy1CHpzhgZF/MGJfkO2N2 0qZIysN5TF5BhFPr3XNCRsozqF7eGw3rvU9CCI90JV2XLbxQ64v3T1HH6OP0UmteLvnRCtJmEmU q8gpZeP9Tcssx15UyQMv8uZRLYqh9+i4w04/sLmxtu0FgJzEhcRyvLhEeoHrKSdZT61R2QzOmn0 SwQyF7oTG+gGGU56yxWVY+LVLU241DwlShcectEEXGwOe6iqZGcaTu6o5KFFi7KJolQfY/+IEZC 83r5bgKZkwFmW4NIBaCZeHEii/iB8jEpVPzyh+VMNNsMr3iUUbylpd3we7xSnNfs5uqMLqGnqaU zRNiCXVDi6kiSKcHBhOr1wr3oFA6qjsXEGay+ZLycNGHUylaI2ZRzqh5cbmnI3bIrZ0biw+t+ky 3qlL2SZg385k48eC2GdLy7Fg7XRMDEcg7/l0ukoeLjV0i8nQNHODTtFaHzQhr44QXHuTGYOcD75 DHtCrRIxQiEb31v3e0n8crknCy+IzGqWpBaezqh21aXZ8ZuAMO+I3w= X-Received: by 2002:a17:903:2ec4:b0:2c0:a711:539 with SMTP id d9443c01a7336-2c2a1bafb3emr14638365ad.5.1780980091392; Mon, 08 Jun 2026 21:41:31 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:31 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:41:00 +0000 Subject: [PATCH v4 09/11] dt-bindings: input: Document hid-over-spi DT schema Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-9-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang , Dmitry Antipov , Jarrett Schultz X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=4888; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=xvf6N522rgUwygII2E7FT/nV/spP4Oa82Sd6W1WQORk=; b=lzd6q3Zwv0Xbtt1uCAylUOu0Rv3Lnnpd4Kb7uh/sIxxS9yumLPROty1vgqdCo2GhrgoVHqDhP 45iDnwdhGOZD0ByMsBZNOQdx/b8PNj9kUpKTTvkwYXvRRVVIPlpBDLP X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= Documentation describes the required and optional properties for implementing Device Tree for a Microsoft G6 Touch Digitizer that supports HID over SPI Protocol 1.0 specification. The properties are common to HID over SPI. Signed-off-by: Dmitry Antipov Signed-off-by: Jarrett Schultz Signed-off-by: Jingyuan Liang --- .../devicetree/bindings/input/hid-over-spi.yaml | 128 +++++++++++++++++= ++++ 1 file changed, 128 insertions(+) diff --git a/Documentation/devicetree/bindings/input/hid-over-spi.yaml b/Do= cumentation/devicetree/bindings/input/hid-over-spi.yaml new file mode 100644 index 000000000000..27cf311e0aab --- /dev/null +++ b/Documentation/devicetree/bindings/input/hid-over-spi.yaml @@ -0,0 +1,128 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/hid-over-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HID over SPI Devices + +maintainers: + - Benjamin Tissoires + - Jiri Kosina + - Jingyuan Liang + +description: |+ + HID over SPI provides support for various Human Interface Devices over t= he + SPI bus. These devices can be for example touchpads, keyboards, touch sc= reens + or sensors. + + The specification has been written by Microsoft and is currently availab= le + here: https://www.microsoft.com/en-us/download/details.aspx?id=3D103325 + + The Microsoft HID over SPI specification explicitly dictates that SPI + opcodes and register addresses (such as input/output report addresses) + are not standardized. Instead, the specification requires the system + firmware (e.g., ACPI or Device Tree) to provide these board-specific + parameters to the OS. Therefore, these varying parameters must be + defined as properties in the Device Tree. + +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + items: + - enum: + - microsoft,g6-touch-digitizer + - const: hid-over-spi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: + GPIO specifier for the digitizer's reset pin (active low). The line = must + be flagged with GPIO_ACTIVE_LOW. + + vdd-supply: + description: + Regulator for the VDD supply voltage. + + input-report-header-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 0xffffff + description: + A value to be included in the Read Approval packet, listing an addre= ss of + the input report header to be put on the SPI bus. This address has 24 + bits. + + input-report-body-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 0xffffff + description: + A value to be included in the Read Approval packet, listing an addre= ss of + the input report body to be put on the SPI bus. This address has 24 = bits. + + output-report-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 0xffffff + description: + A value to be included in the Output Report sent by the host, listin= g an + address where the output report on the SPI bus is to be written to. = This + address has 24 bits. + + read-opcode: + $ref: /schemas/types.yaml#/definitions/uint8 + description: + Value to be used in Read Approval packets. 1 byte. + + write-opcode: + $ref: /schemas/types.yaml#/definitions/uint8 + description: + Value to be used in Write Approval packets. 1 byte. + +required: + - compatible + - interrupts + - reset-gpios + - vdd-supply + - input-report-header-address + - input-report-body-address + - output-report-address + - read-opcode + - write-opcode + +unevaluatedProperties: false + +examples: + - | + #include + #include + + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + hid@0 { + compatible =3D "microsoft,g6-touch-digitizer", "hid-over-spi"; + reg =3D <0x0>; + interrupts-extended =3D <&gpio 42 IRQ_TYPE_EDGE_FALLING>; + reset-gpios =3D <&gpio 27 GPIO_ACTIVE_LOW>; + vdd-supply =3D <&pm8350c_l3>; + pinctrl-names =3D "default"; + pinctrl-0 =3D <&ts_d6_int_bias>; + input-report-header-address =3D <0x1000>; + input-report-body-address =3D <0x1004>; + output-report-address =3D <0x2000>; + read-opcode =3D /bits/ 8 <0x0b>; + write-opcode =3D /bits/ 8 <0x02>; + }; + }; --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E72BF37F8C3 for ; Tue, 9 Jun 2026 04:41:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980100; cv=none; b=LMx7BwqZYTHvX3VZhHDg+Ym/2+J0WIEC63UGKR5lbEsWoGHgD1gwNOpeG05l3s9/1YEXx2ODMwxhSWSE8kTJXT04wweKyMsHS2G5sYbSotQoQZKNMnRYHJces8suz5WfCuNHkSwnZTU8VM+WCqiEXM59hPI5H2xZJuomq+bR5GY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980100; c=relaxed/simple; bh=oFuGc3tJiwCr2zCPxHqXkgQyb1lc5qOYN3lPOUvtDUA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ioaUjs5/p3ipT0ZMZpu7/MjqjkErWwlm0+3CN9ptZjIca6muwwYZs09fNkAIEEP9o0jvF/SNr+V/6pX+Mya0x8fbvCzbl2b3ql57z3FRZUDs1BTXLYzJiCrIKM9vGd//zLoluaeYUorOxGfaXi77CfpDJHuPPfSCsJRNTHk3lgU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=liHwSfe7; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="liHwSfe7" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2bf125989f2so36739705ad.3 for ; Mon, 08 Jun 2026 21:41:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980092; x=1781584892; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=3YK8R1SlsKAhKfLkzDqnTrpzRnHx/wAIPaeuSh5XPa8=; b=liHwSfe7wfvjvRBKova5RIN/XF+P4ivYD3ou7HnAdFMU3OEr+yamSiRlZWD9O6CCEw o8JRzPnKfN8SFl/CF3THegHhpNvh8S/qj6kQsr+4qybI2R7Ukj3RLMr1ZSbjuaPhXYgG 7pOpd99JGvwrAfUqHuvcJlqBB1k2qaHjyOEA0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980092; x=1781584892; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=3YK8R1SlsKAhKfLkzDqnTrpzRnHx/wAIPaeuSh5XPa8=; b=RDVUKqrYsLNXF4CE2oRZuRA9VWaTQQAA6lu49SoT3CRIz1pvC8r/svs/0vggVdEf0e JFBkbM5ecqCohQUuc86bvbrjveT2v5I8U6hnnpMjGeppkZccR2mCwDxJtg2Cmh6RFYXV svUsrNYFZ36PnxNPQ2P5BDa/iN+B0CjN3l5gHz/1Oj1PhUcmtI31JGI1aHfmzrdNTCkr Yit9deZprpyIrvvOpXpalOJDoi3NC5Gq7i8rQ0K77gwTcVNTTmiS7T+c3BC42ZEn9d7f 6pIlOsVpA+i6M+7butlT4ZEeqoynUUItGUHcfMEFu7gQthHM2YdoXgCjdfIA3sCFtmfR oydg== X-Forwarded-Encrypted: i=1; AFNElJ/L1QHIezzhZB45Dgyf5EVi9/Vcjo0RyjyI4s7qO9V0eSbgUoP/NvZqtFSCmPcGG2olUEJkxacmPP9aPbs=@vger.kernel.org X-Gm-Message-State: AOJu0YyBizX+wHBh48XWV36lQnp00kfQSruw79/8NUdbHn3WbtcnQZaC DYvsUQDYaZo9l2AjAtojmAMjhXAKeMMdjicHJ2VK9OVeARaqUz2OJGIiPz6a694h1A== X-Gm-Gg: Acq92OEvMSw6aoOO6/sMqK2sbRuUQBPHkc6HD0u7xmJhJ2J5Ff1Ri3oDaGjYafVkWdL FDw0fWz64/sUhsPQqWmDeAqa9H8VY/j+F0AsJB96AV24CUqC0x28RykQK0D5Pt/78s/o+b5h52c H8boo3Ip2FK08P9YJpJWRP8ymztPtTLTE5UgfTwfNrJiHi5EautSCr0U1P6yGVmH4WTUSMsJpzK Gu1NtSsZsFYDfFfQBVdq/heGMl70O4q5mTv/QINl73p6RVwxrOXgGUHNauevfqxrSWe04vCvjcV ADxUSZ6aP6HapHd0HrMkYKb/ksyir006pxWkspJxZkTNUnQGtmz0xiD/YPDkyUxKp1htA4XjtVc USjRsZPW61lc1f153ba5WtjZC0JZplB59kMJMmTCWfgrekfc9cD4f/uZ/9spqM7miRnZwdNyPgD fMG8+WqetO/FlyyWb2RSGjv5+CWR72d45W97MGK/S5stS2vZD0U/Cwb9aNusl15Nh6uw/nWlU/4 iCoXPV7U8fs39AJHEeWcViAoVhvDUDBhOp41E98GAvT X-Received: by 2002:a17:903:22c4:b0:2c0:e2ea:6b0c with SMTP id d9443c01a7336-2c1e7f92475mr193667815ad.21.1780980092081; Mon, 08 Jun 2026 21:41:32 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:31 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:41:01 +0000 Subject: [PATCH v4 10/11] HID: spi-hid: add power management implementation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-10-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=7125; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=oFuGc3tJiwCr2zCPxHqXkgQyb1lc5qOYN3lPOUvtDUA=; b=6KjYSLF9dyKRplumRGKjaPMEvPCklBGYcZiB4Fvn13XflzP0axAXOG9n0bIZfzy/jjHS6a143 iqmFKGwB+zQAqm7GN6YR9+aEXY7KRkWGhqV02DkkcMd139gl3XmBH5Q X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= Implement HID over SPI driver power management callbacks. Signed-off-by: Jingyuan Liang --- drivers/hid/spi-hid/spi-hid-acpi.c | 1 + drivers/hid/spi-hid/spi-hid-core.c | 133 +++++++++++++++++++++++++++++++++= ++-- drivers/hid/spi-hid/spi-hid-of.c | 1 + drivers/hid/spi-hid/spi-hid.h | 1 + 4 files changed, 131 insertions(+), 5 deletions(-) diff --git a/drivers/hid/spi-hid/spi-hid-acpi.c b/drivers/hid/spi-hid/spi-h= id-acpi.c index 298e3ba44d8a..15cfc4e6cc2f 100644 --- a/drivers/hid/spi-hid/spi-hid-acpi.c +++ b/drivers/hid/spi-hid/spi-hid-acpi.c @@ -238,6 +238,7 @@ static struct spi_driver spi_hid_acpi_driver =3D { .driver =3D { .name =3D "spi_hid_acpi", .owner =3D THIS_MODULE, + .pm =3D &spi_hid_core_pm, .acpi_match_table =3D spi_hid_acpi_match, .probe_type =3D PROBE_PREFER_ASYNCHRONOUS, .dev_groups =3D spi_hid_groups, diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 698e72102c11..517f06913477 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include @@ -245,6 +247,96 @@ static const char *spi_hid_power_mode_string(enum hids= pi_power_state power_state } } =20 +static int spi_hid_suspend(struct spi_hid *shid) +{ + int error; + struct device *dev =3D &shid->spi->dev; + + guard(mutex)(&shid->power_lock); + if (shid->power_state =3D=3D HIDSPI_OFF) + return 0; + + if (shid->hid) { + error =3D hid_driver_suspend(shid->hid, PMSG_SUSPEND); + if (error) { + dev_err(dev, "%s failed to suspend hid driver: %d\n", + __func__, error); + return error; + } + } + + disable_irq(shid->spi->irq); + + if (!device_may_wakeup(dev)) { + set_bit(SPI_HID_RESET_PENDING, &shid->flags); + + shid->ops->assert_reset(shid->ops); + + error =3D shid->ops->power_down(shid->ops); + if (error) { + dev_err(dev, "%s: could not power down\n", __func__); + shid->regulator_error_count++; + shid->regulator_last_error =3D error; + /* Undo partial suspend before returning error */ + shid->ops->deassert_reset(shid->ops); + clear_bit(SPI_HID_RESET_PENDING, &shid->flags); + enable_irq(shid->spi->irq); + if (shid->hid) + hid_driver_reset_resume(shid->hid); + return error; + } + + shid->power_state =3D HIDSPI_OFF; + } + return 0; +} + +static int spi_hid_resume(struct spi_hid *shid) +{ + int error; + struct device *dev =3D &shid->spi->dev; + + guard(mutex)(&shid->power_lock); + + if (!device_may_wakeup(dev)) { + if (shid->power_state =3D=3D HIDSPI_OFF) { + shid->ops->assert_reset(shid->ops); + + shid->ops->sleep_minimal_reset_delay(shid->ops); + + error =3D shid->ops->power_up(shid->ops); + if (error) { + dev_err(dev, "%s: could not power up\n", __func__); + shid->regulator_error_count++; + shid->regulator_last_error =3D error; + return error; + } + shid->power_state =3D HIDSPI_ON; + shid->ops->deassert_reset(shid->ops); + } + } + + enable_irq(shid->spi->irq); + + if (shid->hid) { + error =3D hid_driver_reset_resume(shid->hid); + if (error) { + dev_err(dev, "%s: failed to reset resume hid driver: %d\n", + __func__, error); + /* Undo partial resume before returning error */ + disable_irq(shid->spi->irq); + if (!device_may_wakeup(dev)) { + set_bit(SPI_HID_RESET_PENDING, &shid->flags); + shid->ops->assert_reset(shid->ops); + shid->ops->power_down(shid->ops); + shid->power_state =3D HIDSPI_OFF; + } + return error; + } + } + return 0; +} + static void spi_hid_stop_hid(struct spi_hid *shid) { struct hid_device *hid; @@ -795,6 +887,11 @@ static irqreturn_t spi_hid_dev_irq(int irq, void *_shi= d) trace_spi_hid_header_transfer(shid); =20 scoped_guard(mutex, &shid->io_lock) { + if (shid->power_state =3D=3D HIDSPI_OFF) { + dev_warn(dev, "Device is off, ignoring interrupt\n"); + goto out; + } + error =3D spi_hid_input_sync(shid, shid->input->header, sizeof(shid->input->header), true); if (error) { @@ -802,11 +899,6 @@ static irqreturn_t spi_hid_dev_irq(int irq, void *_shi= d) goto err; } =20 - if (shid->power_state =3D=3D HIDSPI_OFF) { - dev_warn(dev, "Device is off after header was received\n"); - goto out; - } - trace_spi_hid_input_header_complete(shid, shid->input_transfer[0].tx_buf, shid->input_transfer[0].len, @@ -1251,10 +1343,19 @@ int spi_hid_core_probe(struct spi_device *spi, stru= ct spihid_ops *ops, dev_err(dev, "%s: unable to request threaded IRQ\n", __func__); return error; } + if (device_may_wakeup(dev)) { + error =3D dev_pm_set_wake_irq(dev, spi->irq); + if (error) { + dev_err(dev, "%s: failed to set wake IRQ\n", __func__); + return error; + } + } =20 error =3D shid->ops->power_up(shid->ops); if (error) { dev_err(dev, "%s: could not power up\n", __func__); + if (device_may_wakeup(dev)) + dev_pm_clear_wake_irq(dev); return error; } =20 @@ -1284,9 +1385,31 @@ void spi_hid_core_remove(struct spi_device *spi) error =3D shid->ops->power_down(shid->ops); if (error) dev_err(dev, "failed to disable regulator\n"); + + if (device_may_wakeup(dev)) + dev_pm_clear_wake_irq(dev); } EXPORT_SYMBOL_GPL(spi_hid_core_remove); =20 +static int spi_hid_core_pm_suspend(struct device *dev) +{ + struct spi_hid *shid =3D dev_get_drvdata(dev); + + return spi_hid_suspend(shid); +} + +static int spi_hid_core_pm_resume(struct device *dev) +{ + struct spi_hid *shid =3D dev_get_drvdata(dev); + + return spi_hid_resume(shid); +} + +const struct dev_pm_ops spi_hid_core_pm =3D { + SYSTEM_SLEEP_PM_OPS(spi_hid_core_pm_suspend, spi_hid_core_pm_resume) +}; +EXPORT_SYMBOL_GPL(spi_hid_core_pm); + MODULE_DESCRIPTION("HID over SPI transport driver"); MODULE_AUTHOR("Dmitry Antipov "); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/spi-hid/spi-hid-of.c b/drivers/hid/spi-hid/spi-hid= -of.c index ba7d5338f5d8..561cf453e44a 100644 --- a/drivers/hid/spi-hid/spi-hid-of.c +++ b/drivers/hid/spi-hid/spi-hid-of.c @@ -230,6 +230,7 @@ static struct spi_driver spi_hid_of_driver =3D { .driver =3D { .name =3D "spi_hid_of", .owner =3D THIS_MODULE, + .pm =3D &spi_hid_core_pm, .of_match_table =3D spi_hid_of_match, .probe_type =3D PROBE_PREFER_ASYNCHRONOUS, .dev_groups =3D spi_hid_groups, diff --git a/drivers/hid/spi-hid/spi-hid.h b/drivers/hid/spi-hid/spi-hid.h index f5a5f4d54beb..17b2fdf192ed 100644 --- a/drivers/hid/spi-hid/spi-hid.h +++ b/drivers/hid/spi-hid/spi-hid.h @@ -41,5 +41,6 @@ int spi_hid_core_probe(struct spi_device *spi, struct spi= hid_ops *ops, void spi_hid_core_remove(struct spi_device *spi); =20 extern const struct attribute_group *spi_hid_groups[]; +extern const struct dev_pm_ops spi_hid_core_pm; =20 #endif /* SPI_HID_H */ --=20 2.54.0.1064.gd145956f57-goog From nobody Wed Jun 10 16:02:09 2026 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A2F273CA4A9 for ; Tue, 9 Jun 2026 04:41:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980099; cv=none; b=N3jGnGN9s/GWq1hhKVJnTIEqdGhHqJgwvxT3m5px9pGX4QrGST/G/DxHxZiGIrstJoa1dF2Ye2bBw+amI6vZApdksyf+AxP8uo3o/dvxMCACKYWZOj1hMIeqHQPhKMGsrWYCFb4vV/tlxbIbtzywJxXIZjLEWsgesgfb/3ePOdk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780980099; c=relaxed/simple; bh=q5+kv26/nrZW6zX2FC2B8QfgxpMMxjfWrvpFimff1Mk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ugk6WjLKP5F+KlHChMcAIPVdcMTwgQGsy6bBOwblvo9AjliuMFqvfmpD+YxKNw9s+WnLC4lqI7U9xAEyvdeS/5IqwPqTRbSFpQDl+fAoWr0Em5o6H3OP2Sa19MXVzPZ3QKx/vBturE7F9ZBvxRgV8dZ3S8ZAl4/qXTsG5CYFLc0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=PKrfU4Ey; arc=none smtp.client-ip=209.85.214.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="PKrfU4Ey" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2c0c3546924so33150295ad.3 for ; Mon, 08 Jun 2026 21:41:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1780980093; x=1781584893; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=YqxZiyw+e+cflpM3UG3hv/pwSFqmoCoCJh93jY7BBN0=; b=PKrfU4EybXKq036yiOpy1cmPNVTmSBTPM1Q7MXm9CCWLDO+lZJXVcmnqW+ISL97J0h sui2sh4qjXw3mA5HfnGz+Z0Iv35NdLWtVCEWtr2I6ZQ84r+GnNaB2HcRQ6VLcjamJUOA FYp1RAymqlA1gTz+4NTy2ZQJL6QrogmC+rtDQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780980093; x=1781584893; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=YqxZiyw+e+cflpM3UG3hv/pwSFqmoCoCJh93jY7BBN0=; b=KiOQqTZuyaK80b8vtz5zHh4mLDSW0L+A4yDKfN7sOSmzM+WXw6g8nu8XM0ROK1VsJT t2JwZhbHr99xC6VXj15phDpPKdVsIPF9OIdxz/o+IhHa+7fYwBZeh9bN3xAbHYwroo8y Yz2q5TOniCj/pUobxY3eEuUUMftKIC1Ve2LBpgT+tOBm9Dbsc4CrTFnt7L6BSFa49g3/ 6QZCIMzUux7YWGtstbS/UXOhMav6EHVZL8ksVXNoZGAPLtasrToABhhwmKzWWidkZYvW LabYIQyDAs46DghG/xI+xl0a0vo5y9zTqbFWXgvl0DCw7eioGvEmqwjUsLsf7XS13nWk 7OwQ== X-Forwarded-Encrypted: i=1; AFNElJ982iQ2fXuiVBvw4vNOYpT7PPC8/k2DHyTsE+5m9xWhp1DnB3Royv855DjkhXBPbMeNkBwozXHBjv7+RfY=@vger.kernel.org X-Gm-Message-State: AOJu0Yw0XBb785DZXQgPD8ZYL/cyIiyiCZLZX++WLh1nE2fosW9BK8ia wPU8UJSlq3l8BTOFVGoNldsrKis1Gedq5REzq2+ad9Iq/+3Ih00VvZovyAmPmcl4nw== X-Gm-Gg: Acq92OHAsc25tuEdOwCz0NolHBJPiKG9lVP3WTle9rqvckR6UQYgGZcfj3CCuBTrQtK sTJAq9bPmMCtVge8YgCEFxBFUlpmANNNUjNCSwLHrCZnI0knw9FJh5ne54Q68kv0t8vEMJ3FLfy tJBmigBJ4z8nmIeu2ABEgZIhHoA1blqM3gVvxPqOJ9cRz2G0JQhgKwhnd6cgP+595lKmN29o9gA ToyVJUF+YC6/550epjbgA7nG52GeTpkT3oJsjpAzu/kkrc+M0hsfVDfoFWCXYMXvXTZQE8yCh+O gnQnJ5lDbZl1OTWM3zaeiL0zlTIQhOTq3QGUBSiED7IBz2wYr9u2FIz06hOTjRwOM6Bae7eb/0+ N0fQ3DiwrBvwsX//rQX6CtyhpUCqhpY8+84ARo69Ip8p36hKJbDdK0Mwi3cqsxAGOr48CpitXjw m5xmsUe6xkZmHb0OUOQLeKNyey1xfWvo6trFuoFcZ7Vo/Crzs1O29EbZpJh5LfTCYNv+vfLOt5S n9gHylWv3s59XoVqzUvxHQ/RED/z7RaCbWhzHhx2mVw X-Received: by 2002:a17:902:ce8f:b0:2bf:1e59:d99 with SMTP id d9443c01a7336-2c1e80f9ea3mr211630235ad.8.1780980092709; Mon, 08 Jun 2026 21:41:32 -0700 (PDT) Received: from jingyliang-input-linux.c.googlers.com (111.169.168.34.bc.googleusercontent.com. [34.168.169.111]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c1664a67b0sm200736065ad.80.2026.06.08.21.41.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:41:32 -0700 (PDT) From: Jingyuan Liang Date: Tue, 09 Jun 2026 04:41:02 +0000 Subject: [PATCH v4 11/11] HID: spi-hid: add panel follower support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260609-send-upstream-v4-11-b843d5e6ced3@chromium.org> References: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> In-Reply-To: <20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org> To: Jiri Kosina , Benjamin Tissoires , Jonathan Corbet , Mark Brown , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, linux-trace-kernel@vger.kernel.org, devicetree@vger.kernel.org, hbarnor@chromium.org, tfiga@chromium.org, Jingyuan Liang X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780980084; l=8112; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=q5+kv26/nrZW6zX2FC2B8QfgxpMMxjfWrvpFimff1Mk=; b=glvstjxXTBww/N19Xbya/aWrvvQvSaP5TaaNakkGljtc3CQiYDCF/jG8C+3to9vC/UUJC+Lw1 L1aAmFYVyZHAVg5Rm3YlkNvXaA8FPyjOfXvG8V9rGwrxZwz789SFwD1 X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= Add support to spi-hid to be a panel follower. Signed-off-by: Jingyuan Liang --- drivers/hid/spi-hid/spi-hid-core.c | 164 ++++++++++++++++++++++++++++++---= ---- drivers/hid/spi-hid/spi-hid-core.h | 7 ++ 2 files changed, 142 insertions(+), 29 deletions(-) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 517f06913477..27f25d95ed28 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -1281,6 +1281,106 @@ const struct attribute_group *spi_hid_groups[] =3D { }; EXPORT_SYMBOL_GPL(spi_hid_groups); =20 +/* + * At the end of probe we initialize the device: + * 0) assert reset, bias the interrupt line + * 1) sleep minimal reset delay + * 2) power up the device + * 3) deassert reset (high) + * After this we expect an IRQ with a reset response. + */ +static int spi_hid_dev_init(struct spi_hid *shid) +{ + struct spi_device *spi =3D shid->spi; + struct device *dev =3D &spi->dev; + int error; + + shid->ops->assert_reset(shid->ops); + + shid->ops->sleep_minimal_reset_delay(shid->ops); + + error =3D shid->ops->power_up(shid->ops); + if (error) { + dev_err(dev, "%s: could not power up\n", __func__); + shid->regulator_error_count++; + shid->regulator_last_error =3D error; + return error; + } + + shid->ops->deassert_reset(shid->ops); + + enable_irq(spi->irq); + + return 0; +} + +static void spi_hid_panel_follower_work(struct work_struct *work) +{ + struct spi_hid *shid =3D container_of(work, struct spi_hid, + panel_follower_work); + int error; + + if (!shid->desc.hid_version) + error =3D spi_hid_dev_init(shid); + else + error =3D spi_hid_resume(shid); + if (error) + dev_warn(&shid->spi->dev, "Power on failed: %d\n", error); + else + WRITE_ONCE(shid->panel_follower_work_finished, true); +} + +static int spi_hid_panel_follower_resume(struct drm_panel_follower *follow= er) +{ + struct spi_hid *shid =3D container_of(follower, struct spi_hid, panel_fol= lower); + + /* + * Powering on a touchscreen can be a slow process. Queue the work to + * the system workqueue so we don't block the panel's power up. + */ + WRITE_ONCE(shid->panel_follower_work_finished, false); + schedule_work(&shid->panel_follower_work); + + return 0; +} + +static int spi_hid_panel_follower_suspend(struct drm_panel_follower *follo= wer) +{ + struct spi_hid *shid =3D container_of(follower, struct spi_hid, panel_fol= lower); + + cancel_work_sync(&shid->panel_follower_work); + + if (!READ_ONCE(shid->panel_follower_work_finished)) + return 0; + + return spi_hid_suspend(shid); +} + +static const struct drm_panel_follower_funcs + spi_hid_panel_follower_prepare_funcs =3D { + .panel_prepared =3D spi_hid_panel_follower_resume, + .panel_unpreparing =3D spi_hid_panel_follower_suspend, +}; + +static int spi_hid_register_panel_follower(struct spi_hid *shid) +{ + struct device *dev =3D &shid->spi->dev; + + shid->panel_follower.funcs =3D &spi_hid_panel_follower_prepare_funcs; + + /* + * If we're not in control of our own power up/power down then we can't + * do the logic to manage wakeups. Give a warning if a user thought + * that was possible then force the capability off. + */ + if (device_can_wakeup(dev)) { + dev_warn(dev, "Can't wakeup if following panel\n"); + device_set_wakeup_capable(dev, false); + } + + return drm_panel_add_follower(dev, &shid->panel_follower); +} + int spi_hid_core_probe(struct spi_device *spi, struct spihid_ops *ops, struct spi_hid_conf *conf) { @@ -1300,6 +1400,7 @@ int spi_hid_core_probe(struct spi_device *spi, struct= spihid_ops *ops, shid->ops =3D ops; shid->conf =3D conf; set_bit(SPI_HID_RESET_PENDING, &shid->flags); + shid->is_panel_follower =3D drm_is_panel_follower(&spi->dev); =20 spi_set_drvdata(spi, shid); =20 @@ -1313,6 +1414,7 @@ int spi_hid_core_probe(struct spi_device *spi, struct= spihid_ops *ops, init_completion(&shid->output_done); =20 INIT_WORK(&shid->reset_work, spi_hid_reset_work); + INIT_WORK(&shid->panel_follower_work, spi_hid_panel_follower_work); =20 /* * We need to allocate the buffer without knowing the maximum @@ -1323,20 +1425,6 @@ int spi_hid_core_probe(struct spi_device *spi, struc= t spihid_ops *ops, if (error) return error; =20 - /* - * At the end of probe we initialize the device: - * 0) assert reset, bias the interrupt line - * 1) sleep minimal reset delay - * 2) request IRQ - * 3) power up the device - * 4) deassert reset (high) - * After this we expect an IRQ with a reset response. - */ - - shid->ops->assert_reset(shid->ops); - - shid->ops->sleep_minimal_reset_delay(shid->ops); - error =3D devm_request_threaded_irq(dev, spi->irq, NULL, spi_hid_dev_irq, IRQF_ONESHOT | IRQF_NO_AUTOEN, dev_name(&spi->dev), shid); if (error) { @@ -1351,22 +1439,28 @@ int spi_hid_core_probe(struct spi_device *spi, stru= ct spihid_ops *ops, } } =20 - error =3D shid->ops->power_up(shid->ops); - if (error) { - dev_err(dev, "%s: could not power up\n", __func__); - if (device_may_wakeup(dev)) - dev_pm_clear_wake_irq(dev); - return error; + if (shid->is_panel_follower) { + error =3D spi_hid_register_panel_follower(shid); + if (error) { + dev_err_probe(dev, error, + "Failed to register panel follower"); + goto err_wake_irq; + } + } else { + error =3D spi_hid_dev_init(shid); + if (error) + goto err_wake_irq; } =20 - shid->ops->deassert_reset(shid->ops); - - enable_irq(spi->irq); - dev_dbg(dev, "%s: d3 -> %s\n", __func__, spi_hid_power_mode_string(shid->power_state)); =20 return 0; + +err_wake_irq: + if (device_may_wakeup(dev)) + dev_pm_clear_wake_irq(dev); + return error; } EXPORT_SYMBOL_GPL(spi_hid_core_probe); =20 @@ -1376,15 +1470,21 @@ void spi_hid_core_remove(struct spi_device *spi) struct device *dev =3D &spi->dev; int error; =20 - disable_irq(spi->irq); + if (shid->is_panel_follower) + drm_panel_remove_follower(&shid->panel_follower); + else + disable_irq(spi->irq); + cancel_work_sync(&shid->reset_work); =20 spi_hid_stop_hid(shid); =20 - shid->ops->assert_reset(shid->ops); - error =3D shid->ops->power_down(shid->ops); - if (error) - dev_err(dev, "failed to disable regulator\n"); + if (shid->power_state !=3D HIDSPI_OFF) { + shid->ops->assert_reset(shid->ops); + error =3D shid->ops->power_down(shid->ops); + if (error) + dev_err(dev, "failed to disable regulator\n"); + } =20 if (device_may_wakeup(dev)) dev_pm_clear_wake_irq(dev); @@ -1395,6 +1495,9 @@ static int spi_hid_core_pm_suspend(struct device *dev) { struct spi_hid *shid =3D dev_get_drvdata(dev); =20 + if (shid->is_panel_follower) + return 0; + return spi_hid_suspend(shid); } =20 @@ -1402,6 +1505,9 @@ static int spi_hid_core_pm_resume(struct device *dev) { struct spi_hid *shid =3D dev_get_drvdata(dev); =20 + if (shid->is_panel_follower) + return 0; + return spi_hid_resume(shid); } =20 diff --git a/drivers/hid/spi-hid/spi-hid-core.h b/drivers/hid/spi-hid/spi-h= id-core.h index 293e2cfcfbf7..261b2fd7f332 100644 --- a/drivers/hid/spi-hid/spi-hid-core.h +++ b/drivers/hid/spi-hid/spi-hid-core.h @@ -10,6 +10,8 @@ #include #include =20 +#include + /* Protocol message size constants */ #define SPI_HID_READ_APPROVAL_LEN 5 #define SPI_HID_OUTPUT_HEADER_LEN 8 @@ -56,6 +58,10 @@ struct spi_hid { struct spi_hid_input_buf *input; /* Input buffer. */ struct spi_hid_input_buf *response; /* Response buffer. */ =20 + struct drm_panel_follower panel_follower; + bool is_panel_follower; + bool panel_follower_work_finished; + u16 response_length; u16 bufsize; =20 @@ -66,6 +72,7 @@ struct spi_hid { unsigned long flags; /* device flags. */ =20 struct work_struct reset_work; + struct work_struct panel_follower_work; =20 /* Control lock to ensure complete output transaction. */ struct mutex output_lock; --=20 2.54.0.1064.gd145956f57-goog