diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-12 10:51:55 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-12 10:51:55 -0800 |
commit | 0ec5a38bf8499f403f81cb81a0e3a60887d1993c (patch) | |
tree | 87c15546c5fb6204ad60d6d3d922dd50d4430144 /drivers/platform/chrome/cros_hps_i2c.c | |
parent | 7a76117f9fa52afcf244d4f1b8d4ce92f3e5ef99 (diff) | |
parent | 9a8aadcf0b459c1257b9477fd6402e1d5952ae07 (diff) | |
download | linux-stable-0ec5a38bf8499f403f81cb81a0e3a60887d1993c.tar.gz linux-stable-0ec5a38bf8499f403f81cb81a0e3a60887d1993c.tar.bz2 linux-stable-0ec5a38bf8499f403f81cb81a0e3a60887d1993c.zip |
Merge tag 'tag-chrome-platform-for-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux
Pull chrome platform updates from Tzung-Bi Shih:
"New drivers
- Driver for ChromeOS human presence sensor
Cleanups:
- Add missing property in dt-binding example.
- Update the availability of properties in dt-binding.
- Separate dt-binding for ChromeOS fingerprint sensor.
Improvements:
- Set PROBE_PREFER_ASYNCHRONOUS for some drivers for shortening boot time.
Fixes:
- Fix an use-after-free in cros_ec_typec.
And minor fixes and cleanups"
* tag 'tag-chrome-platform-for-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux:
platform/chrome: cros_ec_typec: zero out stale pointers
platform/chrome: cros_usbpd_notify: Fix error handling in cros_usbpd_notify_init()
platform/chrome: cros_ec: Convert to i2c's .probe_new()
platform/chrome: cros_ec_lpc: Force synchronous probe
platform/chrome: cros_ec_spi: Set PROBE_PREFER_ASYNCHRONOUS
platform/chrome: cros_ec_lightbar: Set PROBE_PREFER_ASYNCHRONOUS
platform/chrome: cros_ec_debugfs: Set PROBE_PREFER_ASYNCHRONOUS
platform/chrome: cros_ec_lpc: Mark PROBE_PREFER_ASYNCHRONOUS
platform/chrome: cros_ec_lpc: Move mec_init to device probe
platform/chrome: Use kstrtobool() instead of strtobool()
platform/chrome: cros_ec_lpc_mec: remove cros_ec_lpc_mec_destroy()
dt-bindings: cros-ec: Add ChromeOS fingerprint binding
dt-bindings: cros-ec: Reorganize and enforce property availability
platform/chrome: cros_hps_i2c: make remove callback return void
platform/chrome: add a driver for HPS
Diffstat (limited to 'drivers/platform/chrome/cros_hps_i2c.c')
-rw-r--r-- | drivers/platform/chrome/cros_hps_i2c.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/drivers/platform/chrome/cros_hps_i2c.c b/drivers/platform/chrome/cros_hps_i2c.c new file mode 100644 index 000000000000..62ccb1acb5de --- /dev/null +++ b/drivers/platform/chrome/cros_hps_i2c.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the ChromeOS human presence sensor (HPS), attached via I2C. + * + * The driver exposes HPS as a character device, although currently no read or + * write operations are supported. Instead, the driver only controls the power + * state of the sensor, keeping it on only while userspace holds an open file + * descriptor to the HPS device. + * + * Copyright 2022 Google LLC. + */ + +#include <linux/acpi.h> +#include <linux/fs.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> + +#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) +{ + gpiod_set_value_cansleep(hps->enable_gpio, state); +} + +static int hps_open(struct inode *inode, struct file *file) +{ + struct hps_drvdata *hps = container_of(file->private_data, + struct hps_drvdata, misc_device); + struct device *dev = &hps->client->dev; + + return pm_runtime_resume_and_get(dev); +} + +static int hps_release(struct inode *inode, struct file *file) +{ + struct hps_drvdata *hps = container_of(file->private_data, + struct hps_drvdata, misc_device); + struct device *dev = &hps->client->dev; + + return pm_runtime_put(dev); +} + +static const struct file_operations hps_fops = { + .owner = THIS_MODULE, + .open = hps_open, + .release = hps_release, +}; + +static int hps_i2c_probe(struct i2c_client *client) +{ + struct hps_drvdata *hps; + int ret; + + hps = devm_kzalloc(&client->dev, sizeof(*hps), GFP_KERNEL); + if (!hps) + return -ENOMEM; + + hps->misc_device.parent = &client->dev; + hps->misc_device.minor = MISC_DYNAMIC_MINOR; + hps->misc_device.name = "cros-hps"; + hps->misc_device.fops = &hps_fops; + + i2c_set_clientdata(client, hps); + hps->client = client; + + /* + * HPS is powered on from firmware before entering the kernel, so we + * acquire the line with GPIOD_OUT_HIGH here to preserve the existing + * state. The peripheral is powered off after successful probe below. + */ + hps->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(hps->enable_gpio)) { + ret = PTR_ERR(hps->enable_gpio); + dev_err(&client->dev, "failed to get enable gpio: %d\n", ret); + return ret; + } + + ret = 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 0; +} + +static void hps_i2c_remove(struct i2c_client *client) +{ + struct hps_drvdata *hps = i2c_get_clientdata(client); + + pm_runtime_disable(&client->dev); + misc_deregister(&hps->misc_device); + + /* + * Re-enable HPS, in order to return it to its default state + * (i.e. powered on). + */ + hps_set_power(hps, true); +} + +static int hps_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct hps_drvdata *hps = i2c_get_clientdata(client); + + hps_set_power(hps, false); + return 0; +} + +static int hps_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct hps_drvdata *hps = 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[] = { + { "cros-hps", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hps_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id hps_acpi_id[] = { + { HPS_ACPI_ID, 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hps_acpi_id); +#endif /* CONFIG_ACPI */ + +static struct i2c_driver hps_i2c_driver = { + .probe_new = hps_i2c_probe, + .remove = hps_i2c_remove, + .id_table = hps_i2c_id, + .driver = { + .name = "cros-hps", + .pm = &hps_pm_ops, + .acpi_match_table = ACPI_PTR(hps_acpi_id), + }, +}; +module_i2c_driver(hps_i2c_driver); + +MODULE_ALIAS("acpi:" HPS_ACPI_ID); +MODULE_AUTHOR("Sami Kyöstilä <skyostil@chromium.org>"); +MODULE_DESCRIPTION("Driver for ChromeOS HPS"); +MODULE_LICENSE("GPL"); |