From nobody Thu Apr 9 15:05:53 2026 Received: from mail-pg1-f178.google.com (mail-pg1-f178.google.com [209.85.215.178]) (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 C9BC4372EF0 for ; Tue, 3 Mar 2026 06:13:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518428; cv=none; b=mVoKX1S0sOANuMcaZnz/lOB4I3Q/HiwXvYErGyL556TnfTnqwFG/iTfOxwvQMG/GchGD02ULZodPmEkqGSWOVuaFGDFxRqk0ShVn6HV8iqD9/HWanzREH5IES53KzQ9+2aTZEdrbFNVYf0kEUHrfrT/7PbDmnXfxxBQUGNNBi2c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518428; c=relaxed/simple; bh=G66u8aauHLh3hv8Ltv8yWkfXW+pytZrpTmIScPLSZyE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Znihp1VNkq03a0j+W/cxHlZY8TCK8ww9r510Y8ryfU9nZyrp3VNoELM8q4fC23QcedCZhnmZTnutarbaKF5k2/K6HZfRrILl0SsHNJVVFT37ApoEyva8dqmSFTPXNu+VeFsCT+EUdToPeswwFKEk9VlenrWL8eRcE23dT29xoZc= 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=YB5gyq+9; arc=none smtp.client-ip=209.85.215.178 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="YB5gyq+9" Received: by mail-pg1-f178.google.com with SMTP id 41be03b00d2f7-c6e3e4e7388so2086197a12.1 for ; Mon, 02 Mar 2026 22:13:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518426; x=1773123226; 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=TBLRd0VVPT6JgeA/gmRFgTBh/C6/jiLo21KLSX9AmXs=; b=YB5gyq+9Jx/lm59QV8g/z0+2iozkEQnGdc/kAIsB9JpJTq7as0CQs9EhfBvcGCpD+f kTsyDpnRDmBrpTZ2uNx4PYMfFz4jLPCFTlgIe22H0kFOhIaoMzn8fS7Yht3rXY+Km7Ow uI8hKu6C18NFhyzz69Q88leyNI4L9GPnKdSUo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518426; x=1773123226; 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=TBLRd0VVPT6JgeA/gmRFgTBh/C6/jiLo21KLSX9AmXs=; b=eDRdHZmwGY8rrJ6+xFi95k1Q+t/w0IY4CXXwUZ192R7EI9W6WbCy99PrBwXzPXagKu pCyJdBDx4XTsO5X/7Oei2+wN7z05NKKZwFR3kxF+sTQtUWqjfaDn68oAFzhAF/+Xwrnt z0gfR8lDAEm8YSDTZv9JDT1M1ukAEDKRV3wRoQM4iT2Vyw4lq3u05MKSQaP2WXDDBlt/ P9IogwZQrv9Uk2k0ExVBOBHm8DxAmQdKxq73NMdpIQ73l1AfO3OpBob8Ev7xFUYpD7DH 3D5OhXnXWza4xCGUa8hxYD7fSo9g+EFQLGpUkmhwpmv52FR8ySPv2bwNsU7hhuKVeMZi pKiw== X-Forwarded-Encrypted: i=1; AJvYcCX3k/JJ4Hd2S8NBTbmSETeG8//NmXmmALbFdbOsXqGXPOG6i9RpVeODFeOEqAiYRZ1I8/lLJ5doM3aLT1w=@vger.kernel.org X-Gm-Message-State: AOJu0YyThHLTLQJKpvjjaJaOMK2SWlvlskk6A7gn8iwXMuHlSxvk0wek Tj5uGQNkuiEBwerK6wbd8Js2k/MGzmfEF3+M6T1JOrIYxrnrPbqyFy99mHsu4+SGbw== X-Gm-Gg: ATEYQzz66yMryu5SW+Z+Kx8ntnsvcohHFtBoi8R6IjwSiPOHvHnYzZpZyXVnCWn87Gz pEzdpQmrH63XrWGBPkogHIsbw1aEY0J2jdGzSr8vDGno/GQDOvBfvdiGm9BfBY10omZ77Iw17dL W9PebGH0kAOK73GrMU6A4Rk6605i0DHR9XqRM6pGr35bPPXw7pPMbY41xG8e4y4Cp94FQkfU9l/ hDPocAmhU/Si9K1+J5aMgVLVyv5p3pJNaSnfKnEJapSiJPSzDT+Sz+SfURERXaedhMiopplBtWS M2f0U9611iXoS4Z74ZvgLlV1o2KDLZlE0RMKMOliQvhdGrWaSIdDWPCBieSqrwN+TfL4x2dslA3 dHgDFKN9DSEf+HacucOjb7EAepmHTfQfXgoN/AxjsQfU8hr04Qs3NJ5EkDJOO8mnjVbgfxQitOV E79uNX5yeTOH38y2aa6WwfW0nRi6xFf7lPPSYVMdFv9nUIrauWd1mFy/ZAM1UiBxUvIGTXp4VZa ki8Y/nEmu9obMBNPG7DrwgDCXQBr13V+w== X-Received: by 2002:a17:902:f70f:b0:2ae:506e:4711 with SMTP id d9443c01a7336-2ae506e4abcmr61194795ad.31.1772518426248; Mon, 02 Mar 2026 22:13:46 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:45 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:12:53 +0000 Subject: [PATCH 01/12] 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: <20260303-send-upstream-v1-1-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Jarrett Schultz , Dmitry Antipov X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=1154; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=Ko4HAge31pJ3y5sK3I4VHU62kcrFbomkgScSw+0vFnc=; b=i1wJDOCOTKohS8sUVAT9KTdbS7usX9DiYzDm8pdpDiH1LnyATV9bgGB6dBKN6tIy1Yav+kyUU 1E/czgHhZrsC9IXUbBdK9LOimEy3qzdp6R0G3SqQS+y4oZSRJt3mGxQ 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 Signed-off-by: Jingyuan Liang Reviewed-by: Dmitry Torokhov --- 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.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 6C1D5374E78 for ; Tue, 3 Mar 2026 06:13:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518429; cv=none; b=GK+pRiQ2/Ui+Fm2pllTXfLI5AFTwgj6If8i8URPrq5p4etVJ+4mYimI0hIBYgqUU2dwnn9+vqpvLLzIEE8kLTx/UhrwyFCrslha3S2J7jXCBJwJjNe20AZDQdXnBvd6oXxjoFEUWyuNaPMZ3uvj7yPLdVIZ1jSzTCLyjMts/o0o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518429; c=relaxed/simple; bh=iGiE2E9+VdW+HxA91wNyKiTp5jCXwQwdWtX3DSw03NM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MurmMktnGube0WOZm5s7m55JoxYokhPa7MZvpoB7Lxh3110lDeBdVtPZTVWGE+yykGH2omG2LDyNgiENbR6i0fRmKvBv7Ml2O6zhrDQXiBra5DPlSwkx3+rYNBUA9C9JRvpoiPhBt3g3TYeH5T4gl8EF4RJcdbGZyFZUrBxL6mo= 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=dsYidBuD; arc=none smtp.client-ip=209.85.214.170 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="dsYidBuD" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2addb31945aso39080315ad.1 for ; Mon, 02 Mar 2026 22:13:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518427; x=1773123227; 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=1DNtcofGYhMKbL+3R8ic92tUvVbSv56CGHIW8l4ecF0=; b=dsYidBuDL35A9dJFhrsIPHTd41438Pm1f/fmZX7tw5GmXEw2mwf5u63XChsm3b3hOm GeIFworVxJ0BvySjDz6uZu+a556U5TRY1pHOORyWH2AkUfI5abyAu8CGBYsjdwW31RuS bOrHcV7nfG5P7CFWeyTpcjt7PqJbnFVbFoA20= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518427; x=1773123227; 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=1DNtcofGYhMKbL+3R8ic92tUvVbSv56CGHIW8l4ecF0=; b=Shl2lJZKHZxwzJs2c7TF/rkhQrznxa5n14+ESEr7dVHEETY36iAM5ojySfc64miwEE tj2zRL2rx0RymcaAnp/z5gqbHAMqKAjMZbfXx4Aoj+e/3m48yPkUw9rAsJLaI2/gawJT QRYvLj1eMcEUdBCX8zFYgrcP46k3+zQZiTMAyRSAF1JJQH/jMO+M90WxqHZASPWbm0Lj kmewBVDWaQWJev8/uPaNNUnAQNCcx9qUWVIOsV3yBCzKdXwKQyYVdnK0SY47uJ+kAjPX 7PCkzg/7qIGCaKnPnRJvQmSvSyGSECaETfzhOoe9s4kfRJ78NPchyFnZhAOy4N+iM08G vdzw== X-Forwarded-Encrypted: i=1; AJvYcCXy8e744eF1A0pPgrJ/eK9N0h4o+NDDunUwFdBEvhcuyqe0suoWN39SlADgS3VKFOEe/62ea0kkfWP9nZ8=@vger.kernel.org X-Gm-Message-State: AOJu0Ywd+HHRUMmTMa0I8xr7XBc1PyGuKnUx5IR5YtxvnH7CSoTzDD+K HGA9yInGXSjdQuTK7x8K9e2YqTz9zQSHxhcRKOdz3TYfs4zu3qGtogjrSU8/0mXBDw== X-Gm-Gg: ATEYQzy2IQ8OI9katdiP/koESGtkfTCuHc4miPqCk8hjO/P01o3zzvTmV5O/2bjtU3A G7d+BISPwfqXZJ5vQSLu7osbgxmy8aoIMoGpL8roldco9zMMNhEawldA4c8lJBCuGLByz6fQ7Af gc2ygd6zJ1rBeEjb8ITDekZubvNZwBT/qq8SyKU9Jlzub6UB+ztvLon5hB4f0GWffM2y3oK82W4 yY0LQR7uzJHq7K3Ujm/D7iA0oYDz5mPLgERnsb0Rr8gTwEASwauCOeiTdTMYaXUk//YIS+p+Ml3 VRTvQDY00BQ9ZB4xtu++cKCQO+fvPoMYk4BQiy5iR4dEKbM4iV8oTtOAf1SuKWQNMemTCD81WFB wteakQ1xv2yaVvMHNc/xjmAstZA+fiT6zAgJsZCAyNP9twzfmuGNcS+c5MK88CtAhCOp+MYU2Zq ZH6N38XZu6qD576hcX/GwQj9l2hxhAZt2QLCXg6PL4FiE7Qm6AaRp9VZMsV/O4Eq2NVGGUVBsi9 vsnlEb+Rck8i+qNTVw6GSL4QajpWHkkrQ== X-Received: by 2002:a17:903:2350:b0:2ae:3f72:fdc5 with SMTP id d9443c01a7336-2ae3f730132mr113772815ad.26.1772518426894; Mon, 02 Mar 2026 22:13:46 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:46 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:12:54 +0000 Subject: [PATCH 02/12] 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: <20260303-send-upstream-v1-2-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Jarrett Schultz , Dmitry Antipov X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=1343; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=cf+ZImr6i3mdjVcBn+wz/4JaSL42jhFPKySUJttD6b0=; b=YJAK3yDkHhhBB1rQeKmmk/wkmhQZiNnZ7tZkIkyoA0Fh20kDjb/Qet/ksj96xn4+enVKORB4U Mb/njusCz/xAUKz4lZQ/rh4zomjFu8ThVaKXJWgeW4oc1T5DedFx1a6 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 Signed-off-by: Jingyuan Liang Reviewed-by: Dmitry Torokhov --- 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.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 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 39B5F377026 for ; Tue, 3 Mar 2026 06:13:48 +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=1772518431; cv=none; b=sVhCt4fjUfu3cYEXTVFD2YrPU2yGM9JeCkPdnStGOyQVRhe0d+cq6xITTESZq//vzo9zcPdynz5wdNVwswCifpA+jRl/5Uln+c2d8FJTrDb8yon19wTyMzFOHS5nCT+kh6IimBbd9SW2m/fQK60iLaMto0Mz5s7jp9cl4a7TKpU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518431; c=relaxed/simple; bh=ukAwqv80HxjHW+IK9yJgq5/wKBS7G8D/LoqmQK1EFpk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LkTpbPvWbJzsEDizV0cJHHhdXx5ZBTnLqkfiTHEcB8yubX9bFvWrIwlUeJ38tel9tZ3sdYvSgEbbcWfMi9uW7w7iiQ+Fi/H3p45RKO2KORKMCMlTJNbjm4FVZu5olPWXUrxsF+oZ7ZBO1BaQGNWX8yZ94sKc80cIG1kbiciPzIY= 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=iuzj3Z8v; 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="iuzj3Z8v" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2ad21f437eeso38760635ad.0 for ; Mon, 02 Mar 2026 22:13:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518428; x=1773123228; 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=15sCyctEjRQFVPkb/OR84xqS1QIlAJCOtqS1nQXjKcg=; b=iuzj3Z8vt17PxhgK2fbqsrEiHfL8XPnAEY2CKWF9fza1M5VwY28fA6SU+4N3Hh/s3Y oDknhDqJKQQSejsdmBhWs3kAjt5mvankM5MKJyLaL386eeraUrptrhr5kpX6yk1v6gnE uhMt+eEAi7dpGcaBc3QCnGNUfgpaczh/JVoR4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518428; x=1773123228; 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=15sCyctEjRQFVPkb/OR84xqS1QIlAJCOtqS1nQXjKcg=; b=coe+k4efawIRk36Oa4Dv1iMyc2iE6wwcu4WO8fyao+H5kCRsGMt/oMYGPSHunte0vP iW+edMKAcekOAIzqS7X4VsoZ70vRPfEx8x9zRHTOY70iUvLkpPEoAu1uUziUFwfXtCbS 14n8uKNjA8nUqhznOsm2SixCijTEpBzUoyZRoQpqEz/oG5FcOB31XB28FFA00fr5xDu5 SiDxDgooE3aBXlq6ZlYoZ7hV1H7shXCehaBzSoqE/M1hRsOZ+naVECYWOHblxx3orCil HcQtsyjHOmzl3B6M7crzEt41dO4JDX8GyZMi9ERSkVctdYBWOEBJJhdfv+OIjALSqwFn qcqw== X-Forwarded-Encrypted: i=1; AJvYcCWBlBEhQRblBaJeR7iIC2gvz5a00zDlIWKDFKgTeD/ru41Nzg9efFr9d3dZFfeBRftRRfuYcRoehsdyyc0=@vger.kernel.org X-Gm-Message-State: AOJu0YwjjZJ1x4fUll81uucghAXtBWuMVDvibOln4cb4t/eEwdhTGxdW r0g77IgsioflDO5lKESXelgx1W7hWzWNNub3f50+kcuyOyEIzPR6NwjHmRzcQckTjQ== X-Gm-Gg: ATEYQzwRJsAbDdEubrprtY3zm2+O6hp8B7ayi3U33tAD6Lwc9glHsTPHvwrrpGvmNR8 eZ2D++9OkbBXeGajSxiTM2Mh7PnREEZuQXVuyE6+tOyEVBLLUXBQeyoGyG5oeZXfI1kW/s1R6hp wFf04D58dJoIRPruTsGErl8AYdiPjyMIkv+jwXzjALCD2ejSnS6THhe0G9l3YKo9Ws/BS9B7+U8 u9qREuqqkqLSJZ/K32ieLxm3VdT8E3KknwpAdOriWv5PTxb7KXw4T+CmSBq6AtMvxRuDNoUYvU+ xnY8HUpoaae9OAECYo7dE44UZX74Gqw3/W+GWrNWnk54bC4nvJfIsuzmEfslzNikyyyyT4AIGXG eAbsblthWNga3GsDHD97Btobp6i+hCRnwHkIhKQ31ePQ+dGHeUWvoVb3a7voZcyCf0HJdXC0KwZ Gjs1oYZno7pkpHqdbV66/F4lFEC6+ruauFHrcRO51sa+LHtu6/GxnkKoxKqQuBLirO34GYVEIdZ 1AYxzjb3zPp1ABEn5TFbsicHWvGI8hgsg== X-Received: by 2002:a17:903:2c05:b0:2aa:e47d:e3b with SMTP id d9443c01a7336-2ae60b26667mr12872645ad.0.1772518427680; Mon, 02 Mar 2026 22:13:47 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:47 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:12:55 +0000 Subject: [PATCH 03/12] 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: <20260303-send-upstream-v1-3-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Angela Czubak , Dmitry Antipov X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=8445; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=hQmbG/SosSqChDJrbCFC+M2uTJ1VtkZ9O9Ov/oM9YU8=; b=njmLY56TaIFOGK/q2eYvB9wP7qYKVYE7KifLU9JLXlW3x7RybE/mEcCmwgZgeTrkr5x+vIsnT JwxHlYOrQhLBHI4v2F0Bg0Kho6t2KCpaeP+ZVNRxIrsOeEgHs+csMKA 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 | 212 +++++++++++++++++++++++++++++++++= ++++ 5 files changed, 240 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..5431c60efd50 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -0,0 +1,212 @@ +// 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 + */ +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, "%d (%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, "%d (%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, "%d\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.", __func__); + return error; + } + + error =3D shid->ops->power_up(shid->ops); + if (error) { + dev_err(dev, "%s: could not power up.", __func__); + return error; + } + + shid->ops->deassert_reset(shid->ops); + + dev_dbg(dev, "%s: d3 -> %s.", __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."); +} +EXPORT_SYMBOL_GPL(spi_hid_core_remove); + +MODULE_DESCRIPTION("HID over SPI transport driver"); +MODULE_AUTHOR("Dmitry Antipov "); +MODULE_LICENSE("GPL"); --=20 2.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 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 60977374E5E for ; Tue, 3 Mar 2026 06:13:49 +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=1772518432; cv=none; b=GfGutXaIkjt1+qHuIaNyjSRhO4WQhd8EiwP2ing+0GkmTbZ7I4MQ0eoZFrNv4rn5CohUXeftoCzhksZJjSAAUHc6fq+UCwy1a0MS80wY9IMr1A4if4hHeBjGY8JQYwGS4XO3srTQTcLbVBujEwIdqO4OYHLt0dAaqYCnczt5QBg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518432; c=relaxed/simple; bh=EaYWZSt8MiuzGzBvQ5vDyQnzb4gbLuNBklZ166MO5cY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GxDhPMuf/Ryk4AW8BIi5+SkQWcl+ZUc6yb9S1qfN6fEoJpAx6UwfEm2wY8yvhEXa2oGb88ZOHmJVFAR9ONNSMdsdULCHyZzZtLvmW/me/NAxP3tMJoV/QVYeyQZLgD/VHTYIE0JEC4SarFuFCIq7cEurfmcNOhaDOnApZgQrj5M= 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=B//wgJBi; 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="B//wgJBi" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2ae527552acso6084015ad.0 for ; Mon, 02 Mar 2026 22:13:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518429; x=1773123229; 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=i4Gx+YDrbmwA1yPBdOYjm17ZUGmeBRgxTfM/2qkRtnA=; b=B//wgJBiy/4kEHXCYW7B+aXciDD9z14jv2Kf6dbUhm9bLcixFZAYSLgCYarr5qPr3P wMiyb8hQCPiSeCj8lzl4w3dnVkTTlIMuWYsAewUdSvmnNeG7m9OHAPPwpbp6HtqIK7/m PPr7yUmum/W3t1IE9rOHdFKXe44RIoDBIKm/0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518429; x=1773123229; 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=i4Gx+YDrbmwA1yPBdOYjm17ZUGmeBRgxTfM/2qkRtnA=; b=ZJEtM+pluumELW6hZwiJFDjY2bCJGdNGFJ4xUrwYfMri0LE7N/BX35rYYhwjLLSPfw O2HXMfNBKmeaCJxmvVnHOeG0Xgf3zqlX1mgEHbTGBllOdtXIIKviHkQkW7kebALeSKj+ 1D9lh6K3yjlBe+d+NQedRD8+lzg1WlD8E36ZpKRMTmpLnofnZPjZwdqNV/k60DN6zI+o CuIf724quhD5akL5BE2ZE3SjRtzq+6QP4qD31A9GvqlbjmWqC6CQruFPXESib022ef/K GX57c0lC32A0Ur51/Nb32c4W21mWCI98K/4j/dcSeE2Q15vv0xbY6gwMDB5agB58iGX4 DBZg== X-Forwarded-Encrypted: i=1; AJvYcCWegirIolPLgMpEyJEX50LAYNDsAqZ32E2PnDzGuV0UT9dRXMTlPBKzHBWCn2GqSwXJ2N2WxK0bCuW0lSg=@vger.kernel.org X-Gm-Message-State: AOJu0Yz6puPVaMRmgvqDQL4o4dckKuII9xBAjC1CmD61TMd9ivMkDwjT FW/oALCIItKnNv/FLsCTgU90yv1BW/RzquxFFWz/tOXYSHOGh3i/36O5T8K1dtIQWw== X-Gm-Gg: ATEYQzy/JhgdubeScrjnHyY4WCTLcK56yhx3YdqmGj7ap1CGkWT454gdHuzQTsHnaiT FDzqtnk/vAGLvcEdGox+G/ZDiesdB11Mn98XeHoW/lSpgPEDVtHiMnF8zEpOoAaVRJrgFxnXx5w YtIOqdlvCBwaAAbYvb2Y8hkfwOgrvCsxi6qO7CllS3li5vZ+gvBn0MS+XES0Mt/PPHR0aBsCEY5 NLruzxMG0PLAi5ErWUTsVL/wSPGsci8E+RMVLLkvNrCI8O2bpSeMkWhOH3zg3gNC+9jz5h0w1GO pQ0IjTacVQ6xTqNWwRCxr9mwajX7Aqqi6cjN3azjww+UZ69HzO+ytLBR5UWsQdvBFE0z3EE0569 w+NZ05pkQFLDlhlXhHQxXz1YMh3GOIIoIKIcQmzwg0OdyI0bNGqegVp21MQGpQ6fF5NUlF3cx7s w4LYmx9TbX3E4aS1y6vcRdd6ZFgw9dWrEjN3KenwxQkEHyK9jbW83w0Xd+53d2j7IhLFluv1PBN j6lwuTuT346uwlE0ITMsH2vWJD8OXEBnrvcLtUMILOD X-Received: by 2002:a17:903:32cc:b0:2ae:4555:479e with SMTP id d9443c01a7336-2ae45557aa1mr81948155ad.18.1772518428712; Mon, 02 Mar 2026 22:13:48 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:47 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:12:56 +0000 Subject: [PATCH 04/12] 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: <20260303-send-upstream-v1-4-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Dmitry Antipov , Angela Czubak X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=16406; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=EaYWZSt8MiuzGzBvQ5vDyQnzb4gbLuNBklZ166MO5cY=; b=qaqgdvZrDDdpWxCKa6Jyx8qxjIgl0F0xgWKp7pkpfhmdFFXzgnQHavit11QBPphrV38p2OG91 h4zWnkOMBwzDEtATh2ROfefT7civcJZUkTaO7dQZ/qMyTXn2FgKMobj 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 | 519 +++++++++++++++++++++++++++++++++= ++++ 1 file changed, 519 insertions(+) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 5431c60efd50..18b035324f06 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 { @@ -60,8 +116,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; @@ -69,6 +143,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) { @@ -83,11 +184,416 @@ 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; + + guard(mutex)(&shid->output_lock); + if (report->content_length > shid->desc.max_output_length) { + dev_err(dev, "Output report too big, content_length 0x%x.", + 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.", error); + + return error; +} + +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; + + 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."); + return -ETIMEDOUT; + } + + 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.", 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.", + 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.", 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.", 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.", + 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 sizeof(shid->input->header) + sizeof(shid->input->body)= + report_size; + int outbufsize =3D sizeof(shid->output->header) + report_size; + + // devm_krealloc with __GFP_ZERO ensures the new memory is initialized + shid->output =3D devm_krealloc(dev, shid->output, outbufsize, GFP_KERNEL = | __GFP_ZERO); + shid->input =3D devm_krealloc(dev, shid->input, inbufsize, GFP_KERNEL | _= _GFP_ZERO); + shid->response =3D devm_krealloc(dev, shid->response, inbufsize, GFP_KERN= EL | __GFP_ZERO); + + 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 ((report->size - 1) >> 3) + 1 + + 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).", + 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; + int error, len; + + len =3D spi_hid_report_descriptor_request(shid); + if (len < 0) { + dev_err(dev, "Report descriptor request failed, %d.", 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.", 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."); + return -EINVAL; + } + + ret =3D spi_hid_set_request(shid, &buf[1], len - 1, + reportnum); + if (ret) { + dev_err(dev, "failed to set report."); + 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."); + 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."); + 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", __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."); + 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) { @@ -158,6 +664,15 @@ int spi_hid_core_probe(struct spi_device *spi, struct = spihid_ops *ops, =20 spi_set_drvdata(spi, shid); =20 + /* + * 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 @@ -190,6 +705,8 @@ int spi_hid_core_probe(struct spi_device *spi, struct s= pihid_ops *ops, dev_dbg(dev, "%s: d3 -> %s.", __func__, spi_hid_power_mode_string(shid->power_state)); =20 + spi_hid_create_device(shid); + return 0; } EXPORT_SYMBOL_GPL(spi_hid_core_probe); @@ -200,6 +717,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.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 2026 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (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 0CDAE38228B for ; Tue, 3 Mar 2026 06:13:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518433; cv=none; b=pASf7jZ0XCuSXMl0KFToCjP45lsfqdnWBdmqbGduv2PoFu0Nit7TgGG+e3CPc0lDNbPZ9OFOQYiCMwJ3MY7Ze4HwcWCN0VWpJfwck9sRExsr8c4CeXaNmXD30rNGsDYpYXfw9oAas7VLvoWwlNcydrU+cbk8TWSrBgJGfd4FBQs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518433; c=relaxed/simple; bh=C4LZDlU0Fvnpa+YtYAHxmO1B30tTrWCHU+SIbyuNZAg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JDTJeuriW1hqViBKbt2TJUfi1AFppTinu91PayHI14s/zq5ZZcFasoLxp36XkcUhwN2NJpn5uyd/vxFzEJ7dPuRCzdbr4j68feNCJE+1rBE1TPwbGqJ8X1SzDq11+mBKOfp0QUta8Q09nr3PGQj2l82kPQGsvaKfCqIvmMdL5ls= 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=iEc/LWA0; arc=none smtp.client-ip=209.85.214.181 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="iEc/LWA0" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-2ae5636ab04so16335615ad.3 for ; Mon, 02 Mar 2026 22:13:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518429; x=1773123229; 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=vEPl8FXJ36FwTyRODKjg7eAVmOxPOUrqwiKxceJZPPc=; b=iEc/LWA0lbfC7i03qxKGwR0G0d0F6Rp/jZmCvjat1cODUimfAaFBtu7J+bbp3+5anU wkVG6IMbPmz77aNsZ7KY5QKv862+H5KrCt7WMmHZHkVOGghJIGIbzYlGGwQdMs0d6i0q IpMHxZjB6cM1/dGm/JshwwavFdzqBvR1oMxnM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518429; x=1773123229; 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=vEPl8FXJ36FwTyRODKjg7eAVmOxPOUrqwiKxceJZPPc=; b=fFs827c6THTFnu2eiP4d2IetjW1n4DxT833iPOIfYrQ1a8N1Z2lw6i5YrgoDXrfrlc MXJzZMmjWMz8WVgSvZor1jxdQK0amJyS9XFGclDMGQTLXQElitQFIk2DN2VtrJAxjrK5 Z8DKlRy+Wdv3rHo79NvZeC2qtG5h6RWYA4MckSKCvXYiLOg905/GQW2xlc8lav8bx/32 mU6JsSrRbm1HgmlY2w/0kqyKoOp22oI9oguF4cbRSUMXQZ5ac0zFQBom9joMoWADnoJ5 yinDVzsdRgPEPwjVX2v4tDkM24lLB7wW6dS8Z5uQ+keSH7c1eQV9zsTksQCRYzm4wtz0 UU9g== X-Forwarded-Encrypted: i=1; AJvYcCWW3/IhVrgTM9Z2yBDO+2s+HzHSfY6tq/4jFhUFOB0JaX6LhuJw5QK0PSV7JjjQZo2j9+gSeJeaeLUIAPQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yy882oPwKQKNJ0DOB2OVUA4RFEET0i+WpR4Ttfq53XnDMl+1nTO 9TDgVyJ42mhyWjhOKtp24i7w5X8gbBxLdFeVkWftvFXrq3YBCN1kV+8iqRi8itfkx7um6Z6hFaU ++fE= X-Gm-Gg: ATEYQzz7YBDjkGxnJ88HGASEcW1BgEB06nVxf6oAWRdB2IYqmvgc/tCrTKTUuEtQKsS Vk5j1J5XQ4le7STID+7ECE53dS0XVHy2GC3jMbZHaqmNFJ6u4JhvRAL3CS6zflFD650aFFxjUNy rqcEVJ4mPpGDA1vajeB27TGNcmFeTPA6gSlI5PYkaRWAe8sL7NXuSkztAqTCQzrhP9ANOfacZ0e I1N9oCDxHHPseQvT9LrADDhV6ZL1WLspEt7OjwagIM2UguGiPc3HI23ZxlpGODp3Vjy/sVutRQv s2iyQU191Da6ccflA660hdNxaCMMcZ5g1DGc0jeffEBr/3y2wEP8EI7V3aHacxldQ350ltup+hW +IqWX8u8M3Mdn84KrhdqKvPbqHmSZjte3yis14Tpuie1rIkiNHU4zTj47YJuBrSBBHKUpHQ+Shx vQeTw/xpjWV+Gaf9P6i9EPTo8QC9vTNw6DXLDyarQorjHHYV/sIwn0elX4vIyi1n70VFwHZ5ct3 zwo1lyUcO7XOAC+UaHAgkJhSvF79SnwwQ== X-Received: by 2002:a17:903:4b4b:b0:2ae:4d13:ae90 with SMTP id d9443c01a7336-2ae4d13d414mr81370595ad.37.1772518429410; Mon, 02 Mar 2026 22:13:49 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:48 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:12:57 +0000 Subject: [PATCH 05/12] 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: <20260303-send-upstream-v1-5-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Dmitry Antipov , Angela Czubak X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=21692; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=C4LZDlU0Fvnpa+YtYAHxmO1B30tTrWCHU+SIbyuNZAg=; b=w7peWTEp5VBEQqAb0qJbfqEgSNAIrrw4vCSpcG0HOmehKdfHt9FBpsqukgL7OvU9FNL2WsCp9 bWmf/q0QC94ARDlyQxfpv97Ny7mDfcy98iW6K7zi2HdkHnU/FFdzk0p 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 | 562 +++++++++++++++++++++++++++++++++= +++- 1 file changed, 557 insertions(+), 5 deletions(-) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 18b035324f06..08865d42555f 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -23,11 +23,16 @@ #include #include #include +#include #include #include #include +#include #include +#include #include +#include +#include #include #include #include @@ -35,12 +40,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 +64,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 +88,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]; @@ -113,6 +160,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 @@ -130,10 +180,17 @@ struct spi_hid { =20 unsigned long flags; /* device flags. */ =20 + struct work_struct reset_work; + /* Control lock to make sure one output transaction at a time. */ struct mutex output_lock; + /* Power lock to make sure one power state change at a time. */ + struct mutex power_lock; struct completion output_done; =20 + 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. */ =20 u32 regulator_error_count; @@ -145,6 +202,66 @@ struct spi_hid { =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) @@ -156,6 +273,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.", 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; @@ -195,6 +339,50 @@ static void spi_hid_stop_hid(struct spi_hid *shid) hid_destroy_device(hid); } =20 +static void spi_hid_error(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; + + if (shid->reset_attempts++ >=3D SPI_HID_MAX_RESET_ATTEMPTS) { + dev_err(dev, "unresponsive device, aborting."); + 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."); + 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) { @@ -249,6 +437,86 @@ 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!"); + 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.", 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; + + 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"); + 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.", error); + return 0; + } else if (error) { + dev_err(dev, "Bad input report: %d.", 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."); + } 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. @@ -268,6 +536,8 @@ static int spi_hid_report_descriptor_request(struct spi= _hid *shid) if (ret) { dev_err(dev, "Expected report descriptor not received: %d.", ret); + set_bit(SPI_HID_ERROR, &shid->flags); + schedule_work(&shid->reset_work); return ret; } =20 @@ -322,6 +592,205 @@ 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", + __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.", __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; + + if (test_and_clear_bit(SPI_HID_RESET_RESPONSE, &shid->flags)) { + spi_hid_reset_response(shid); + return; + } + + if (test_and_clear_bit(SPI_HID_CREATE_DEVICE, &shid->flags)) { + guard(mutex)(&shid->power_lock); + if (shid->power_state =3D=3D HIDSPI_OFF) { + dev_err(dev, "%s: Powered off, returning", __func__); + return; + } + + if (!shid->hid) { + error =3D spi_hid_create_device(shid); + if (error) { + dev_err(dev, "%s: Failed to create hid device: %d.", + __func__, error); + return; + } + } else { + spi_hid_refresh_device(shid); + } + + return; + } + + if (test_and_clear_bit(SPI_HID_ERROR, &shid->flags)) { + spi_hid_error(shid); + return; + } +} + +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 (body.content_len > header.report_length) { + dev_err(dev, "Bad body length %d > %d.", body.content_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 %lu.", + 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 %lu.", + 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.", + 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."); + 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.", + body.input_report_type); + break; + default: + dev_err(dev, "Unknown input report: 0x%x.", 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).", + 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.", + 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."); + return -EOPNOTSUPP; + } + + if (header->sync_const !=3D SPI_HID_INPUT_HEADER_SYNC_BYTE) { + dev_err(dev, "Invalid input report sync constant (0x%x).", + 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; @@ -338,6 +807,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.", error); + set_bit(SPI_HID_ERROR, &shid->flags); + schedule_work(&shid->reset_work); return error; } =20 @@ -357,9 +828,81 @@ 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; + + 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.", error); + goto err; + } + + if (shid->power_state =3D=3D HIDSPI_OFF) { + dev_warn(dev, "Device is off after header was received."); + goto out; + } + + if (shid->input_message.status < 0) { + dev_warn(dev, "Error reading header: %d.", + 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.", 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_lengt= h, + false); + if (error) { + dev_err(dev, "Failed to transfer body: %d.", error); + goto err; + } + + if (shid->power_state =3D=3D HIDSPI_OFF) { + dev_warn(dev, "Device is off after body was received."); + goto out; + } + + if (shid->input_message.status < 0) { + dev_warn(dev, "Error reading body: %d.", + 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.", error); + goto err; + } + +out: + return IRQ_HANDLED; + +err: + set_bit(SPI_HID_ERROR, &shid->flags); + schedule_work(&shid->reset_work); return IRQ_HANDLED; } =20 @@ -661,11 +1204,22 @@ 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); + init_completion(&shid->output_done); + + 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. */ @@ -705,8 +1259,6 @@ int spi_hid_core_probe(struct spi_device *spi, struct = spihid_ops *ops, dev_dbg(dev, "%s: d3 -> %s.", __func__, spi_hid_power_mode_string(shid->power_state)); =20 - spi_hid_create_device(shid); - return 0; } EXPORT_SYMBOL_GPL(spi_hid_core_probe); --=20 2.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 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 88FD23822AE for ; Tue, 3 Mar 2026 06:13:51 +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=1772518435; cv=none; b=shi+VD+VkCdpLShvpyjBDUpwCi6Cw0Bem9q1a+ar0rntL58o32+9n0tQc2rrObs02AESzozJ4PAJwsSAsNBB3PZDNyhsbgNdg9SnUEdvqYE4yV7pYs/FWom+qQV3rwhEJDBLgy1sJWOJWLIiOI/HJMQyApTLXix/HX+kRMqfMKQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518435; c=relaxed/simple; bh=0g1QWhz6+O5/l4VAgRjYr0puTKmj3ArJ8Y9hJXu55P4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LHYa2j0DGyf5FG2abjFjgOvFqN5JUxtgnV6BbUDJOVHoBwQvQwTiDzxAYHhUAkcDhKYI2FUdO9xXE7QglaXgO8QdCjwenfKWOyRlJS7s9gTBMxL9Nz6yJ+PzP9oZWjQBnfcDk4mLuFNGwpvD2gs8bjV/ag6UdqM7oO2NR7CLLCI= 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=fQc9FI/q; 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="fQc9FI/q" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-2ab232cc803so25237525ad.3 for ; Mon, 02 Mar 2026 22:13:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518431; x=1773123231; 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=PGp9P+3go41oALz0pMzSRlLuZvq3NDQcuPYrLYr8Lw4=; b=fQc9FI/qg9JiU2kWDLyC74bseHi46wYpBL14liOwg7iTpj9vttOHVFyuI/o+Cgom2o EmS4mSHHvnb0k3UdmrT+B5vzlcnFEs8s1gxXmCpEC5O/heGYqrmUJkdBvZ1ARWyuNjHE ItB+kyA8FomVyPqHRo+pALiRntSAuZcn3+SSo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518431; x=1773123231; 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=PGp9P+3go41oALz0pMzSRlLuZvq3NDQcuPYrLYr8Lw4=; b=IdKxApd0E5gm+aOrSIPrm2zvWBpoBqcyqH7KQaXw+DOLKJRW0tHCmQZb49wtBRSeAT okf3HyACB9ZrdGEsxCiqe+2rtnDU9SlcxVAjUoMF8ao+zIRRDVLSLJs0mlk5lsC71GjO 8mK0IAJTM3VUtknTC0tOaq3bhf2IWcV8grzAsdGlL7GowLTJLqLs8QzqrdJh7A/5B8kU NkJjtqvwWVagJFwsvjjQc/gvZlU8taJnJTsVMb2We50Sr6AkcqXDB0U2ArsiAMbXf0WR prI9KhlI7NYLi2xNG6zgfNmjTTFSaPRQB/RLddALMcm13NoDduzjNEp1JCmqtd81tGOH zg6g== X-Forwarded-Encrypted: i=1; AJvYcCVkOIvFV/qmc2VL+r8L26qsJ9rTQ/IeG5+YDuG+ECQNFuRcI54+dMw8eUJOP0e1orjuVeQvEzlrBEz5mSI=@vger.kernel.org X-Gm-Message-State: AOJu0YzSXl0shtxuTlf6I5ntPzXzD989CKAXvb2PHXMNHYgHngaW/i+5 c5qu2RdoWf0YWHYp008Ob9VGYeIzjsbQi171C006p55YU2RIdDf9d+gmEbrt4vEkcw== X-Gm-Gg: ATEYQzwewjYKaSyF8sBDLVDBiC99bDT8aWuWWB4yHNGIHaeN2TfMmPXGCcu3RfO8wdy UiUpbwVRXsTllCR0LRM5KImvz/ZtJfFuhR7zpmupTt79ujaoiTIx4XhXwuKS61zUGyYoUvqXser e3x49It0VWyYOVXZL8A0/HIx0C/DAFCnuKWH72FHYe/djfGkF+zoUaG5PXvd9xDpnbipTE61xZg 3TsihnLFyL/FdxV1gfvYD5roQqAtSOEwQ6I+63jEUfm+apIagMs3KHT3V4g93nlsySuEIINcY3P PjkRKRo6+18nlZfSQUwmntWv5EaRxAv99+4iJlww6f6CGP100aLfFPDsZGhldIfyb1wRJHfXnkc 6r5GtY+dJ593YX5FK3dqUJ9aUiVZBaka0OkmlyCD4UWimJoY2MeVms7Fl25FO2fYPuwCtLDAje/ PNCCHwnHxGCkjCcl9cz/iTGnSijnynRnaQ/Rjm6L9OqZrTQ1fcOO1Mep/eTldLq+X5rcyDwhbOA y42YzC7ePKTWVfXpk3UUvzAkxIp2iA56A== X-Received: by 2002:a17:903:1a0d:b0:2ae:5b3f:a751 with SMTP id d9443c01a7336-2ae5b3faa62mr32971725ad.45.1772518430878; Mon, 02 Mar 2026 22:13:50 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:49 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:12:58 +0000 Subject: [PATCH 06/12] 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: <20260303-send-upstream-v1-6-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Dmitry Antipov , Angela Czubak X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=11596; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=0g1QWhz6+O5/l4VAgRjYr0puTKmj3ArJ8Y9hJXu55P4=; b=aaYml8IyRBxlBFUMYwPYesuynyGMClXYcXM4fD66yuuDrQQYNh/I3Cix0L9E0/yoB+0Xgy8Zu vEtA+HCHwXHCQvBBR4iXNLogdRr2+TyCTvQ8NiCDnt04VBi66qSQe8G 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/spi-hid-core.c | 71 ----------------- drivers/hid/spi-hid/spi-hid-core.h | 83 ++++++++++++++++++++ include/trace/events/spi_hid.h | 156 +++++++++++++++++++++++++++++++++= ++++ 3 files changed, 239 insertions(+), 71 deletions(-) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 08865d42555f..e3273846267e 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -81,13 +81,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; @@ -104,12 +97,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; @@ -118,19 +105,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; @@ -155,51 +129,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 make sure one output transaction at a time. */ - struct mutex output_lock; - /* Power lock to make sure one power state change at a time. */ - struct mutex power_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. */ -}; - 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-core.h b/drivers/hid/spi-hid/spi-h= id-core.h new file mode 100644 index 000000000000..2bfdfbe6d7fc --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-core.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Microsoft Corporation + * Copyright (c) 2026 Google LLC + */ + +#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 make sure one output transaction at a time. */ + struct mutex output_lock; + /* Power lock to make sure one power state change at a time. */ + struct mutex power_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. */ +}; diff --git a/include/trace/events/spi_hid.h b/include/trace/events/spi_hid.h new file mode 100644 index 000000000000..e9a579b3806c --- /dev/null +++ b/include/trace/events/spi_hid.h @@ -0,0 +1,156 @@ +/* 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 + +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 shid->spi->chip_select; + __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 shid->spi->chip_select; + __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 shid->spi->chip_select; + __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 */ + +#include --=20 2.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 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 B1EE2382364 for ; Tue, 3 Mar 2026 06:13:52 +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=1772518437; cv=none; b=G1Xw/0cQYC9xSKh0aesJFa0U8xeMsJsPg58ezHBKq0DXB0rFBuE/Qg7nM62a5jkGW0lJwNXC+5rOb4lFuOcyWePY3vo80pHJvCg3+4pOyEivRNfPuBitW52njdydVvciwBqEbr3rARX6UX8Ak5SR6UT8t8swEsIVXADM3DBRGbQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518437; c=relaxed/simple; bh=y2Cem10Qv1YmWLyFKJBsPz4T7G43UQJxTY5IpBrrdHk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RKUVkHJWIM4sB63J64yg3IWziMobXeRjFebUaxCeTs+9VdqQqBKMK7TGinsdm+TvL/NqqnLtM7lOeCO+WlQe5RQupE+oivyVc2WzO8tqFNpzR5d0FvVyZuGN3ydUNRJSDa/625+bcX9IIaoxF0Rm5UOl9wJ4cCg/QVn9WtR4XBM= 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=EW7uCRr5; 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="EW7uCRr5" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2ae56f8776dso10516625ad.3 for ; Mon, 02 Mar 2026 22:13:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518432; x=1773123232; 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=kui0y2+cGzzQv/dC2bho3UUUq5l8OpAeevb4T+wuEN0=; b=EW7uCRr5knO/a8NNsKShKCWEt/yum/GK0PM24VqApBpH8plfE+ixG0fPd7g8y0NEuN rzu2y0pmfds+5QQwfk+4LcArl2BHSiDDugOQUHlELQcpVaZAZADOAsdfly3K6BH1xS3V cbmaeSMIT6VC5WH6g1b76ek8vekVGLhyVPwUY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518432; x=1773123232; 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=kui0y2+cGzzQv/dC2bho3UUUq5l8OpAeevb4T+wuEN0=; b=Sp3uV2gmPrK48rxUls6oJ+Ujlt8zDdf+WJDHxKM18t9Krq/Y5uCDpJ8f6M4bTWhk+j cKli7pkZatPxq3LgcgB1cZxfA0B8I3qJEisqTOk3y075eEcwExEN00vV6+uQMKBdObPr 4CRFm6vwkjgyL6T2um18MeNt1mEewnbiKNyT0ejsA3H1tYBLGxNdcPdykHEAuLEKIm5V /PpjnLtPM/ukreEHSswaNBG1UezrONeBLgwbfE4uOYKsAosCuhZCBROIDHClMdmcnBnq srgzp1Ru914trfZnJcJqfXnUwQQvJJ+Js54IaKV3pxL2HG1VHBsFzjJsm69XAccqOdeh HU5Q== X-Forwarded-Encrypted: i=1; AJvYcCWkw8+OAmatwmibIKE6Lh+RxwcpTxENPjLCLO/CXJ+wUI5bvA6yCOaLk9XxlQiC1nHp/s4rl1TF1l2k6KU=@vger.kernel.org X-Gm-Message-State: AOJu0YyoQPNwOFj5n+AtxhXOpz6iveWw1PkTJE7CkCDy4LnNM7XuaODl i7AJQw+Op9cDqN13/4q4obfeHsVIIo1nu3jgz9dyZXiKKbRx8qAh+7FIVQMN3a+qPA== X-Gm-Gg: ATEYQzyOYCnqI+rgaPYLMc14vcdvVre8jb+FfqkMQQCEqk/1CV5VaxMLrhY1+e81ykF 7oPvBZrTP3f2E+LcR/G8wqtrapAew7OY4SmK/Ahb05Vqr6M1JypOO11dEI3KQf7cEbSKKWR55SP 5gruNitcIDB4E/npacEVhu4HuSdO5yTZTVcknYtcBgmhBguh5sY8mPqQnHNnmNjm72PS3lJqnXg pJF75j2vUhleEHjo10u5mqXe/sMQ2dFdp+dsidhOE+YCFFshiCpCF6pwUNl7W2vmhTKFrAEvfJz mHDwdZksMCCIfdcd6g1A0NQy3Xsbm7iFKdd8Ql4eB+XjRbNfmeyxQDlmQv2wSiZkI+QEy1wt5HC T9rfLUkM9vDNdSNtgtbADvtvzOCMlQbdZMDMdXWlyGq1cX3HOuO9ypcKZFQnSFLkd9Pu9VG+Pyq +dhjDpcHlCMLk15oIJkWn8iC5r0g+vnnpp14GDTDmd6c0Ml50ZGWBVWwKIRgdJUrbR8ubWS7niR 3hG8T9HTMl5+1GTeG+Y/pJ1NHaG9SKliQ== X-Received: by 2002:a17:902:e74c:b0:2ae:5275:4d40 with SMTP id d9443c01a7336-2ae52755397mr54277375ad.56.1772518431896; Mon, 02 Mar 2026 22:13:51 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:51 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:12:59 +0000 Subject: [PATCH 07/12] 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: <20260303-send-upstream-v1-7-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Angela Czubak X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=12219; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=SxaOYdJ5yvt0fUwumywWmbgQu8LfGh+4B6mGGyxMnGw=; b=9tt1vrx/7LmyNQ4Jr15LFthLcr1k6rwnK7brbp8XbLBJdjdL+nK0G7hF4JLAWHTV+MNsLL0Iu HRLBPCShjcoDghDUNJZyF+cDeW3x27CWyiWlqWyYSOjEwRZpR+ZN/vg 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 Signed-off-by: Jingyuan Liang Reviewed-by: Dmitry Torokhov --- 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 | 27 +--- drivers/hid/spi-hid/spi-hid.h | 44 +++++++ 5 files changed, 316 insertions(+), 24 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 92e24cddbfc2..753c7b7a7844 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 +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..612e74fe72f9 --- /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 -ENODEV; + } + + /* 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); + usleep_range(1000 * conf->minimal_reset_delay_ms, + 1000 * (conf->minimal_reset_delay_ms + 1)); +} + +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 ACPI_PTR(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 e3273846267e..02beb209a92d 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -43,6 +43,9 @@ #include #include =20 +#include "spi-hid.h" +#include "spi-hid-core.h" + /* Protocol constants */ #define SPI_HID_READ_APPROVAL_CONSTANT 0xff #define SPI_HID_INPUT_HEADER_SYNC_BYTE 0x5a @@ -105,30 +108,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 - */ -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..1fdd45262647 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid.h @@ -0,0 +1,44 @@ +/* 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 + */ +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.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 2026 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (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 89966382372 for ; Tue, 3 Mar 2026 06:13:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518437; cv=none; b=R6TpfEmC7cT3Xwqahn7ZrsOt21tZajH88p69fN5d7EXKDbvptZW7dS4pwPwiVH7WbBLqjl3HlThjtjJgM/ow/fC85tHZYxH6Nn7QIM/S2KTow4p0esSzLGmyB4ZmNi1BsnPg//IZZfYlSHtlhBLP4i4Ugc+N3A7F+sMlsUTCDlM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518437; c=relaxed/simple; bh=0OWVvkpb8UuVF2dcKxIXv4nkgilqjcELXDqX61I3pA0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cl/IX66xAVQi8P3n1alc8TSMhQwYYQJRa2W/88p0ptSVXAKraBEiFlGgfZYQt0Z5OBOl1hIOFRVntPW/63uKZ2bKZsZK6b2/ZmVgRVTCB7MBEZWClIgmQPXSc1IHhd1dYOkKiZoCSw4RMMZI1PdbUB6vfE7PbGyMBfagV0j6TxM= 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=fJ8/Z8bx; arc=none smtp.client-ip=209.85.214.178 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="fJ8/Z8bx" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-2ae41544dcfso28639095ad.1 for ; Mon, 02 Mar 2026 22:13:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518433; x=1773123233; 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=jrh1Fd8nSQVaqqRA38zJbtmK+DIMwuwcBDvEg12fGDM=; b=fJ8/Z8bxteLz7rT+nhqFNSDZRHnUA3zljqh6wFYM44pFWZ9bdu4PR5AlwDukloOSyE QIpIESPK6a89y7YtVcj2M+mLTBTRDScVfBBBVJnIDThy2Tc/YcQPxpV40XTMsVh7lmEI pn42nBWpZsP3EG2thQFSyqzmaXyKauIUU4pk8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518433; x=1773123233; 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=jrh1Fd8nSQVaqqRA38zJbtmK+DIMwuwcBDvEg12fGDM=; b=dx0VOUGv34+WNnVkzrGgRO0SlgHNpRI20V/u5mbWpUEVZWDS9ALTPWCosVXqeiJgem LlCPq5I4/BBfQEJN3zFYnVJmazOL8MxgZFYrMyN8kktPop+VcavwgoVpP2D1PUvxyQEb LyLemzQQW+0rLc4v6NlUlCgh4DF17DHFtkUnePpomPSD6AyX2P2eP7/y0RciRmvC2c/6 w+PELRTjV0lDtkNnsSuU/qVBVB+ajUAzTIzTLVlNfwaoDI28Xxae49VYInLZQmB001lr XouEsvtQffAyFxEKYdWXRXoLmpEX81N00txyfpUxBumpkrm3FfTpkjQZNfyC9KgCRLaA 2JuQ== X-Forwarded-Encrypted: i=1; AJvYcCXATTBRJV50D4TX9gTmmNxgN6JYJKw9Q3OX5ICSxNVgCSHDX+fPjFzIIeTg1UpKJ8qz8N0S6qwlVscOsn0=@vger.kernel.org X-Gm-Message-State: AOJu0YwjBbV2WyF0JZReaVubIJbOrQH4s17HjNKjXdlWFk4iWCsZITFE PgknyByUrG72YG0wARTQA54zsnnxJKWd+AoMljgc6iv3IWPHVBYoIKCgWz7/isfspA== X-Gm-Gg: ATEYQzwZzVn8hvS8M3x/n3aFr7JF/vFD0a0J5UqHxd3JGTRjnb8C9kSyMnqFJmGiRS1 vH4d77xYIuBGhkgpA0dTxPx+/cZIPHA412I/5xgkB2IvTFRZSZkaUD1mxjD1FIbx7Q2e6Si5kAB R+AUmdYEHWKFrOiISabJ5Vby+mPrvsPU6QV8vBMQ1K8bNtLE0TJMu3XJR9S8Ort/Rxwhv1xc3ww pQNZPRbBIYmXgAlaVHY2Yfi4r8bunltHFU9Lmx5l55Lrt3S0cms0e/cyMr+9Ge6PV4nHKpKAI1u bBoSqgdZO2Me+RfjN4qwc2bR8Ft729QQogwxtPUx26Aw6PUUgQMw+SEPcQXfZr4caO7GXwF63St s4pXrNyvPfmYmUEWXS1VIkEEBXcbstVEhuulj7gZF5Bssf+RZ97y4LJ3hux4Sxz/fn64pU+hJWU rObRdivFX2JuzNj1MpF5kEM3WXtnMJnl4RPHQ5RbA1c1Yyh7ZJBpG3Ln2cxVqwkYWGhjNqI36FU mSwaAauf50ub14HSwUdtxJxuZ4QM3UabQ== X-Received: by 2002:a17:902:f68b:b0:2ae:5851:9960 with SMTP id d9443c01a7336-2ae58519b53mr41949005ad.21.1772518432883; Mon, 02 Mar 2026 22:13:52 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:52 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:13:00 +0000 Subject: [PATCH 08/12] 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: <20260303-send-upstream-v1-8-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Jarrett Schultz , Dmitry Antipov X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=8754; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=DhX2Uh1rHNONR9PWKjCuHamOIXQpxg2tk//xxr/GNPg=; b=dDRFUCZnpLF1KnXWXYiRsSlUS8MJO3AWwE32XYsswZX0/B6AABOdoeQqF2SlQ+BRC4FVSDbK0 QNBC2h7LX40BSzuPL2J/nM+D3eAr/CRBC5O6OGBgYZrNS/o2yrtw80H 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 | 243 +++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 259 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 753c7b7a7844..fe627fd378e3 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 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..a20c8146230b --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-of.c @@ -0,0 +1,243 @@ +// 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" + +/* Config structure is filled with data from Device Tree */ +struct spi_hid_of_config { + struct spihid_ops ops; + + struct spi_hid_conf property_conf; + u32 post_power_on_delay_ms; + u32 minimal_reset_delay_ms; + struct gpio_desc *reset_gpio; + struct regulator *supply; + bool supply_enabled; +}; + +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; + + error =3D device_property_read_u32(dev, "post-power-on-delay-ms", &val); + if (error) { + dev_err(dev, "post-power-on-delay-ms not provided, using 10."); + val =3D 10; + } + conf->post_power_on_delay_ms =3D val; + + error =3D device_property_read_u32(dev, "minimal-reset-delay-ms", &val); + if (error) { + dev_err(dev, "minimal-reset-delay-ms not provided, using 100."); + val =3D 100; + } + conf->minimal_reset_delay_ms =3D val; + + /* FIXME: not reading hid-over-spi-flags, multi-fragment not supported */ + + 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; + usleep_range(1000 * conf->post_power_on_delay_ms, + 1000 * (conf->post_power_on_delay_ms + 1)); + } + + 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); + usleep_range(1000 * conf->minimal_reset_delay_ms, + 1000 * (conf->minimal_reset_delay_ms + 1)); +} + +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; + + 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); +} + +const struct of_device_id spi_hid_of_match[] =3D { + { .compatible =3D "hid-over-spi" }, + {} +}; +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 of_match_ptr(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.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 2026 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) (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 AD8C7382395 for ; Tue, 3 Mar 2026 06:13:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518438; cv=none; b=by7uHyTqOYYYBZMLHtUjkKe/XFhsxQ27nU8Begn4wtT6+06Eiz5Mj1yxClnj+uWZu4qJrWLs59/bn8w9avxc5/yZ4ztCF+ttCoXYcNS3Je24CfsZFGIG9IUHBcCC6scrgxQQiGWdpJrf8iZbG9sf38TUSZWl/u7jiOgc87HShXQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518438; c=relaxed/simple; bh=HKsF8s2CN39ptYXnUpAjllMxDvOrGH7BmQr/pDNdroM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YAVbX4oC0SueXVNSJMTS60h/Sb7Qcnuev5UhFXVFGP3RrLdJz4UTiWC0Rt+TCeKuQTr7vZJwwR8AfIMFHhHsha6P2M5VqzXJizrsIevbsVhfTgySELuXUXklvcWgM9JZS0cHjH4HccKvHfGuPEv1S0ictgfUyKX/eka0co3bi6M= 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=YnaJKba/; arc=none smtp.client-ip=209.85.214.173 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="YnaJKba/" Received: by mail-pl1-f173.google.com with SMTP id d9443c01a7336-2addb31945aso39081085ad.1 for ; Mon, 02 Mar 2026 22:13:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518434; x=1773123234; 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=1zbYunSd9SIDIr6FUrKkB2zIIm+2n+JbdlctTH55ffY=; b=YnaJKba/CUPhw6WwK6kd5nrN5VX9i36RPXyzclk5PLwufyN7lcKbfUO4l8c/CSHPcT nMY06euorAc+J+0Jv6l2JaF3u+TrHniCG11W5hN3kKgRQ6iSsSyz1v/zUy3VH2mu4toi pKNA9nsVj+zL8Nw3CEqskl0bMs8cC8mQGB1Tc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518434; x=1773123234; 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=1zbYunSd9SIDIr6FUrKkB2zIIm+2n+JbdlctTH55ffY=; b=w0GdxvXm+/2t5MRvEqvMAePTpSX4eM1pkj0tyBkB+fkYqR3Iac1dozlNctSaG8b/NK XSRiTf4rE6H+Z12PST4O1E2/2GI7soSRcLO7eJnQ7JMUT0KlCrQdUfbVR4rfw0iSDlg+ LVPtxMRDhIS6z1r4idiJo9gLUorJc+yy2l8hiy0plebcJhtXhMXP+DEfv4QDSdtcDBLS wRXDVrvNlG3K9AB8K3X6OVPSIgWXc6B1Hh/RqwgVyOPjYlHmve7dKIFhn1KQ5JM2v45x LJ9dl20EQ/P8xXCfKYRusMHv6vQF3H+NKnwbxxd+WmvhbXuJoIbYDrXP1nyszjUCZ3GE UKow== X-Forwarded-Encrypted: i=1; AJvYcCUAgxiTk1Jbf4EdvgdA2MbP6c7ewM/vhFEx7YTpwzmUeK/Y/TIfHwW3SX9tbtXEPwiYvNhHvohvJbdnJ0w=@vger.kernel.org X-Gm-Message-State: AOJu0YwtJV6QBkitFxjuYnYJw1BkihgLq7Y7mxe0JC1y7wVsh66d7Yns l3oK9fRBitKTh4lTPwqSbn4JAMyEL4+S0jGuiIj1KTCkKo9Fypi1PcI66f6mW5j7xg== X-Gm-Gg: ATEYQzw6LBbqtqPP7syD/ANo7/HZ6CBRxYjRs3oC8sbAmWTdiHBv268Bc0XyB9dW82k mnQlSZYg2FtdVg7vNx1FuOMNxEI4iO7/7Z3V1icfdvYbhzPyfiyJjfxqJwDmeRTvc4HIpROiGwr rU3911u6L+2V4Uc9QxUss056V2tC2qOIJRFkSWOvaQ8PJ4l7EnF9v0yX9kstJaf7J4+yK4bNmOi 0hmRGZJqtJQIoyGIpGczVTZkKKf31rngKLSrhCvSzue572FUPNeam0bWSdeQwAN13sFozkS5iX5 OLg0/ZT6A5ry9gtDMi1P8l5oTkuv4oXqU/xs/YaaY+DpvmygbCe619rzvTYJ3i1Zz90ZFk5JF05 2NDZjjg56OJJso5Z1kY3NaJ/M59DEvSoJagebnCX0Q/FSPBJN1fQe4VZ2Xth1jkUO4xhXFkIIt6 RjsG/Vg4dgdJeq+Z7sGGwE/B5qNn2pvKZvINX3yuf08/V/XbAaGgVvIlr9vLhoZMgu+3BrN9FI9 mBBr2Kwz1UiXI06Epg4YD7T3wU5y8Ndrw== X-Received: by 2002:a17:903:1786:b0:2aa:e817:1bcf with SMTP id d9443c01a7336-2ae2e4d7a3bmr137864645ad.51.1772518433772; Mon, 02 Mar 2026 22:13:53 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:53 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:13:01 +0000 Subject: [PATCH 09/12] 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: <20260303-send-upstream-v1-9-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang , Dmitry Antipov , Jarrett Schultz X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=5693; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=HKsF8s2CN39ptYXnUpAjllMxDvOrGH7BmQr/pDNdroM=; b=3yauzbUstrOGl5qDbA/zaXfBs8F8XvC7hZJ8BVjAu9S340VlZ8h1r6uNBWzD+nBmeke1laoEl KMLb+MDQtJyB1u7HQFp7CyBh7fKvz7M3Cislqv92CB/53/zJ5KRua02 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 | 153 +++++++++++++++++= ++++ 1 file changed, 153 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..b623629ed9d3 --- /dev/null +++ b/Documentation/devicetree/bindings/input/hid-over-spi.yaml @@ -0,0 +1,153 @@ +# 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 + +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 + + If this binding is used, the kernel module spi-hid will handle the commu= nication + with the device and the generic hid core layer will handle the protocol. + +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - microsoft,g6-touch-digitizer + - const: hid-over-spi + - description: Just "hid-over-spi" alone is allowed, but not recomme= nded. + 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 addres= s 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. + + post-power-on-delay-ms: + description: + Optional time in ms required by the device after enabling its regula= tors + or powering it on, before it is ready for communication. + + minimal-reset-delay-ms: + description: + Optional minimum amount of time in ms that device needs to be in res= et + state for the reset to take effect. + + 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. + + hid-over-spi-flags: + $ref: /schemas/types.yaml#/definitions/uint16 + description: + 16 bits. + Bits 0-12 - Reserved (must be 0) + Bit 13 - SPI Write Mode. Possible values - + * 0b0- Writes are carried out in Single-SPI mode + * 0b1- Writes are carried out in the Multi-SPI mode specified by b= its + 14-15 + Bits 14-15 - Multi-SPI Mode. Possible values - + * 0b00- Single SPI + * 0b01- Dual SPI + * 0b10- Quad SPI + +required: + - compatible + - interrupts + - reset-gpios + - vdd-supply + - input-report-header-address + - input-report-body-address + - output-report-address + - read-opcode + - write-opcode + - hid-over-spi-flags + +additionalProperties: false + +examples: + - | + #include + #include + + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + hid@0 { + compatible =3D "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_reset_assert &ts_d6_int_bias>; + input-report-header-address =3D <0x1000>; + input-report-body-address =3D <0x1004>; + output-report-address =3D <0x2000>; + read-opcode =3D <0x0b>; + write-opcode =3D <0x02>; + hid-over-spi-flags =3D <0x0000>; + post-power-on-delay-ms =3D <5>; + minimal-reset-delay-ms =3D <5>; + }; + }; \ No newline at end of file --=20 2.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 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 A3771382F0A for ; Tue, 3 Mar 2026 06:13:55 +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=1772518439; cv=none; b=gQZQcHIVnPJzv2+Q//GQYqmCwRzojmUq8sGQoIPfMSTfd/H2QF2GxA54Iwu4Yw3Y/jQ3nF5e9XOCABtPkM9RhIQ9KxX/D377Hx4BCzkvOqxfdGr9uhc2+nOB2sO2os/jcKVUALXbuhvienliUG1oW0DFTnZHDUE8uqPo47L8LWo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518439; c=relaxed/simple; bh=2NiZAoDC3alIu0louVywwr639R2HWq7OVqahJ+UY2qs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rwr9/R5Uiqt08gN/9g/FxHia+Wnl8dRUGPmdgZF558VcgXias7B+GnrK1+fF5BA8SqKnR8FW7JvilAwL5XngKCV+Z+rt8giGa2ZReBdljnJe023LmemiRpotuFsGtvRZITzdcko2tXhNCiLpevycFQB6sDLaeQLGSVcpkHBUIhM= 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=RdQ9h3Us; 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="RdQ9h3Us" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2ae46fc8ec1so14459415ad.3 for ; Mon, 02 Mar 2026 22:13:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518435; x=1773123235; 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=gUpkwHwpX2KwRPifYdo4IGj691Egz/3WoBPDe25QAL0=; b=RdQ9h3UsQiD6V/BKBw+xY0wBto5UiKVJhlxbU4j2Hi0qGvtksGENLY6CMtXoO7Hf/a 6vLi9GU/Up/6BnYhGcdCELhWkcttK7UbqwQcV3l6OdPzX1/ddDbxZDP2qsLT5H62eGAi bfM/p/fG2e/FlZIWQAQyC2s2bgAwCkbWFdiOk= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518435; x=1773123235; 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=gUpkwHwpX2KwRPifYdo4IGj691Egz/3WoBPDe25QAL0=; b=pB90xp+uETd1DvQjGdvxSyKyYXXsGiuvFIHwnlOu4lE91XWGsqgoP1GpAabTKYCeHJ WU7z/liAEoqx5GGWCBu75lrYCmUFZBzqrHuuiGuyaKwWf6sxHEQgMZIlX90inGgLz+Xj zxo3sL0N1Ouh88QBjWbEVnuGHVrgJeizzFeTGZV/7fFP/iQZMr0iDYczGKoVep7qSivA jnMhdpsSznLY1XZEAh83vpnikFL+oUXz+Scf2nXH9CRErx4LZ9mMqi+dx0RCafBF4eTb E/xBQz3unBCp9G6MraoiC6+5gB9Ngl0DmAHyCfZMG62EMjSTzkS4++d6nDUwr6d+qF8/ jyLA== X-Forwarded-Encrypted: i=1; AJvYcCXJOSvRJTXR9cOcTAo0IVIwEq4LpPGy+eL3b0N0Yk2Mq92zVk4V5EtC/Jf9jQa3IQDQ8+tLq3ZBoShySss=@vger.kernel.org X-Gm-Message-State: AOJu0YydXYdOdmsCoLliQtHLJtUSJIBNcVhw94ormlfPJwpiW/ndkIpK 7k8HKxXXyeD4gOrYH9hBI/KOtS+V32BEs70cPvoJQ9vUnxCnc9FsH4rwqtrDm5/ZuA== X-Gm-Gg: ATEYQzz5AeQTdjmPvuDKX2KgWiR0NuYMSdarCTFRix5nTlKnQRaFtks1q6rDNaYgywN WvgmJSUWZCoilBXTudcXFrr7YZFuLMj/zbXZtdapzpy6otk/UFH5dOn0xB6+wnPncxavKJmuQ7L sBuD2TBxxxWZgGJsfUOZgePCxS23iviAsVFVywSanGdUtYbQ7Ur20zsNJY9XirK172vmrfObDPS Uwr1FRVPa/u4pRVM6s8CrsUPvkiYikXtCzbni0YqSRaN5l3PqSjlTzRjdkpGxvs/nbvDGFXMbyg bl/zDLTVE86TaKBZdi1rkN7GoJcwyTxVmPLucaalSQWrrItRA6uL/0snHnoXmWtSQ7w2FqmttsP wB8jLT+TPyfFI7AzG+U0YIfQkUWdIRjPqQUnnZMp5qpHfjz3AOUu/cMxYe/rxOpF/dFD1WtkFAh xdIsUjICzNpz75tWYC7HQdBY5/e4NUE8Co2Hr8m+T9ERUkxbkBv3LZfxL1Qwlhyuszln3VZhvGD BuiNz+K+/g8pqzgxD2dhfcY9jtd3U/z/A== X-Received: by 2002:a17:902:f605:b0:2ae:4fc8:3f5d with SMTP id d9443c01a7336-2ae4fc8413bmr60047075ad.48.1772518434943; Mon, 02 Mar 2026 22:13:54 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:54 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:13:02 +0000 Subject: [PATCH 10/12] 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: <20260303-send-upstream-v1-10-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=5475; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=2NiZAoDC3alIu0louVywwr639R2HWq7OVqahJ+UY2qs=; b=xTK6qEpzG/F1jIuNi2IahxlTcrTi8zoxhftG8itoIwOTbVRnoTrMkT/BMzRjaNtHEQhGmbviq M3CBY14dXgSDE+8gnxfC1va3/UGPdCQCTCkJ+y+nSwaRTGTN4SEpWs3 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 | 107 +++++++++++++++++++++++++++++++++= ++++ drivers/hid/spi-hid/spi-hid-of.c | 1 + drivers/hid/spi-hid/spi-hid.h | 1 + 4 files changed, 110 insertions(+) diff --git a/drivers/hid/spi-hid/spi-hid-acpi.c b/drivers/hid/spi-hid/spi-h= id-acpi.c index 612e74fe72f9..2c1e4de99fea 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 ACPI_PTR(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 02beb209a92d..797ba99394f9 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -236,6 +238,81 @@ static const char *spi_hid_power_mode_string(enum hids= pi_power_state power_state } } =20 +static void 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; + + if (shid->hid) { + error =3D hid_driver_suspend(shid->hid, PMSG_SUSPEND); + if (error) { + dev_err(dev, "%s failed to suspend hid driver: %d", + __func__, error); + return; + } + } + + disable_irq(shid->spi->irq); + + clear_bit(SPI_HID_READY, &shid->flags); + + 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.", __func__); + shid->regulator_error_count++; + shid->regulator_last_error =3D error; + return; + } + + shid->power_state =3D HIDSPI_OFF; + } +} + +static void spi_hid_resume(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_ON) + return; + + enable_irq(shid->spi->irq); + + if (!device_may_wakeup(dev)) { + 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.", __func__); + shid->regulator_error_count++; + shid->regulator_last_error =3D error; + return; + } + shid->power_state =3D HIDSPI_ON; + + shid->ops->deassert_reset(shid->ops); + } + + if (shid->hid) { + error =3D hid_driver_reset_resume(shid->hid); + if (error) + dev_err(dev, "%s: failed to reset resume hid driver: %d.", + __func__, error); + } +} + static void spi_hid_stop_hid(struct spi_hid *shid) { struct hid_device *hid =3D shid->hid; @@ -1155,6 +1232,13 @@ int spi_hid_core_probe(struct spi_device *spi, struc= t spihid_ops *ops, dev_err(dev, "%s: unable to request threaded IRQ.", __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.", __func__); + return error; + } + } =20 error =3D shid->ops->power_up(shid->ops); if (error) { @@ -1186,6 +1270,29 @@ void spi_hid_core_remove(struct spi_device *spi) } 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); + + spi_hid_suspend(shid); + + return 0; +} + +static int spi_hid_core_pm_resume(struct device *dev) +{ + struct spi_hid *shid =3D dev_get_drvdata(dev); + + spi_hid_resume(shid); + + return 0; +} + +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 a20c8146230b..bc1d3c5a4dda 100644 --- a/drivers/hid/spi-hid/spi-hid-of.c +++ b/drivers/hid/spi-hid/spi-hid-of.c @@ -227,6 +227,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 of_match_ptr(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 1fdd45262647..5651c7fb706a 100644 --- a/drivers/hid/spi-hid/spi-hid.h +++ b/drivers/hid/spi-hid/spi-hid.h @@ -40,5 +40,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.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 2026 Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) (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 DD13D382F28 for ; Tue, 3 Mar 2026 06:13:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518440; cv=none; b=ENZDdiivxCYVek3eJN1/1Ff9FqrlKM6VuaBaQauSfYdsPn1ZVK/S+EkdBPR3TofV/dSWOHtYlM283n/NyjtifunQmL/8ypFTe41ZbR20bW25EA4SPWSwFKJ8fGGtoOK/m5Km3Ec5L9IB25oZoTewuB40AVMkq6OE+3YJIKXXfZg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518440; c=relaxed/simple; bh=9VBbxDeCBOwEkQqdAXnbqlSFol8sG1Sk8yHeuXnwVc8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=k+XfOu7+7PiiQJhjcGUWX3L4+PFco8H7gNsvXp6HghBSys6W9IhLb2VFlwOJ1b/VgrNmaS8xygS1fBCHvqTEYXCsvH6bhSNzAFFyS52/Y1U8Euhw43lv+PumYxFawR9A4xF2thkm8IkKIKIQaAg6M5VZg2ymCN/jmT3MGVtJulY= 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=ZgRE2rPw; arc=none smtp.client-ip=209.85.214.180 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="ZgRE2rPw" Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-2ad9a9be502so36331375ad.0 for ; Mon, 02 Mar 2026 22:13:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518436; x=1773123236; 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=7CYLqWK3CZS7x6XnoVJUokrAtWYl16nmLP+jWdmHpw8=; b=ZgRE2rPwuTae0hGxG2C2ywFa0J02lqELncNOzg8b0JII/sW4eF9RT9I4Rco4iTmql3 6UV0+Ob991A3hskKPug+BNWNGkgU94gl2vUqdr6OMgI7winH5zhFH5jaWUYuZEqLAx+L dJdjEVad7WChPSwtooyZD3WSz9thGmw72XD/I= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518436; x=1773123236; 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=7CYLqWK3CZS7x6XnoVJUokrAtWYl16nmLP+jWdmHpw8=; b=jaE8kER/Gv3kCgJs54K3B4BTd+TR9ZNGnr8QVAuXygm+BVhpImY2f5o6+zbJ2fAHMb bz5sjia865CiOsp9sPG3s8OpDsKtz5Q91UEOl0ErVmofQ1Y7Pib0sHZeYfAZ+X9pEqOJ GKgpXlJmSy+oBi5FZUkzJ//M9qGuHeBRylvehSyJfzIy6t0EYXmJOcbBLpxOCaJGPrxs +hmLxCKZyNbAuReBjNCZEToNXixRswEvR6/rG+2RfBn9NbPRTmZZX9Hk94VtT5WwRodR RSRN+2MsJ2tEUEE9f0qvBhr6zy1pCmxtiT4PmwstHrW/92a1fO4A4b9cf8A2ZedplpV9 tzlw== X-Forwarded-Encrypted: i=1; AJvYcCXzuvQb62Eh2EYsdnjB/QQWuDb6GxeCVxcYNB6VxrVqnDFS4vlLPPVbFpfarHpHcmB8+fZZBS2Adl1kMZ4=@vger.kernel.org X-Gm-Message-State: AOJu0YzJzOsm9k497MOvtX/UIcM040XAnvRv5lY1ZOs9r3GK9UO+ESiO dfxzNxTPqzX9WPqJEbWKCLgFp4LBr6wz2j6WnsXz2bdJ0v3BG21/jwMcHO3MMbQf2A== X-Gm-Gg: ATEYQzxpqbNRBdA3BcRymKX4tZ9CL+2EkHakX6sK6v6gwa/F/lQwstT3CDcDr7eHgpf b8MPkWoXfQYST9KegeZQ5isNAzu9PZhssMj6Di3iu27nr137GFigV1LkAIXifXHOnbts3vFLLEQ sl/NjfFZ4rkYe6N8kBMtgMCMTI6jmgxoHP8p/0PGXkW6DdYjj7bAHcWW8RclXTQRf1UkoaMjuNg zkcUCyv0IzRlltgg0bn1QK4IWS8CEp535nGGgrz4kk20EVdYw02TMlOv3HleugkboAXHnBtVeEj jwdyKyEOqqfK/oJ0HYjUDitF0nFP+AUZwNvUBNcpYc0TeFLLoccXdof/zICrV/KXnEHIZ12+4q6 jjWAxqaICf94hTKIrpnvLjYWGZkYMLqjMY67n1I01m5l6N7XcTU+/zdYDUPk1TFD/6jdOS8mMNm yLEbgEasByOo+M0DwuzYOCTNZ/MQ9mxpTPbWOcBCUBRkDVD+gkMdtb+PztYMOVVOr2YC8JnyxL2 UMMwZDrj+WBfPUJz4nfrpG5acpE8mgZ2g== X-Received: by 2002:a17:902:f541:b0:2ab:344e:1413 with SMTP id d9443c01a7336-2ae2e46c080mr134889525ad.34.1772518436078; Mon, 02 Mar 2026 22:13:56 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:55 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:13:03 +0000 Subject: [PATCH 11/12] 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: <20260303-send-upstream-v1-11-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=10421; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=9VBbxDeCBOwEkQqdAXnbqlSFol8sG1Sk8yHeuXnwVc8=; b=jZHqmh4vIgwl89Ot2YigAOzVXGc+PbFSOe3mkaSX/v16i2WnGSlJIczYjrPpNxw149oHIEuXm 70WzKA8HtINA03O6OgK0QyBvu2GFsn6i7kFxc3m1WQDPblsHJcn13Kf 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 | 199 +++++++++++++++++++++++++++++----= ---- drivers/hid/spi-hid/spi-hid-core.h | 7 ++ 2 files changed, 163 insertions(+), 43 deletions(-) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 797ba99394f9..893a0d4642d2 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -238,21 +238,21 @@ static const char *spi_hid_power_mode_string(enum hid= spi_power_state power_state } } =20 -static void spi_hid_suspend(struct spi_hid *shid) +static int spi_hid_suspend(struct spi_hid *shid) { int error; struct device *dev =3D &shid->spi->dev; =20 guard(mutex)(&shid->power_lock); if (shid->power_state =3D=3D HIDSPI_OFF) - return; + return 0; =20 if (shid->hid) { error =3D hid_driver_suspend(shid->hid, PMSG_SUSPEND); if (error) { dev_err(dev, "%s failed to suspend hid driver: %d", __func__, error); - return; + return error; } } =20 @@ -270,21 +270,22 @@ static void spi_hid_suspend(struct spi_hid *shid) dev_err(dev, "%s: could not power down.", __func__); shid->regulator_error_count++; shid->regulator_last_error =3D error; - return; + return error; } =20 shid->power_state =3D HIDSPI_OFF; } + return 0; } =20 -static void spi_hid_resume(struct spi_hid *shid) +static int spi_hid_resume(struct spi_hid *shid) { int error; struct device *dev =3D &shid->spi->dev; =20 guard(mutex)(&shid->power_lock); if (shid->power_state =3D=3D HIDSPI_ON) - return; + return 0; =20 enable_irq(shid->spi->irq); =20 @@ -298,7 +299,7 @@ static void spi_hid_resume(struct spi_hid *shid) dev_err(dev, "%s: could not power up.", __func__); shid->regulator_error_count++; shid->regulator_last_error =3D error; - return; + return error; } shid->power_state =3D HIDSPI_ON; =20 @@ -307,10 +308,13 @@ static void spi_hid_resume(struct spi_hid *shid) =20 if (shid->hid) { error =3D hid_driver_reset_resume(shid->hid); - if (error) + if (error) { dev_err(dev, "%s: failed to reset resume hid driver: %d.", __func__, error); + return error; + } } + return 0; } =20 static void spi_hid_stop_hid(struct spi_hid *shid) @@ -1171,6 +1175,132 @@ 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) request IRQ + * 3) power up the device + * 4) 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 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.", __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.", __func__); + return error; + } + } + + error =3D shid->ops->power_up(shid->ops); + if (error) { + dev_err(dev, "%s: could not power up.", __func__); + shid->regulator_error_count++; + shid->regulator_last_error =3D error; + return error; + } + + shid->ops->deassert_reset(shid->ops); + + 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", error); + else + WRITE_ONCE(shid->panel_follower_work_finished, true); + + /* + * The work APIs provide a number of memory ordering guarantees + * including one that says that memory writes before schedule_work() + * are always visible to the work function, but they don't appear to + * guarantee that a write that happened in the work is visible after + * cancel_work_sync(). We'll add a write memory barrier here to match + * with spi_hid_panel_unpreparing() to ensure that our write to + * panel_follower_work_finished is visible there. + */ + smp_wmb(); +} + +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); + + /* Match with shid_core_panel_follower_work() */ + smp_rmb(); + 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) { @@ -1190,6 +1320,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 @@ -1202,6 +1333,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 @@ -1212,42 +1344,18 @@ int spi_hid_core_probe(struct spi_device *spi, stru= ct 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, dev_name(&spi->dev), shid); - if (error) { - dev_err(dev, "%s: unable to request threaded IRQ.", __func__); - return error; - } - if (device_may_wakeup(dev)) { - error =3D dev_pm_set_wake_irq(dev, spi->irq); + if (shid->is_panel_follower) { + error =3D spi_hid_register_panel_follower(shid); if (error) { - dev_err(dev, "%s: failed to set wake IRQ.", __func__); + dev_err(dev, "%s: could not add panel follower.", __func__); return error; } + } else { + error =3D spi_hid_dev_init(shid); + if (error) + return error; } =20 - error =3D shid->ops->power_up(shid->ops); - if (error) { - dev_err(dev, "%s: could not power up.", __func__); - return error; - } - - shid->ops->deassert_reset(shid->ops); - dev_dbg(dev, "%s: d3 -> %s.", __func__, spi_hid_power_mode_string(shid->power_state)); =20 @@ -1261,6 +1369,9 @@ void spi_hid_core_remove(struct spi_device *spi) struct device *dev =3D &spi->dev; int error; =20 + if (shid->is_panel_follower) + drm_panel_remove_follower(&shid->panel_follower); + spi_hid_stop_hid(shid); =20 shid->ops->assert_reset(shid->ops); @@ -1274,18 +1385,20 @@ static int spi_hid_core_pm_suspend(struct device *d= ev) { struct spi_hid *shid =3D dev_get_drvdata(dev); =20 - spi_hid_suspend(shid); + if (shid->is_panel_follower) + return 0; =20 - return 0; + return spi_hid_suspend(shid); } =20 static int spi_hid_core_pm_resume(struct device *dev) { struct spi_hid *shid =3D dev_get_drvdata(dev); =20 - spi_hid_resume(shid); + if (shid->is_panel_follower) + return 0; =20 - return 0; + return spi_hid_resume(shid); } =20 const struct dev_pm_ops spi_hid_core_pm =3D { diff --git a/drivers/hid/spi-hid/spi-hid-core.h b/drivers/hid/spi-hid/spi-h= id-core.h index 2bfdfbe6d7fc..88e9020d37aa 100644 --- a/drivers/hid/spi-hid/spi-hid-core.h +++ b/drivers/hid/spi-hid/spi-hid-core.h @@ -7,6 +7,8 @@ #include #include =20 +#include + /* Protocol message size constants */ #define SPI_HID_READ_APPROVAL_LEN 5 #define SPI_HID_OUTPUT_HEADER_LEN 8 @@ -53,6 +55,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 @@ -63,6 +69,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 make sure one output transaction at a time. */ struct mutex output_lock; --=20 2.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 15:05:53 2026 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (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 F0390382F39 for ; Tue, 3 Mar 2026 06:13:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518441; cv=none; b=aokGys6bx5ZgL3nfRL8zS4AbenR4xfr37gmTL5AiEpUct1fgAu+4ybz6y+dPosXYj3TEFVAEJu7/Hq+vVrZxdhVd7C4MOzJqKih0si2BzQSBU5WZlOPzcQwZuMXhaAKZpJXlxZ8mKa5dhLQa9wJoT6eXXLEY6JWYJ0Lxsftx7zk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772518441; c=relaxed/simple; bh=RuF4EBelwWjX4MVB7wRAKwjHLIyH356VR5EZd3QmjHw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ApVYojkm99SJYFfSkU1w0+pXOIIFRuZ3bbmPt0M5gE4ZAZKfJ0LPKdI1gHW9b+5Acfsn8a3oL8Mr6qXoHrQs14p63WpFPlGu2pCJHL/wxQIiBz7DW6Ue94MFcBzEGOYLI4B7hMNaoyGBI0Gx1dO+YWi5vRNPFeVH/9YpA5SYpwI= 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=e+9q8H9Z; arc=none smtp.client-ip=209.85.214.181 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="e+9q8H9Z" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-2ae46fc8ec1so14459585ad.3 for ; Mon, 02 Mar 2026 22:13:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1772518437; x=1773123237; 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=iTYYpNGe7aJcDLJJdiqjkV8VqGfZqV4Qv94rVv/ZSwU=; b=e+9q8H9Z2qaUbup2uhMXSUAKhFg4kP8EaZTJCDbEtlMY+sjG2idWUzGG1GalnklDbq raUw8Zf/h+acrwI14KJAwQvza07p9H78YbNaSXlXXCEIVKfAC/pgoJmTo9aVbwrhqhh7 3BFB1AizMh3VTRYKE5TM+u5QAbuW000RDS1hM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772518437; x=1773123237; 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=iTYYpNGe7aJcDLJJdiqjkV8VqGfZqV4Qv94rVv/ZSwU=; b=t3R8L1x/7gH1qQtJn8FjlH9pXKGuWAOlmY+LMGC1dzCIloJVXZMb8RGZsYGqxCul9E iFoc7yXxezrA/DDep64ggFQQGlB1GCFaNeSYUBObQFrhQirWt64Q29f0mNCd+/COlLUe LQSmVyHjOua3aGtRLdk4mTG/Pbdrl4KCfX/bNW2BRWI+fW2Z6N9dvJUpFr1iP4u2FF2P dZL6Fopsrp3Hj6/1V1KkjXt0YLEeSv+SvQkk77K+Yh8ifMnhPB01oFt7f4wp9Be2bSKl xM/KXVt0lfEsXMAljhpKV677by5offIIn+uLHmTi/W8o20KK5Rxe6jA/hpiPF3o61opK HLGg== X-Forwarded-Encrypted: i=1; AJvYcCXlWMrUPIeOdlQXVf496mt1AVMNuhfBq4jBfUZvdHvepA58VPsA++mZM3BQ7leloKK8S8htOH/WbAt2zVE=@vger.kernel.org X-Gm-Message-State: AOJu0YwZCTrHR2NQGFI9QAD3srLJzTO8pLAMe/Dre/rBDWOJYf7/e9KK L8ZuJdPa4CZye85cCOpUZuYjLqrdoVnRxmlYZ46YnK0+pWKPWSob0TsQ/fPDX8kESA== X-Gm-Gg: ATEYQzwlCEz//P/JOoe85pQdVr1ILCqoNTfd+luizWu4zzQNiLYukN+SRmzejIpRXdq X6EgOhXd6MDre+wVkTbw+ukZquIJC2ZDn7aUf3jrNjXfeCTWVf+u+ivkSjKSKRPOQZj0xGtkmTJ DEBFAWuU3wZb0TVLN6X4LoXcvRb8XnbBX0wMrqgy2prQbs0G57xUvBfoaEs+V7EvPHxIcgFh81x l0rQ0Ipf9iQNonzTPpqB1zg/0xOy9DVBcF9iM/QRBEduE7nIMvSwgJY6gwAzv7amMHKaJYBSUE/ OastG1mkBFFfGeVMCMJaeNwe0WdQjfmCj7pxWOI9JnFX5AnWOIG6TnGoQxkQo5g8FTub280CCd3 5DQp+bljK0nzdVd2c3hKzfaTKQaHYF2CmfZ6nRCdMavrYoM4K8OkhxZ3CN/wCCElaR2uA08GqIl vzrFwnpSpAS05PbhRda0hXEjM1aUhBS/HSyeQtOSx4gKHsBQfujSX6nPeWG7ePWimUdmOPkjM9I kzeVcwytBHv8b2tT733V7S50T0dfSmcKw== X-Received: by 2002:a17:903:b0d:b0:2ae:5598:1db3 with SMTP id d9443c01a7336-2ae55982a32mr46227035ad.53.1772518437154; Mon, 02 Mar 2026 22:13:57 -0800 (PST) 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-2adfb6fe4f3sm152639735ad.91.2026.03.02.22.13.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2026 22:13:56 -0800 (PST) From: Jingyuan Liang Date: Tue, 03 Mar 2026 06:13:04 +0000 Subject: [PATCH 12/12] HID: spi-hid: add quirkis to support mode switch for Ilitek touch 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: <20260303-send-upstream-v1-12-1515ba218f3d@chromium.org> References: <20260303-send-upstream-v1-0-1515ba218f3d@chromium.org> In-Reply-To: <20260303-send-upstream-v1-0-1515ba218f3d@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, Jingyuan Liang X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772518424; l=7060; i=jingyliang@chromium.org; s=20260213; h=from:subject:message-id; bh=RuF4EBelwWjX4MVB7wRAKwjHLIyH356VR5EZd3QmjHw=; b=yqf3lINoDU5izp7XLxBncUEfy96hR5Uvs/pFDvxmmcHDmeFoqVujMRkAE6X/GypO/8SUjW/3o lT326CxlFyPDmz4HEgVb+PXKKZ5GIdZgPXaG1pCF1ZDP2sH2scC1goy X-Developer-Key: i=jingyliang@chromium.org; a=ed25519; pk=VTYSdqslTtYOjWWoIGgYoWupGWqNSidrggReKMgfPo4= Add quirks to support mode switch among Ilitek normal, debug and test mode and allow delay before send output reports. Add a shared variable to configure response timeout value for Ilitek touch controllers. Signed-off-by: Jingyuan Liang --- drivers/hid/spi-hid/spi-hid-core.c | 84 ++++++++++++++++++++++++++++++++++= +++- drivers/hid/spi-hid/spi-hid-core.h | 4 ++ drivers/hid/spi-hid/spi-hid.h | 6 +++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-h= id-core.c index 893a0d4642d2..736e51f10cfc 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -22,6 +22,7 @@ =20 #include #include +#include #include #include #include @@ -45,9 +46,14 @@ #include #include =20 +#include "../hid-ids.h" #include "spi-hid.h" #include "spi-hid-core.h" =20 +/* quirks to control the device */ +#define SPI_HID_QUIRK_MODE_SWITCH BIT(0) +#define SPI_HID_QUIRK_READ_DELAY BIT(1) + /* Protocol constants */ #define SPI_HID_READ_APPROVAL_CONSTANT 0xff #define SPI_HID_INPUT_HEADER_SYNC_BYTE 0x5a @@ -86,6 +92,16 @@ #define SPI_HID_CREATE_DEVICE 4 #define SPI_HID_ERROR 5 =20 +static const struct spi_hid_quirks { + __u16 idVendor; + __u16 idProduct; + __u32 quirks; +} spi_hid_quirks[] =3D { + { USB_VENDOR_ID_ILITEK, HID_ANY_ID, + SPI_HID_QUIRK_MODE_SWITCH | SPI_HID_QUIRK_READ_DELAY }, + { 0, 0 } +}; + /* Processed data from input report header */ struct spi_hid_input_header { u8 version; @@ -112,6 +128,27 @@ struct spi_hid_output_report { =20 static struct hid_ll_driver spi_hid_ll_driver; =20 +/** + * spi_hid_lookup_quirk: return any quirks associated with a SPI HID device + * @idVendor: the 16-bit vendor ID + * @idProduct: the 16-bit product ID + * + * Returns: a u32 quirks value. + */ +static u32 spi_hid_lookup_quirk(const u16 idVendor, const u16 idProduct) +{ + u32 quirks =3D 0; + int n; + + for (n =3D 0; spi_hid_quirks[n].idVendor; n++) + if (spi_hid_quirks[n].idVendor =3D=3D idVendor && + (spi_hid_quirks[n].idProduct =3D=3D (__u16)HID_ANY_ID || + spi_hid_quirks[n].idProduct =3D=3D idProduct)) + quirks =3D spi_hid_quirks[n].quirks; + + return quirks; +} + static void spi_hid_populate_read_approvals(const struct spi_hid_conf *con= f, u8 *header_buf, u8 *body_buf) { @@ -382,6 +419,9 @@ static int spi_hid_send_output_report(struct spi_hid *s= hid, u8 padding; int error; =20 + if (shid->quirks & SPI_HID_QUIRK_READ_DELAY) + usleep_range(2000, 2100); + guard(mutex)(&shid->output_lock); if (report->content_length > shid->desc.max_output_length) { dev_err(dev, "Output report too big, content_length 0x%x.", @@ -406,18 +446,38 @@ static int spi_hid_send_output_report(struct spi_hid = *shid, return error; } =20 +static const u32 spi_hid_get_timeout(struct spi_hid *shid) +{ + struct device *dev =3D &shid->spi->dev; + u32 timeout; + + timeout =3D READ_ONCE(shid->ops->response_timeout_ms); + + if (timeout < SPI_HID_RESP_TIMEOUT || timeout > 10000) { + dev_dbg(dev, "Response timeout is out of range, using default %d", + SPI_HID_RESP_TIMEOUT); + timeout =3D SPI_HID_RESP_TIMEOUT; + } + + return timeout; +} + static int spi_hid_sync_request(struct spi_hid *shid, struct spi_hid_output_report *report) { struct device *dev =3D &shid->spi->dev; + u32 timeout =3D SPI_HID_RESP_TIMEOUT; int error; =20 error =3D spi_hid_send_output_report(shid, report); if (error) return error; =20 + if (shid->quirks & SPI_HID_QUIRK_MODE_SWITCH) + timeout =3D spi_hid_get_timeout(shid); + error =3D wait_for_completion_interruptible_timeout(&shid->output_done, - msecs_to_jiffies(SPI_HID_RESP_TIMEOUT)); + msecs_to_jiffies(timeout)); if (error =3D=3D 0) { dev_err(dev, "Response timed out."); return -ETIMEDOUT; @@ -561,6 +621,8 @@ static int spi_hid_create_device(struct spi_hid *shid) hid->vendor =3D shid->desc.vendor_id; hid->product =3D shid->desc.product_id; =20 + shid->quirks =3D spi_hid_lookup_quirk(hid->vendor, hid->product); + snprintf(hid->name, sizeof(hid->name), "spi %04X:%04X", hid->vendor, hid->product); strscpy(hid->phys, dev_name(&shid->spi->dev), sizeof(hid->phys)); @@ -836,6 +898,24 @@ static irqreturn_t spi_hid_dev_irq(int irq, void *_shi= d) goto out; } =20 + if (shid->quirks & SPI_HID_QUIRK_MODE_SWITCH) { + /* + * Update reset_pending on mode transitions inferred from + * response timeout (entering/exiting a mode). + */ + u32 timeout =3D spi_hid_get_timeout(shid); + bool mode_enabled =3D timeout > SPI_HID_RESP_TIMEOUT; + + if (mode_enabled !=3D shid->prev_mode_enabled) { + if (mode_enabled) + set_bit(SPI_HID_RESET_PENDING, &shid->flags); + else + clear_bit(SPI_HID_RESET_PENDING, &shid->flags); + } + + shid->prev_mode_enabled =3D mode_enabled; + } + if (shid->input_message.status < 0) { dev_warn(dev, "Error reading header: %d.", shid->input_message.status); @@ -1190,6 +1270,8 @@ static int spi_hid_dev_init(struct spi_hid *shid) struct device *dev =3D &spi->dev; int error; =20 + shid->ops->custom_init(shid->ops); + shid->ops->assert_reset(shid->ops); =20 shid->ops->sleep_minimal_reset_delay(shid->ops); diff --git a/drivers/hid/spi-hid/spi-hid-core.h b/drivers/hid/spi-hid/spi-h= id-core.h index 88e9020d37aa..8441dbad95d4 100644 --- a/drivers/hid/spi-hid/spi-hid-core.h +++ b/drivers/hid/spi-hid/spi-hid-core.h @@ -62,6 +62,10 @@ struct spi_hid { u16 response_length; u16 bufsize; =20 + bool prev_mode_enabled; /* Previous device mode tracked for SPI_HID_QUIRK= _MODE_SWITCH. */ + + unsigned long quirks; /* Various quirks. */ + enum hidspi_power_state power_state; =20 u8 reset_attempts; /* The number of reset attempts. */ diff --git a/drivers/hid/spi-hid/spi-hid.h b/drivers/hid/spi-hid/spi-hid.h index 5651c7fb706a..3c0369bdb4ab 100644 --- a/drivers/hid/spi-hid/spi-hid.h +++ b/drivers/hid/spi-hid/spi-hid.h @@ -25,6 +25,9 @@ struct spi_hid_conf { * @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 + * @custom_init: customized device init + * @response_timeout_ms: output report response timeout in ms */ struct spihid_ops { int (*power_up)(struct spihid_ops *ops); @@ -32,6 +35,9 @@ struct spihid_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 (*custom_init)(struct spihid_ops *ops); + + u32 response_timeout_ms; }; =20 int spi_hid_core_probe(struct spi_device *spi, struct spihid_ops *ops, --=20 2.53.0.473.g4a7958ca14-goog