From nobody Mon Jun 29 22:18:23 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E46B9C433EF for ; Wed, 2 Feb 2022 04:50:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244623AbiBBEuO (ORCPT ); Tue, 1 Feb 2022 23:50:14 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52632 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244602AbiBBEuM (ORCPT ); Tue, 1 Feb 2022 23:50:12 -0500 Received: from mail-pj1-x1035.google.com (mail-pj1-x1035.google.com [IPv6:2607:f8b0:4864:20::1035]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A2372C061714 for ; Tue, 1 Feb 2022 20:50:12 -0800 (PST) Received: by mail-pj1-x1035.google.com with SMTP id o64so19196765pjo.2 for ; Tue, 01 Feb 2022 20:50:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=xVhMtqHlu6JCIou6HFk3Q3pHLaJU2AATV0pA57Jnst4=; b=n5DIelVdYVyO/sj67oGCUOVJ1jS5AQTKPdEKv37fKxOp+RLjQgpHcseTn5phAoNs06 ZXsTuGhRdYkAHMJQAmNqdJhf/YEKPUaEq6m3jr7DJSBw4moy4pDp/K7CX4W9AfdCL3op i9UeQV4Jlflj2o3I5nQ+HpTz7nzALYLMKI09Q= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=xVhMtqHlu6JCIou6HFk3Q3pHLaJU2AATV0pA57Jnst4=; b=rZeXDJj4eo/eNzdRlCdd+W15h8d87paxkW6vax16B06IoyzbdbnpQppQNKxVNR+0XB gIwq/YA58t07XeoD3Oy60jCJogze7gp1S4w2cI8oKpumcDLvRbpLbWPZ6RLKnneLFHQy 4CJdBAXE6yWD84ZSm9vFxXeJ6d7HkmY5egMkV5I9L22JwQrTEdUaRQvJ/8SgWLunB8ic b9lV84jrf5a6sRr8KZXtKeDd1BrDmtGVvv2+s5yetIusiT9wP4B+tfK3tL88LAlN+qim gzz1tyZVlOPk/ldw+vaX9QNB7CQ8u86YXLHBuDCwxY1OOT2s2ZWvkl78TIzc7fgzqb0J k3zA== X-Gm-Message-State: AOAM531sm0Vo9fwnT1hiKgTnE2ZOYaUXm3SAALpn7zyebfAJysfMYlkI 2D1ob9k7sBJ4qXZEZt/39YW4RJYMPzDtYw== X-Google-Smtp-Source: ABdhPJy9sveQ++Xlglmd5XhpO3noAMeCB3+NreACzmlZlZUE4kpkrxDvnfmXHtZe40Kwcot1in72xw== X-Received: by 2002:a17:90a:fe2:: with SMTP id 89mr6188943pjz.162.1643777411946; Tue, 01 Feb 2022 20:50:11 -0800 (PST) Received: from localhost ([2401:fa00:9:14:97af:7793:b97d:448e]) by smtp.gmail.com with UTF8SMTPSA id a14sm24981865pfv.212.2022.02.01.20.50.09 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 01 Feb 2022 20:50:11 -0800 (PST) From: =?UTF-8?q?Sami=20Ky=C3=B6stil=C3=A4?= To: LKML Cc: dtor@chromium.org, gregkh@linuxfoundation.org, arnd@arndb.de, evanbenn@chromium.org, =?UTF-8?q?Sami=20Ky=C3=B6stil=C3=A4?= Subject: [PATCH v2 1/1] drivers/misc: add a driver for HPS Date: Wed, 2 Feb 2022 15:49:37 +1100 Message-Id: <20220202044937.3187603-2-skyostil@chromium.org> X-Mailer: git-send-email 2.35.0.rc2.247.g8bbb082509-goog In-Reply-To: <20220202044937.3187603-1-skyostil@chromium.org> References: <20220202044937.3187603-1-skyostil@chromium.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch introduces a driver for the ChromeOS screen privacy sensor (aka. HPS). The driver supports a sensor connected to the I2C bus and identified as "GOOG0020" in the ACPI tables. When loaded, the driver exports the sensor to userspace through a character device. This device only supports power management, i.e., communication with the sensor must be done through regular I2C transmissions from userspace. Power management is implemented by enabling the respective power GPIO while at least one userspace process holds an open fd on the character device. By default, the device is powered down if there are no active clients. Note that the driver makes no effort to preserve the state of the sensor between power down and power up events. Userspace is responsible for reinitializing any needed state once power has been restored. The device firmware, I2C protocol and other documentation is available at https://chromium.googlesource.com/chromiumos/platform/hps-firmware. Signed-off-by: Sami Ky=C3=B6stil=C3=A4 --- Changes in v2: - Removed custom ioctl interface. - Reworked to use miscdev. MAINTAINERS | 6 ++ drivers/misc/Kconfig | 10 +++ drivers/misc/Makefile | 1 + drivers/misc/hps-i2c.c | 184 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 201 insertions(+) create mode 100644 drivers/misc/hps-i2c.c diff --git a/MAINTAINERS b/MAINTAINERS index f41088418aae..6c164ed84f60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8801,6 +8801,12 @@ S: Maintained W: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi F: fs/hpfs/ =20 +HPS (ChromeOS snooping protection sensor) DRIVER +M: Sami Ky=C3=B6stil=C3=A4 +R: Evan Benn +S: Maintained +F: drivers/misc/hps-i2c.c + HSI SUBSYSTEM M: Sebastian Reichel S: Maintained diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0f5a49fc7c9e..44fcf60a67aa 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -244,6 +244,16 @@ config HP_ILO To compile this driver as a module, choose M here: the module will be called hpilo. =20 +config HPS_I2C + tristate "ChromeOS HPS device" + depends on HID && I2C && PM + help + Say Y here if you want to enable support for the ChromeOS + anti-snooping sensor (HPS), attached via I2C. The driver supports a + sensor connected to the I2C bus and exposes it as a character device. + To save power, the sensor is automatically powered down when no + clients are accessing it. + config QCOM_COINCELL tristate "Qualcomm coincell charger support" depends on MFD_SPMI_PMIC || COMPILE_TEST diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a086197af544..162a7d530dab 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SGI_GRU) +=3D sgi-gru/ obj-$(CONFIG_CS5535_MFGPT) +=3D cs5535-mfgpt.o obj-$(CONFIG_GEHC_ACHC) +=3D gehc-achc.o obj-$(CONFIG_HP_ILO) +=3D hpilo.o +obj-$(CONFIG_HPS_I2C) +=3D hps-i2c.o obj-$(CONFIG_APDS9802ALS) +=3D apds9802als.o obj-$(CONFIG_ISL29003) +=3D isl29003.o obj-$(CONFIG_ISL29020) +=3D isl29020.o diff --git a/drivers/misc/hps-i2c.c b/drivers/misc/hps-i2c.c new file mode 100644 index 000000000000..9071c9324fa7 --- /dev/null +++ b/drivers/misc/hps-i2c.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the ChromeOS screen privacy sensor (HPS), attached via I2C. + * + * The driver exposes HPS as a character device, although currently no rea= d or + * write operations are supported. Instead, the driver only controls the p= ower + * state of the sensor, keeping it on only while userspace holds an open f= ile + * descriptor to the HPS device. + * + * Copyright 2022 Google LLC. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define HPS_ACPI_ID "GOOG0020" + +struct hps_drvdata { + struct i2c_client *client; + struct miscdevice misc_device; + struct gpio_desc *enable_gpio; +}; + +static void hps_set_power(struct hps_drvdata *hps, bool state) +{ + if (!IS_ERR_OR_NULL(hps->enable_gpio)) + gpiod_set_value_cansleep(hps->enable_gpio, state); +} + +static void hps_unload(void *drv_data) +{ + struct hps_drvdata *hps =3D drv_data; + + hps_set_power(hps, true); +} + +static int hps_open(struct inode *inode, struct file *file) +{ + struct hps_drvdata *hps =3D container_of(file->private_data, + struct hps_drvdata, misc_device); + struct device *dev =3D &hps->client->dev; + int ret; + + ret =3D pm_runtime_get_sync(dev); + if (ret < 0) + goto pm_get_fail; + return 0; + +pm_get_fail: + pm_runtime_put(dev); + pm_runtime_disable(dev); + return ret; +} + +static int hps_release(struct inode *inode, struct file *file) +{ + struct hps_drvdata *hps =3D container_of(file->private_data, + struct hps_drvdata, misc_device); + struct device *dev =3D &hps->client->dev; + int ret; + + ret =3D pm_runtime_put(dev); + if (ret < 0) + goto pm_put_fail; + return 0; + +pm_put_fail: + pm_runtime_disable(dev); + return ret; +} + +const struct file_operations hps_fops =3D { + .owner =3D THIS_MODULE, + .open =3D hps_open, + .release =3D hps_release, +}; + +static int hps_i2c_probe(struct i2c_client *client) +{ + struct hps_drvdata *hps; + int ret =3D 0; + + hps =3D devm_kzalloc(&client->dev, sizeof(*hps), GFP_KERNEL); + if (!hps) + return -ENOMEM; + + memset(&hps->misc_device, 0, sizeof(hps->misc_device)); + hps->misc_device.parent =3D &client->dev; + hps->misc_device.minor =3D MISC_DYNAMIC_MINOR; + hps->misc_device.name =3D "hps"; + hps->misc_device.fops =3D &hps_fops; + + i2c_set_clientdata(client, hps); + hps->client =3D client; + hps->enable_gpio =3D devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_HIG= H); + if (IS_ERR(hps->enable_gpio)) { + ret =3D PTR_ERR(hps->enable_gpio); + dev_err(&client->dev, "failed to get enable gpio: %d\n", ret); + return ret; + } + + ret =3D devm_add_action(&client->dev, &hps_unload, hps); + if (ret) { + dev_err(&client->dev, + "failed to install unload action: %d\n", ret); + return ret; + } + + ret =3D misc_register(&hps->misc_device); + if (ret) { + dev_err(&client->dev, "failed to initialize misc device: %d\n", ret); + return ret; + } + + hps_set_power(hps, false); + pm_runtime_enable(&client->dev); + return ret; +} + +static int hps_i2c_remove(struct i2c_client *client) +{ + struct hps_drvdata *hps =3D i2c_get_clientdata(client); + + pm_runtime_disable(&client->dev); + misc_deregister(&hps->misc_device); + return 0; +} + +static int hps_suspend(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct hps_drvdata *hps =3D i2c_get_clientdata(client); + + hps_set_power(hps, false); + return 0; +} + +static int hps_resume(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct hps_drvdata *hps =3D i2c_get_clientdata(client); + + hps_set_power(hps, true); + return 0; +} +static UNIVERSAL_DEV_PM_OPS(hps_pm_ops, hps_suspend, hps_resume, NULL); + +static const struct i2c_device_id hps_i2c_id[] =3D { + { "hps", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hps_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id hps_acpi_id[] =3D { + { HPS_ACPI_ID, 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hps_acpi_id); +#endif /* CONFIG_ACPI */ + +static struct i2c_driver hps_i2c_driver =3D { + .probe_new =3D hps_i2c_probe, + .remove =3D hps_i2c_remove, + .id_table =3D hps_i2c_id, + .driver =3D { + .name =3D "hps", + .pm =3D &hps_pm_ops, +#ifdef CONFIG_ACPI + .acpi_match_table =3D ACPI_PTR(hps_acpi_id), +#endif + }, +}; +module_i2c_driver(hps_i2c_driver); + +MODULE_ALIAS("acpi:" HPS_ACPI_ID); +MODULE_AUTHOR("Sami Ky=C3=B6stil=C3=A4 "); +MODULE_DESCRIPTION("Driver for ChromeOS HPS"); +MODULE_LICENSE("GPL"); --=20 2.35.0.rc2.247.g8bbb082509-goog