Merge tag 'chrome-platform-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux

Pull chrome-platform updates from Tzung-Bi Shih:
 "Improvements:

   - Use named initializers for struct i2c_device_id

  Fixes:

   - Fix a probe race between cros_ec_sensorhub and cros_ec_sysfs

   - Check for the presence of ACPI_COMPANION() for drivers converted
     from acpi_driver to platform_driver to avoid issues where
     device_match_driver_override() might forcibly match the driver to
     the device

   - Fix a possible UAF in cros_ec_chardev

   - Prevent build for big-endian systems as CHROME_PLATFORMS drivers
     are only running and testing under little-endian systems

  Cleanups:

   - Drop some redundant bits in cros_kbd_led_backlight and Kconfig"

* tag 'chrome-platform-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux:
  platform/chrome: Prevent build for big-endian systems
  platform/chrome: Remove superfluous dependencies from CROS_EC
  platform/chrome: cros_ec_chardev: Introduce rwsem for protecting ec_dev
  platform/chrome: cros_ec_chardev: Add event relayer
  platform/chrome: cros_ec_chardev: Move data to chardev_pdata
  platform/chrome: cros_ec_chardev: Introduce chardev_data
  platform/chrome: Use named initializers for struct i2c_device_id
  platform/chrome: wilco_ec: event: Check ACPI_COMPANION()
  platform/chrome: chromeos_tbmc: Check ACPI_COMPANION()
  platform/chrome: chromeos_privacy_screen: Check ACPI_COMPANION()
  platform/chrome: cros_kbd_led_backlight: Drop CONFIG_MFD_CROS_EC_DEV ifdeffery
  platform/chrome: cros_kbd_led_backlight: Pass keyboard_led as parameter
  platform/chrome: cros_kbd_led_backlight: Drop max_brightness from driver data
  platform/chrome: Resolve kb_wake_angle visibility race
This commit is contained in:
Linus Torvalds
2026-06-16 07:19:53 +05:30
11 changed files with 150 additions and 60 deletions

View File

@@ -6,6 +6,7 @@
menuconfig CHROME_PLATFORMS
bool "Platform support for Chrome hardware"
depends on X86 || ARM || ARM64 || COMPILE_TEST
depends on !CPU_BIG_ENDIAN || COMPILE_TEST
help
Say Y here to get to see options for platform support for
various Chromebooks and Chromeboxes. This option alone does
@@ -75,7 +76,6 @@ config CHROMEOS_OF_HW_PROBER
config CROS_EC
tristate "ChromeOS Embedded Controller"
select CROS_EC_PROTO
depends on X86 || ARM || ARM64 || COMPILE_TEST
help
If you say Y here you get support for the ChromeOS Embedded
Controller (EC) providing keyboard, battery and power services.

View File

@@ -104,6 +104,9 @@ static const struct drm_privacy_screen_ops chromeos_privacy_screen_ops = {
static int chromeos_privacy_screen_probe(struct platform_device *pdev)
{
if (!ACPI_COMPANION(&pdev->dev))
return -ENODEV;
struct drm_privacy_screen *drm_privacy_screen =
drm_privacy_screen_register(&pdev->dev,
&chromeos_privacy_screen_ops,

View File

@@ -69,9 +69,13 @@ static int chromeos_tbmc_probe(struct platform_device *pdev)
{
struct input_dev *idev;
struct device *dev = &pdev->dev;
struct acpi_device *adev = ACPI_COMPANION(dev);
struct acpi_device *adev;
int ret;
adev = ACPI_COMPANION(dev);
if (!adev)
return -ENODEV;
idev = devm_input_allocate_device(dev);
if (!idev)
return -ENOMEM;

View File

@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kref.h>
#include <linux/miscdevice.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
@@ -22,6 +23,7 @@
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>
@@ -31,14 +33,44 @@
/* Arbitrary bounded size for the event queue */
#define CROS_MAX_EVENT_LEN PAGE_SIZE
struct chardev_priv {
/*
* Platform device driver data.
*/
struct chardev_pdata {
struct miscdevice misc;
struct kref kref;
struct rw_semaphore ec_dev_sem;
struct cros_ec_device *ec_dev;
u16 cmd_offset;
struct blocking_notifier_head subscribers;
struct notifier_block relay;
};
static void chardev_pdata_release(struct kref *kref)
{
struct chardev_pdata *pdata = container_of(kref, typeof(*pdata), kref);
kfree(pdata);
}
static int cros_ec_chardev_relay_event(struct notifier_block *nb,
unsigned long queued_during_suspend,
void *_notify)
{
struct chardev_pdata *pdata = container_of(nb, typeof(*pdata), relay);
blocking_notifier_call_chain(&pdata->subscribers, queued_during_suspend,
_notify);
return NOTIFY_OK;
}
struct chardev_priv {
struct notifier_block notifier;
wait_queue_head_t wait_event;
unsigned long event_mask;
struct list_head events;
size_t event_len;
u16 cmd_offset;
struct chardev_pdata *pdata;
};
struct ec_event {
@@ -61,10 +93,10 @@ static int ec_get_version(struct chardev_priv *priv, char *str, int maxlen)
if (!msg)
return -ENOMEM;
msg->command = EC_CMD_GET_VERSION + priv->cmd_offset;
msg->command = EC_CMD_GET_VERSION + priv->pdata->cmd_offset;
msg->insize = sizeof(*resp);
ret = cros_ec_cmd_xfer_status(priv->ec_dev, msg);
ret = cros_ec_cmd_xfer_status(priv->pdata->ec_dev, msg);
if (ret < 0) {
snprintf(str, maxlen,
"Unknown EC version, returned error: %d\n",
@@ -92,10 +124,18 @@ static int cros_ec_chardev_mkbp_event(struct notifier_block *nb,
{
struct chardev_priv *priv = container_of(nb, struct chardev_priv,
notifier);
struct cros_ec_device *ec_dev = priv->ec_dev;
struct cros_ec_device *ec_dev;
struct ec_event *event;
unsigned long event_bit = 1 << ec_dev->event_data.event_type;
int total_size = sizeof(*event) + ec_dev->event_size;
unsigned long event_bit;
int total_size;
guard(rwsem_read)(&priv->pdata->ec_dev_sem);
if (!priv->pdata->ec_dev)
return NOTIFY_DONE;
ec_dev = priv->pdata->ec_dev;
event_bit = 1 << ec_dev->event_data.event_type;
total_size = sizeof(*event) + ec_dev->event_size;
if (!(event_bit & priv->event_mask) ||
(priv->event_len + total_size) > CROS_MAX_EVENT_LEN)
@@ -157,8 +197,7 @@ static struct ec_event *cros_ec_chardev_fetch_event(struct chardev_priv *priv,
static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
{
struct miscdevice *mdev = filp->private_data;
struct cros_ec_dev *ec = dev_get_drvdata(mdev->parent);
struct cros_ec_device *ec_dev = ec->ec_dev;
struct chardev_pdata *pdata = container_of(mdev, typeof(*pdata), misc);
struct chardev_priv *priv;
int ret;
@@ -166,18 +205,20 @@ static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
if (!priv)
return -ENOMEM;
priv->ec_dev = ec_dev;
priv->cmd_offset = ec->cmd_offset;
priv->pdata = pdata;
kref_get(&pdata->kref);
filp->private_data = priv;
INIT_LIST_HEAD(&priv->events);
init_waitqueue_head(&priv->wait_event);
nonseekable_open(inode, filp);
priv->notifier.notifier_call = cros_ec_chardev_mkbp_event;
ret = blocking_notifier_chain_register(&ec_dev->event_notifier,
ret = blocking_notifier_chain_register(&pdata->subscribers,
&priv->notifier);
if (ret) {
dev_err(ec_dev->dev, "failed to register event notifier\n");
dev_err(pdata->ec_dev->dev,
"failed to register event notifier\n");
kref_put(&priv->pdata->kref, chardev_pdata_release);
kfree(priv);
}
@@ -188,6 +229,10 @@ static __poll_t cros_ec_chardev_poll(struct file *filp, poll_table *wait)
{
struct chardev_priv *priv = filp->private_data;
guard(rwsem_read)(&priv->pdata->ec_dev_sem);
if (!priv->pdata->ec_dev)
return EPOLLHUP;
poll_wait(filp, &priv->wait_event, wait);
if (list_empty(&priv->events))
@@ -205,6 +250,10 @@ static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer,
size_t count;
int ret;
guard(rwsem_read)(&priv->pdata->ec_dev_sem);
if (!priv->pdata->ec_dev)
return -ENODEV;
if (priv->event_mask) { /* queued MKBP event */
struct ec_event *event;
@@ -251,11 +300,11 @@ static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer,
static int cros_ec_chardev_release(struct inode *inode, struct file *filp)
{
struct chardev_priv *priv = filp->private_data;
struct cros_ec_device *ec_dev = priv->ec_dev;
struct ec_event *event, *e;
blocking_notifier_chain_unregister(&ec_dev->event_notifier,
blocking_notifier_chain_unregister(&priv->pdata->subscribers,
&priv->notifier);
kref_put(&priv->pdata->kref, chardev_pdata_release);
list_for_each_entry_safe(event, e, &priv->events, node) {
list_del(&event->node);
@@ -298,8 +347,8 @@ static long cros_ec_chardev_ioctl_xcmd(struct chardev_priv *priv, void __user *a
goto exit;
}
s_cmd->command += priv->cmd_offset;
ret = cros_ec_cmd_xfer(priv->ec_dev, s_cmd);
s_cmd->command += priv->pdata->cmd_offset;
ret = cros_ec_cmd_xfer(priv->pdata->ec_dev, s_cmd);
/* Only copy data to userland if data was received. */
if (ret < 0)
goto exit;
@@ -313,7 +362,7 @@ static long cros_ec_chardev_ioctl_xcmd(struct chardev_priv *priv, void __user *a
static long cros_ec_chardev_ioctl_readmem(struct chardev_priv *priv, void __user *arg)
{
struct cros_ec_device *ec_dev = priv->ec_dev;
struct cros_ec_device *ec_dev = priv->pdata->ec_dev;
struct cros_ec_readmem s_mem = { };
long num;
@@ -343,6 +392,10 @@ static long cros_ec_chardev_ioctl(struct file *filp, unsigned int cmd,
{
struct chardev_priv *priv = filp->private_data;
guard(rwsem_read)(&priv->pdata->ec_dev_sem);
if (!priv->pdata->ec_dev)
return -ENODEV;
if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
return -ENOTTY;
@@ -374,28 +427,61 @@ static int cros_ec_chardev_probe(struct platform_device *pdev)
{
struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
struct miscdevice *misc;
struct chardev_pdata *pdata;
int ret;
/* Create a char device: we want to create it anew */
misc = devm_kzalloc(&pdev->dev, sizeof(*misc), GFP_KERNEL);
if (!misc)
pdata = kzalloc_obj(*pdata);
if (!pdata)
return -ENOMEM;
misc->minor = MISC_DYNAMIC_MINOR;
misc->fops = &chardev_fops;
misc->name = ec_platform->ec_name;
misc->parent = pdev->dev.parent;
platform_set_drvdata(pdev, pdata);
kref_init(&pdata->kref);
init_rwsem(&pdata->ec_dev_sem);
pdata->ec_dev = ec->ec_dev;
pdata->cmd_offset = ec->cmd_offset;
BLOCKING_INIT_NOTIFIER_HEAD(&pdata->subscribers);
pdata->relay.notifier_call = cros_ec_chardev_relay_event;
ret = blocking_notifier_chain_register(&pdata->ec_dev->event_notifier,
&pdata->relay);
if (ret) {
dev_err(&pdev->dev, "failed to register event notifier\n");
goto err_put_pdata;
}
dev_set_drvdata(&pdev->dev, misc);
pdata->misc.minor = MISC_DYNAMIC_MINOR;
pdata->misc.fops = &chardev_fops;
pdata->misc.name = ec_platform->ec_name;
pdata->misc.parent = pdev->dev.parent;
return misc_register(misc);
ret = misc_register(&pdata->misc);
if (ret) {
dev_err(&pdev->dev, "failed to register misc device\n");
goto err_unregister_notifier;
}
return 0;
err_unregister_notifier:
blocking_notifier_chain_unregister(&pdata->ec_dev->event_notifier,
&pdata->relay);
err_put_pdata:
kref_put(&pdata->kref, chardev_pdata_release);
return ret;
}
static void cros_ec_chardev_remove(struct platform_device *pdev)
{
struct miscdevice *misc = dev_get_drvdata(&pdev->dev);
struct chardev_pdata *pdata = platform_get_drvdata(pdev);
struct cros_ec_device *ec_dev = pdata->ec_dev;
misc_deregister(misc);
/* stop new fops from being created */
misc_deregister(&pdata->misc);
/* stop existing fops from running */
scoped_guard(rwsem_write, &pdata->ec_dev_sem)
pdata->ec_dev = NULL;
blocking_notifier_chain_unregister(&ec_dev->event_notifier,
&pdata->relay);
kref_put(&pdata->kref, chardev_pdata_release);
}
static const struct platform_device_id cros_ec_chardev_id[] = {

View File

@@ -348,7 +348,7 @@ MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match);
#endif
static const struct i2c_device_id cros_ec_i2c_id[] = {
{ "cros-ec-i2c" },
{ .name = "cros-ec-i2c" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id);

View File

@@ -122,8 +122,12 @@ static int cros_ec_sensorhub_register(struct device *dev,
sensor_type[sensorhub->resp->info.type]++;
}
if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
ec->has_kb_wake_angle = true;
if (ec->group && sysfs_update_group(&ec->class_dev.kobj,
ec->group))
dev_warn(dev, "Unable to update sysfs");
}
if (cros_ec_check_features(ec,
EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {

View File

@@ -405,7 +405,8 @@ static int cros_ec_sysfs_probe(struct platform_device *pd)
struct device *dev = &pd->dev;
int ret;
ret = sysfs_create_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
ec_dev->group = &cros_ec_attr_group;
ret = sysfs_create_group(&ec_dev->class_dev.kobj, ec_dev->group);
if (ret < 0)
dev_err(dev, "failed to create attributes. err=%d\n", ret);
@@ -416,7 +417,7 @@ static void cros_ec_sysfs_remove(struct platform_device *pd)
{
struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
sysfs_remove_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
sysfs_remove_group(&ec_dev->class_dev.kobj, ec_dev->group);
}
static const struct platform_device_id cros_ec_sysfs_id[] = {

View File

@@ -131,7 +131,7 @@ static int hps_resume(struct device *dev)
static DEFINE_RUNTIME_DEV_PM_OPS(hps_pm_ops, hps_suspend, hps_resume, NULL);
static const struct i2c_device_id hps_i2c_id[] = {
{ "cros-hps" },
{ .name = "cros-hps" },
{ }
};
MODULE_DEVICE_TABLE(i2c, hps_i2c_id);

View File

@@ -32,12 +32,11 @@ struct keyboard_led {
* @brightness_set_blocking: Set LED brightness level. It can block the
* caller for the time required for accessing a
* LED device register
* @max_brightness: Maximum brightness.
*
* See struct led_classdev in include/linux/leds.h for more details.
*/
struct keyboard_led_drvdata {
int (*init)(struct platform_device *pdev);
int (*init)(struct platform_device *pdev, struct keyboard_led *keyboard_led);
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
@@ -45,12 +44,8 @@ struct keyboard_led_drvdata {
enum led_brightness brightness);
int (*brightness_set_blocking)(struct led_classdev *led_cdev,
enum led_brightness brightness);
enum led_brightness max_brightness;
};
#define KEYBOARD_BACKLIGHT_MAX 100
#ifdef CONFIG_ACPI
/* Keyboard LED ACPI Device must be defined in firmware */
@@ -94,7 +89,8 @@ keyboard_led_get_brightness_acpi(struct led_classdev *cdev)
return brightness;
}
static int keyboard_led_init_acpi(struct platform_device *pdev)
static int keyboard_led_init_acpi(struct platform_device *pdev,
struct keyboard_led *keyboard_led)
{
acpi_handle handle;
acpi_status status;
@@ -116,17 +112,15 @@ static const struct keyboard_led_drvdata keyboard_led_drvdata_acpi = {
.init = keyboard_led_init_acpi,
.brightness_set = keyboard_led_set_brightness_acpi,
.brightness_get = keyboard_led_get_brightness_acpi,
.max_brightness = KEYBOARD_BACKLIGHT_MAX,
};
#endif /* CONFIG_ACPI */
#if IS_ENABLED(CONFIG_MFD_CROS_EC_DEV)
static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev)
static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev,
struct keyboard_led *keyboard_led)
{
struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
struct keyboard_led *keyboard_led = platform_get_drvdata(pdev);
keyboard_led->ec = cros_ec;
@@ -175,15 +169,8 @@ static const struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm_mfd = {
.init = keyboard_led_init_ec_pwm_mfd,
.brightness_set_blocking = keyboard_led_set_brightness_ec_pwm,
.brightness_get = keyboard_led_get_brightness_ec_pwm,
.max_brightness = KEYBOARD_BACKLIGHT_MAX,
};
#else /* IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) */
static const struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm_mfd = {};
#endif /* IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) */
static int keyboard_led_is_mfd_device(struct platform_device *pdev)
{
return IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) && mfd_get_cell(pdev);
@@ -205,17 +192,16 @@ static int keyboard_led_probe(struct platform_device *pdev)
keyboard_led = devm_kzalloc(&pdev->dev, sizeof(*keyboard_led), GFP_KERNEL);
if (!keyboard_led)
return -ENOMEM;
platform_set_drvdata(pdev, keyboard_led);
if (drvdata->init) {
err = drvdata->init(pdev);
err = drvdata->init(pdev, keyboard_led);
if (err)
return err;
}
keyboard_led->cdev.name = "chromeos::kbd_backlight";
keyboard_led->cdev.flags |= LED_CORE_SUSPENDRESUME | LED_REJECT_NAME_CONFLICT;
keyboard_led->cdev.max_brightness = drvdata->max_brightness;
keyboard_led->cdev.max_brightness = 100;
keyboard_led->cdev.brightness_set = drvdata->brightness_set;
keyboard_led->cdev.brightness_set_blocking = drvdata->brightness_set_blocking;
keyboard_led->cdev.brightness_get = drvdata->brightness_get;

View File

@@ -452,8 +452,13 @@ static void hangup_device(struct event_device_data *dev_data)
static int event_device_probe(struct platform_device *pdev)
{
struct event_device_data *dev_data;
struct acpi_device *adev;
int error, minor;
adev = ACPI_COMPANION(&pdev->dev);
if (!adev)
return -ENODEV;
minor = ida_alloc_max(&event_ida, EVENT_MAX_DEV-1, GFP_KERNEL);
if (minor < 0) {
error = minor;
@@ -494,8 +499,7 @@ static int event_device_probe(struct platform_device *pdev)
goto free_dev_data;
/* Install an ACPI notify handler. */
error = acpi_dev_install_notify_handler(ACPI_COMPANION(&pdev->dev),
ACPI_DEVICE_NOTIFY,
error = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY,
event_device_notify, &pdev->dev);
if (error)
goto free_cdev;

View File

@@ -228,6 +228,7 @@ struct cros_ec_platform {
/**
* struct cros_ec_dev - ChromeOS EC device entry point.
* @class_dev: Device structure used in sysfs.
* @group: sysfs attributes groups for this EC.
* @ec_dev: cros_ec_device structure to talk to the physical device.
* @dev: Pointer to the platform device.
* @debug_info: cros_ec_debugfs structure for debugging information.
@@ -237,6 +238,7 @@ struct cros_ec_platform {
*/
struct cros_ec_dev {
struct device class_dev;
const struct attribute_group *group;
struct cros_ec_device *ec_dev;
struct device *dev;
struct cros_ec_debugfs *debug_info;