1 // SPDX-License-Identifier: GPL-2.0 2 /* 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 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. 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 23 struct hps_drvdata { 24 struct i2c_client *client; 25 struct miscdevice misc_device; 26 struct gpio_desc *enable_gpio; 27 }; 28 29 static void hps_set_power(struct hps_drvdata *hps, bool state) 30 { 31 gpiod_set_value_cansleep(hps->enable_gpio, state); 32 } 33 34 static 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 43 static 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 pm_runtime_put(dev); 50 51 return 0; 52 } 53 54 static const struct file_operations hps_fops = { 55 .owner = THIS_MODULE, 56 .open = hps_open, 57 .release = hps_release, 58 }; 59 60 static int hps_i2c_probe(struct i2c_client *client) 61 { 62 struct hps_drvdata *hps; 63 int ret; 64 65 hps = devm_kzalloc(&client->dev, sizeof(*hps), GFP_KERNEL); 66 if (!hps) 67 return -ENOMEM; 68 69 hps->misc_device.parent = &client->dev; 70 hps->misc_device.minor = MISC_DYNAMIC_MINOR; 71 hps->misc_device.name = "cros-hps"; 72 hps->misc_device.fops = &hps_fops; 73 74 i2c_set_clientdata(client, hps); 75 hps->client = client; 76 77 /* 78 * HPS is powered on from firmware before entering the kernel, so we 79 * acquire the line with GPIOD_OUT_HIGH here to preserve the existing 80 * state. The peripheral is powered off after successful probe below. 81 */ 82 hps->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_HIGH); 83 if (IS_ERR(hps->enable_gpio)) { 84 ret = PTR_ERR(hps->enable_gpio); 85 dev_err(&client->dev, "failed to get enable gpio: %d\n", ret); 86 return ret; 87 } 88 89 ret = misc_register(&hps->misc_device); 90 if (ret) { 91 dev_err(&client->dev, "failed to initialize misc device: %d\n", ret); 92 return ret; 93 } 94 95 hps_set_power(hps, false); 96 pm_runtime_enable(&client->dev); 97 return 0; 98 } 99 100 static void hps_i2c_remove(struct i2c_client *client) 101 { 102 struct hps_drvdata *hps = i2c_get_clientdata(client); 103 104 pm_runtime_disable(&client->dev); 105 misc_deregister(&hps->misc_device); 106 107 /* 108 * Re-enable HPS, in order to return it to its default state 109 * (i.e. powered on). 110 */ 111 hps_set_power(hps, true); 112 } 113 114 static int hps_suspend(struct device *dev) 115 { 116 struct i2c_client *client = to_i2c_client(dev); 117 struct hps_drvdata *hps = i2c_get_clientdata(client); 118 119 hps_set_power(hps, false); 120 return 0; 121 } 122 123 static int hps_resume(struct device *dev) 124 { 125 struct i2c_client *client = to_i2c_client(dev); 126 struct hps_drvdata *hps = i2c_get_clientdata(client); 127 128 hps_set_power(hps, true); 129 return 0; 130 } 131 static DEFINE_RUNTIME_DEV_PM_OPS(hps_pm_ops, hps_suspend, hps_resume, NULL); 132 133 static const struct i2c_device_id hps_i2c_id[] = { 134 { "cros-hps" }, 135 { } 136 }; 137 MODULE_DEVICE_TABLE(i2c, hps_i2c_id); 138 139 #ifdef CONFIG_ACPI 140 static const struct acpi_device_id hps_acpi_id[] = { 141 { HPS_ACPI_ID, 0 }, 142 { } 143 }; 144 MODULE_DEVICE_TABLE(acpi, hps_acpi_id); 145 #endif /* CONFIG_ACPI */ 146 147 static struct i2c_driver hps_i2c_driver = { 148 .probe = hps_i2c_probe, 149 .remove = hps_i2c_remove, 150 .id_table = hps_i2c_id, 151 .driver = { 152 .name = "cros-hps", 153 .pm = pm_ptr(&hps_pm_ops), 154 .acpi_match_table = ACPI_PTR(hps_acpi_id), 155 }, 156 }; 157 module_i2c_driver(hps_i2c_driver); 158 159 MODULE_ALIAS("acpi:" HPS_ACPI_ID); 160 MODULE_AUTHOR("Sami Kyöstilä <skyostil@chromium.org>"); 161 MODULE_DESCRIPTION("Driver for ChromeOS HPS"); 162 MODULE_LICENSE("GPL"); 163