diff options
Diffstat (limited to 'drivers/platform/chrome')
-rw-r--r-- | drivers/platform/chrome/Kconfig | 10 | ||||
-rw-r--r-- | drivers/platform/chrome/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_debugfs.c | 1 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_i2c.c | 5 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lightbar.c | 4 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc.c | 15 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc_mec.c | 6 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc_mec.h | 7 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_spi.c | 1 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_typec.c | 3 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_hps_i2c.c | 160 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_usbpd_notify.c | 6 | ||||
-rw-r--r-- | drivers/platform/chrome/wilco_ec/core.c | 5 |
13 files changed, 195 insertions, 29 deletions
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 6b954c5acadb..c1ca247987d2 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -228,6 +228,16 @@ config CROS_EC_TYPEC To compile this driver as a module, choose M here: the module will be called cros_ec_typec. +config CROS_HPS_I2C + tristate "ChromeOS HPS device" + depends on HID && I2C && PM + help + Say Y here if you want to enable support for the ChromeOS + human presence 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 CROS_USBPD_LOGGER tristate "Logging driver for USB PD charger" depends on CHARGER_CROS_USBPD diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 2950610101f1..f6068d077a40 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o cros-ec-sensorhub-objs := cros_ec_sensorhub.o cros_ec_sensorhub_ring.o obj-$(CONFIG_CROS_EC_SENSORHUB) += cros-ec-sensorhub.o obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o +obj-$(CONFIG_CROS_HPS_I2C) += cros_hps_i2c.o obj-$(CONFIG_CROS_USBPD_LOGGER) += cros_usbpd_logger.o obj-$(CONFIG_CROS_USBPD_NOTIFY) += cros_usbpd_notify.o diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 4e63adf083ea..21d973fc6be2 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -521,6 +521,7 @@ static struct platform_driver cros_ec_debugfs_driver = { .driver = { .name = DRV_NAME, .pm = &cros_ec_debugfs_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = cros_ec_debugfs_probe, .remove = cros_ec_debugfs_remove, diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index b6823c654c3f..dbe698f33128 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -286,8 +286,7 @@ done: return ret; } -static int cros_ec_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int cros_ec_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct cros_ec_device *ec_dev = NULL; @@ -373,7 +372,7 @@ static struct i2c_driver cros_ec_driver = { .of_match_table = of_match_ptr(cros_ec_i2c_of_match), .pm = &cros_ec_i2c_pm_ops, }, - .probe = cros_ec_i2c_probe, + .probe_new = cros_ec_i2c_probe, .remove = cros_ec_i2c_remove, .id_table = cros_ec_i2c_id, }; diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 469dfc7a4a03..1674105decfb 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -8,6 +8,7 @@ #include <linux/device.h> #include <linux/fs.h> #include <linux/kobject.h> +#include <linux/kstrtox.h> #include <linux/module.h> #include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> @@ -493,7 +494,7 @@ static ssize_t userspace_control_store(struct device *dev, bool enable; int ret; - ret = strtobool(buf, &enable); + ret = kstrtobool(buf, &enable); if (ret < 0) return ret; @@ -601,6 +602,7 @@ static struct platform_driver cros_ec_lightbar_driver = { .driver = { .name = DRV_NAME, .pm = &cros_ec_lightbar_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = cros_ec_lightbar_probe, .remove = cros_ec_lightbar_remove, diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 7677ab3c0ead..7fc8f82280ac 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -354,6 +354,9 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) return -EBUSY; } + cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, + EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); + /* * Read the mapped ID twice, the first one is assuming the * EC is a Microchip Embedded Controller (MEC) variant, if the @@ -554,6 +557,12 @@ static struct platform_driver cros_ec_lpc_driver = { .name = DRV_NAME, .acpi_match_table = cros_ec_lpc_acpi_device_ids, .pm = &cros_ec_lpc_pm_ops, + /* + * ACPI child devices may probe before us, and they racily + * check our drvdata pointer. Force synchronous probe until + * those races are resolved. + */ + .probe_type = PROBE_FORCE_SYNCHRONOUS, }, .probe = cros_ec_lpc_probe, .remove = cros_ec_lpc_remove, @@ -586,14 +595,10 @@ static int __init cros_ec_lpc_init(void) return -ENODEV; } - cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, - EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); - /* Register the driver */ ret = platform_driver_register(&cros_ec_lpc_driver); if (ret) { pr_err(DRV_NAME ": can't register driver: %d\n", ret); - cros_ec_lpc_mec_destroy(); return ret; } @@ -603,7 +608,6 @@ static int __init cros_ec_lpc_init(void) if (ret) { pr_err(DRV_NAME ": can't register device: %d\n", ret); platform_driver_unregister(&cros_ec_lpc_driver); - cros_ec_lpc_mec_destroy(); } } @@ -615,7 +619,6 @@ static void __exit cros_ec_lpc_exit(void) if (!cros_ec_lpc_acpi_device_found) platform_device_unregister(&cros_ec_lpc_device); platform_driver_unregister(&cros_ec_lpc_driver); - cros_ec_lpc_mec_destroy(); } module_init(cros_ec_lpc_init); diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c index bbc2884f5e2f..0d9c79b270ce 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.c +++ b/drivers/platform/chrome/cros_ec_lpc_mec.c @@ -146,9 +146,3 @@ void cros_ec_lpc_mec_init(unsigned int base, unsigned int end) mec_emi_end = end; } EXPORT_SYMBOL(cros_ec_lpc_mec_init); - -void cros_ec_lpc_mec_destroy(void) -{ - mutex_destroy(&io_mutex); -} -EXPORT_SYMBOL(cros_ec_lpc_mec_destroy); diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h index aa1018f6b0f2..9d0521b23e8a 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.h +++ b/drivers/platform/chrome/cros_ec_lpc_mec.h @@ -45,13 +45,6 @@ enum cros_ec_lpc_mec_io_type { */ void cros_ec_lpc_mec_init(unsigned int base, unsigned int end); -/* - * cros_ec_lpc_mec_destroy - * - * Cleanup MEC I/O. - */ -void cros_ec_lpc_mec_destroy(void); - /** * cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range. * diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 7360b3ff6e4f..21143dba8970 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -834,6 +834,7 @@ static struct spi_driver cros_ec_driver_spi = { .name = "cros-ec-spi", .of_match_table = cros_ec_spi_of_match, .pm = &cros_ec_spi_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = cros_ec_spi_probe, .remove = cros_ec_spi_remove, diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 2a7ff14dc37e..59de4ce01fab 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -173,10 +173,13 @@ static int cros_typec_get_switch_handles(struct cros_typec_port *port, role_sw_err: typec_switch_put(port->ori_sw); + port->ori_sw = NULL; ori_sw_err: typec_retimer_put(port->retimer); + port->retimer = NULL; retimer_sw_err: typec_mux_put(port->mux); + port->mux = NULL; mux_err: return -ENODEV; } 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"); diff --git a/drivers/platform/chrome/cros_usbpd_notify.c b/drivers/platform/chrome/cros_usbpd_notify.c index 4b5a81c9dc6d..10670b6588e3 100644 --- a/drivers/platform/chrome/cros_usbpd_notify.c +++ b/drivers/platform/chrome/cros_usbpd_notify.c @@ -239,7 +239,11 @@ static int __init cros_usbpd_notify_init(void) return ret; #ifdef CONFIG_ACPI - platform_driver_register(&cros_usbpd_notify_acpi_driver); + ret = platform_driver_register(&cros_usbpd_notify_acpi_driver); + if (ret) { + platform_driver_unregister(&cros_usbpd_notify_plat_driver); + return ret; + } #endif return 0; } diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c index 5b42992bff38..d6a994bdc182 100644 --- a/drivers/platform/chrome/wilco_ec/core.c +++ b/drivers/platform/chrome/wilco_ec/core.c @@ -129,7 +129,6 @@ unregister_rtc: unregister_debugfs: if (ec->debugfs_pdev) platform_device_unregister(ec->debugfs_pdev); - cros_ec_lpc_mec_destroy(); return ret; } @@ -143,10 +142,6 @@ static int wilco_ec_remove(struct platform_device *pdev) platform_device_unregister(ec->rtc_pdev); if (ec->debugfs_pdev) platform_device_unregister(ec->debugfs_pdev); - - /* Teardown cros_ec interface */ - cros_ec_lpc_mec_destroy(); - return 0; } |