brintos

brintos / linux-shallow public Read only

0
0
Text · 4.0 KiB · dd14957 Raw
161 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for the ChromeOS human presence sensor (HPS), attached via I2C.4 *5 * The driver exposes HPS as a character device, although currently no read or6 * write operations are supported. Instead, the driver only controls the power7 * state of the sensor, keeping it on only while userspace holds an open file8 * descriptor to the HPS device.9 *10 * Copyright 2022 Google LLC.11 */12 13#include <linux/acpi.h>14#include <linux/fs.h>15#include <linux/gpio/consumer.h>16#include <linux/i2c.h>17#include <linux/miscdevice.h>18#include <linux/module.h>19#include <linux/pm_runtime.h>20 21#define HPS_ACPI_ID		"GOOG0020"22 23struct hps_drvdata {24	struct i2c_client *client;25	struct miscdevice misc_device;26	struct gpio_desc *enable_gpio;27};28 29static void hps_set_power(struct hps_drvdata *hps, bool state)30{31	gpiod_set_value_cansleep(hps->enable_gpio, state);32}33 34static int hps_open(struct inode *inode, struct file *file)35{36	struct hps_drvdata *hps = container_of(file->private_data,37					       struct hps_drvdata, misc_device);38	struct device *dev = &hps->client->dev;39 40	return pm_runtime_resume_and_get(dev);41}42 43static int hps_release(struct inode *inode, struct file *file)44{45	struct hps_drvdata *hps = container_of(file->private_data,46					       struct hps_drvdata, misc_device);47	struct device *dev = &hps->client->dev;48 49	return pm_runtime_put(dev);50}51 52static const struct file_operations hps_fops = {53	.owner = THIS_MODULE,54	.open = hps_open,55	.release = hps_release,56};57 58static int hps_i2c_probe(struct i2c_client *client)59{60	struct hps_drvdata *hps;61	int ret;62 63	hps = devm_kzalloc(&client->dev, sizeof(*hps), GFP_KERNEL);64	if (!hps)65		return -ENOMEM;66 67	hps->misc_device.parent = &client->dev;68	hps->misc_device.minor = MISC_DYNAMIC_MINOR;69	hps->misc_device.name = "cros-hps";70	hps->misc_device.fops = &hps_fops;71 72	i2c_set_clientdata(client, hps);73	hps->client = client;74 75	/*76	 * HPS is powered on from firmware before entering the kernel, so we77	 * acquire the line with GPIOD_OUT_HIGH here to preserve the existing78	 * state. The peripheral is powered off after successful probe below.79	 */80	hps->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_HIGH);81	if (IS_ERR(hps->enable_gpio)) {82		ret = PTR_ERR(hps->enable_gpio);83		dev_err(&client->dev, "failed to get enable gpio: %d\n", ret);84		return ret;85	}86 87	ret = misc_register(&hps->misc_device);88	if (ret) {89		dev_err(&client->dev, "failed to initialize misc device: %d\n", ret);90		return ret;91	}92 93	hps_set_power(hps, false);94	pm_runtime_enable(&client->dev);95	return 0;96}97 98static void hps_i2c_remove(struct i2c_client *client)99{100	struct hps_drvdata *hps = i2c_get_clientdata(client);101 102	pm_runtime_disable(&client->dev);103	misc_deregister(&hps->misc_device);104 105	/*106	 * Re-enable HPS, in order to return it to its default state107	 * (i.e. powered on).108	 */109	hps_set_power(hps, true);110}111 112static int hps_suspend(struct device *dev)113{114	struct i2c_client *client = to_i2c_client(dev);115	struct hps_drvdata *hps = i2c_get_clientdata(client);116 117	hps_set_power(hps, false);118	return 0;119}120 121static int hps_resume(struct device *dev)122{123	struct i2c_client *client = to_i2c_client(dev);124	struct hps_drvdata *hps = i2c_get_clientdata(client);125 126	hps_set_power(hps, true);127	return 0;128}129static DEFINE_RUNTIME_DEV_PM_OPS(hps_pm_ops, hps_suspend, hps_resume, NULL);130 131static const struct i2c_device_id hps_i2c_id[] = {132	{ "cros-hps", 0 },133	{ }134};135MODULE_DEVICE_TABLE(i2c, hps_i2c_id);136 137#ifdef CONFIG_ACPI138static const struct acpi_device_id hps_acpi_id[] = {139	{ HPS_ACPI_ID, 0 },140	{ }141};142MODULE_DEVICE_TABLE(acpi, hps_acpi_id);143#endif /* CONFIG_ACPI */144 145static struct i2c_driver hps_i2c_driver = {146	.probe = hps_i2c_probe,147	.remove = hps_i2c_remove,148	.id_table = hps_i2c_id,149	.driver = {150		.name = "cros-hps",151		.pm = pm_ptr(&hps_pm_ops),152		.acpi_match_table = ACPI_PTR(hps_acpi_id),153	},154};155module_i2c_driver(hps_i2c_driver);156 157MODULE_ALIAS("acpi:" HPS_ACPI_ID);158MODULE_AUTHOR("Sami Kyöstilä <skyostil@chromium.org>");159MODULE_DESCRIPTION("Driver for ChromeOS HPS");160MODULE_LICENSE("GPL");161