1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for the ChromeOS human presence sensor (HPS), attached via I2C.
5 * The driver exposes HPS as a character device, although currently no read or
6 * write operations are supported. Instead, the driver only controls the power
7 * state of the sensor, keeping it on only while userspace holds an open file
8 * descriptor to the HPS device.
10 * Copyright 2022 Google LLC.
13 #include <linux/acpi.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>
21 #define HPS_ACPI_ID "GOOG0020"
24 struct i2c_client
*client
;
25 struct miscdevice misc_device
;
26 struct gpio_desc
*enable_gpio
;
29 static void hps_set_power(struct hps_drvdata
*hps
, bool state
)
31 gpiod_set_value_cansleep(hps
->enable_gpio
, state
);
34 static int hps_open(struct inode
*inode
, struct file
*file
)
36 struct hps_drvdata
*hps
= container_of(file
->private_data
,
37 struct hps_drvdata
, misc_device
);
38 struct device
*dev
= &hps
->client
->dev
;
40 return pm_runtime_resume_and_get(dev
);
43 static int hps_release(struct inode
*inode
, struct file
*file
)
45 struct hps_drvdata
*hps
= container_of(file
->private_data
,
46 struct hps_drvdata
, misc_device
);
47 struct device
*dev
= &hps
->client
->dev
;
49 return pm_runtime_put(dev
);
52 static const struct file_operations hps_fops
= {
55 .release
= hps_release
,
58 static int hps_i2c_probe(struct i2c_client
*client
)
60 struct hps_drvdata
*hps
;
63 hps
= devm_kzalloc(&client
->dev
, sizeof(*hps
), GFP_KERNEL
);
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
;
72 i2c_set_clientdata(client
, hps
);
76 * HPS is powered on from firmware before entering the kernel, so we
77 * acquire the line with GPIOD_OUT_HIGH here to preserve the existing
78 * state. The peripheral is powered off after successful probe below.
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
);
87 ret
= misc_register(&hps
->misc_device
);
89 dev_err(&client
->dev
, "failed to initialize misc device: %d\n", ret
);
93 hps_set_power(hps
, false);
94 pm_runtime_enable(&client
->dev
);
98 static void hps_i2c_remove(struct i2c_client
*client
)
100 struct hps_drvdata
*hps
= i2c_get_clientdata(client
);
102 pm_runtime_disable(&client
->dev
);
103 misc_deregister(&hps
->misc_device
);
106 * Re-enable HPS, in order to return it to its default state
109 hps_set_power(hps
, true);
112 static int hps_suspend(struct device
*dev
)
114 struct i2c_client
*client
= to_i2c_client(dev
);
115 struct hps_drvdata
*hps
= i2c_get_clientdata(client
);
117 hps_set_power(hps
, false);
121 static int hps_resume(struct device
*dev
)
123 struct i2c_client
*client
= to_i2c_client(dev
);
124 struct hps_drvdata
*hps
= i2c_get_clientdata(client
);
126 hps_set_power(hps
, true);
129 static DEFINE_RUNTIME_DEV_PM_OPS(hps_pm_ops
, hps_suspend
, hps_resume
, NULL
);
131 static const struct i2c_device_id hps_i2c_id
[] = {
135 MODULE_DEVICE_TABLE(i2c
, hps_i2c_id
);
138 static const struct acpi_device_id hps_acpi_id
[] = {
142 MODULE_DEVICE_TABLE(acpi
, hps_acpi_id
);
143 #endif /* CONFIG_ACPI */
145 static struct i2c_driver hps_i2c_driver
= {
146 .probe
= hps_i2c_probe
,
147 .remove
= hps_i2c_remove
,
148 .id_table
= hps_i2c_id
,
151 .pm
= pm_ptr(&hps_pm_ops
),
152 .acpi_match_table
= ACPI_PTR(hps_acpi_id
),
155 module_i2c_driver(hps_i2c_driver
);
157 MODULE_ALIAS("acpi:" HPS_ACPI_ID
);
158 MODULE_AUTHOR("Sami Kyöstilä <skyostil@chromium.org>");
159 MODULE_DESCRIPTION("Driver for ChromeOS HPS");
160 MODULE_LICENSE("GPL");