serdev: handle devices without an ACPI or device tree node
Subsystems (9)
v6 cover serdev: handle devices without an ACPI or device tree node
Driver-created serdev clients without an ACPI companion or device tree node currently have no modalias and cannot be matched to a driver:
their modalias sysfs read returns -ENODEV and their creators force a bind with device_driver_attach().
Backlight function on dell-uart-backlight is not affected: that class device is registered independently and gets its own add uevent. The user-visible breakage is the failing modalias read on an existing serdev child -- Checkbox dkms_info_attachment aborts with "OSError: [Errno 19] No such device".
Resolve the two long-standing serdev-core TODOs by letting the creator pass a modalias to serdev_device_add() and giving the serdev driver an id_table, modelled on the SPI bus. ACPI and OF match first and are unchanged. Convert dell-uart-backlight and the Lenovo Yoga Tab 2 Pro fastcharger to this path and drop their force-attach workarounds.
Changes in v6:
- Patch 1: switch dell-uart-backlight and yoga-tab2-pro-1380 fastcharger over to the new serdev_device_add() signature with modalias=NULL so the tree keeps building through patch 1; patches 2/3 then switch them to a real modalias. Fixes the bisect break that sashiko-bot flagged (true positive, Markus Probst confirmed).
- Patch 1: wire up struct serdev_device_id in modpost, modelled on the SPI bus: new DEVID() + DEVID_FIELD() entries in scripts/mod/devicetable-offsets.c, do_serdev_entry() and devtable[] entry in scripts/mod/file2alias.c, and the device-id/serdev.h include in include/linux/mod_devicetable.h. Also add serdev_device_id to scripts/coccinelle/misc/of_table.cocci. Reported by sashiko-bot; the two in-tree firmware-less clients do not need MODULE_DEVICE_TABLE(serdev, ...) because the same module creates and drives their serdev child, but the plumbing matches the other buses in case out-of-tree users want it. Markus Probst confirmed as true positive.
- Patch 1: drop the unused #include <linux/string.h> from include/linux/serdev.h. strscpy() is used only in core.c, which already pulls string.h transitively. Reported by Markus Probst.
- Patch 1: drop Markus's v5 Reviewed-by. The modpost wiring and the dell/yoga NULL-stub bisect fix are new code he has not seen; the tag belongs on code that was actually reviewed.
- Patch 3: drop the "serdev driver MUST be registered first" comment in yt2_1380_fc_module_init(). With id_table matching the order of driver_register vs device_add no longer matters. Reported by Markus Probst.
Changes in v5:
- Patch 1: restore is_serdev_device() guard at the top of serdev_device_match(). Dropping it was correct when match only called acpi/of helpers, but Patch 1 also adds serdev_match_id(sdrv->id_table, serdev->modalias), which dereferences serdev_device-only memory. Since serdev_controller shares serdev_bus_type, the driver core calls .match on controllers too; without the guard, serdev->modalias on a controller reads bytes from inside host/nr/serdev/ops, and strcmp() can walk past the end of the controller struct. Reported by sashiko-bot on 2/3 and 3/3.
Changes in v4:
- Patch 1: drop the dead is_serdev_device() guard from the match callback, and dereference serdev once at the top
- Patch 1: drop the "large enough" comment on SERDEV_NAME_SIZE
- Patch 1: in struct serdev_device_driver, place id_table right under driver, next to driver->of_match_table and driver->acpi_match_table
- Patch 1: merge serdev_device_set_modalias() into serdev_device_add() so the modalias is set at the only point where forgetting it would matter; internal firmware callers pass NULL
- All three patches: tightened commit messages and moved the v2/v3 changelog narrative out of Patch 1 into this cover letter
Changes in v3:
- Rename struct serdev_device::name to ::modalias; dev_name() is unchanged
- Model matching on the SPI bus: add struct serdev_device_id and id_table; drop driver->name matching
- Add serdev_device_set_modalias() helper and SERDEV_MODULE_PREFIX
- No MODULE_DEVICE_TABLE(serdev, ...) support: both in-tree firmware-less clients are created by the same module that provides their driver, so udev has nothing to autoload
Changes in v2:
- Instead of silently returning success with no modalias, use a creator-supplied name as a "serial:<name>" modalias fallback and as the matching key, so clients can bind through normal serdev matching
v5: https://lore.kernel.org/lkml/[email protected]/
v4: https://lore.kernel.org/lkml/[email protected]/
v3: https://lore.kernel.org/lkml/[email protected]/
v2: https://lore.kernel.org/lkml/[email protected]/
v1: https://lore.kernel.org/lkml/[email protected]/
ChunAn Wu (3):
serdev: handle devices without an ACPI or device tree node platform/x86: dell-uart-backlight: Bind through normal serdev matching platform/x86: lenovo-yoga-tab2-pro-1380-fastcharger: Bind through
normal serdev matching
MAINTAINERS | 1 + .../platform/x86/dell/dell-uart-backlight.c | 18 +++--- .../lenovo/yoga-tab2-pro-1380-fastcharger.c | 25 +++----- .../platform/x86/x86-android-tablets/core.c | 2 +- drivers/power/sequencing/pwrseq-pcie-m2.c | 2 +- drivers/tty/serdev/core.c | 62 ++++++++++++++++--- include/linux/device-id/serdev.h | 19 ++++++ include/linux/mod_devicetable.h | 1 + include/linux/serdev.h | 11 +++- scripts/coccinelle/misc/of_table.cocci | 16 ++--- scripts/mod/devicetable-offsets.c | 3 + scripts/mod/file2alias.c | 9 +++ 12 files changed, 120 insertions(+), 49 deletions(-) create mode 100644 include/linux/device-id/serdev.h
base-commit: 9e947d96915af63e28896caae85a81b74855621b
Signature · 1 line
2.34.1
PATCH v6 1/3 serdev: handle devices without an ACPI or device tree node
Serdev clients instantiated by a driver have no ACPI companion and no device tree node. Their modalias read returns -ENODEV, their add uevent is suppressed, and they cannot be driver-matched, so creators force a bind with device_driver_attach().
Resolve both TODOs in the serdev core by modelling matching on the SPI
bus: let the creator pass a modalias to serdev_device_add() and give struct serdev_device_driver an id_table. ACPI and OF are still tried first; clients without their own modalias keep returning -ENODEV. The device name, derived from the controller, is unchanged.
Split struct serdev_device_id and its defines into a new include/linux/device-id/serdev.h, following the pattern already used by the SPI and I2C buses, and wire the new ID struct up in file2alias and the of_table coccinelle check.
Update the existing serdev_device_add() callers to pass NULL. The two in-tree firmware-less clients (dell-uart-backlight, yoga-tab2-pro-1380 fastcharger) also pass NULL here so the tree keeps building across the series; the two following patches switch them to a real modalias and drop their force_attach workarounds.
--- MAINTAINERS | 1 + .../platform/x86/dell/dell-uart-backlight.c | 2 +- .../lenovo/yoga-tab2-pro-1380-fastcharger.c | 2 +- .../platform/x86/x86-android-tablets/core.c | 2 +- drivers/power/sequencing/pwrseq-pcie-m2.c | 2 +- drivers/tty/serdev/core.c | 62 ++++++++++++++++--- include/linux/device-id/serdev.h | 19 ++++++ include/linux/mod_devicetable.h | 1 + include/linux/serdev.h | 11 +++- scripts/coccinelle/misc/of_table.cocci | 16 ++--- scripts/mod/devicetable-offsets.c | 3 + scripts/mod/file2alias.c | 9 +++ 12 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 include/linux/device-id/serdev.h
MAINTAINERS +1 -0
@@ -24905,6 +24905,7 @@ L: [email protected] S: Maintained F: Documentation/devicetree/bindings/serial/serial.yaml F: drivers/tty/serdev/ +F: include/linux/device-id/serdev.h F: include/linux/serdev.h F: rust/helpers/serdev.c F: rust/kernel/serdev.rs
drivers/platform/x86/dell/dell-uart-backlight.c +1 -1
@@ -354,7 +354,7 @@ static int dell_uart_bl_pdev_probe(struct platform_device *pdev)if (!serdev)return -ENOMEM ; - ret =serdev_device_add (serdev); + ret =serdev_device_add (serdev,NULL );if (ret) {dev_err (&pdev->dev,"error %d adding serdev\n" , ret);serdev_device_put (serdev);
drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c +1 -1
@@ -260,7 +260,7 @@ static int yt2_1380_fc_pdev_probe(struct platform_device *pdev)/* The fwnode is a managed node, so it will be auto-put on serdev_device_put() */ fwnode_handle_get (dev_fwnode (&serdev->dev)); - ret =serdev_device_add (serdev); + ret =serdev_device_add (serdev,NULL );if (ret) {serdev_device_put (serdev);return dev_err_probe (&pdev->dev, ret,"adding serdev\n" );
drivers/platform/x86/x86-android-tablets/core.c +1 -1
@@ -317,7 +317,7 @@ static __init int x86_instantiate_serdev(const struct x86_dev_info *dev_info, inACPI_COMPANION_SET (&serdev->dev, serdev_adev);acpi_device_set_enumerated (serdev_adev); - ret =serdev_device_add (serdev); + ret =serdev_device_add (serdev,NULL );if (ret) {dev_err (&serdev->dev,"error %d adding serdev\n" , ret);serdev_device_put (serdev);
drivers/power/sequencing/pwrseq-pcie-m2.c +1 -1
@@ -309,7 +309,7 @@ static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx,if (ret) goto err_free_serdev; - ret =serdev_device_add (pci_dev->serdev); + ret =serdev_device_add (pci_dev->serdev,NULL );if (ret) {dev_err (dev,"Failed to add serdev for PCI device (%s): %d\n" ,pci_name (pdev), ret);
drivers/tty/serdev/core.c +53 -9
123 lines not on this page · raw message
include/linux/device-id/serdev.h new file +19 -0
@@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef LINUX_DEVICE_ID_SERDEV_H +#define LINUX_DEVICE_ID_SERDEV_H + +#ifdef __KERNEL__ +typedef unsigned long kernel_ulong_t ; +#endif + +/* serdev */ + +#define SERDEV_NAME_SIZE 40 +#define SERDEV_MODULE_PREFIX "serial:" + +struct serdev_device_id { +char name[SERDEV_NAME_SIZE ]; +kernel_ulong_t driver_data;/* Data private to the driver */ +}; + +#endif /* ifndef LINUX_DEVICE_ID_SERDEV_H */
include/linux/mod_devicetable.h +1 -0
@@ -52,6 +52,7 @@#include "device-id/rpmsg.h" #include "device-id/sdio.h" #include "device-id/sdw.h" +#include "device-id/serdev.h" #include "device-id/serio.h" #include "device-id/slim.h" #include "device-id/spi.h"
include/linux/serdev.h +10 -1
@@ -7,6 +7,7 @@#include <linux/types.h> #include <linux/device.h> +#include <linux/device-id/serdev.h> #include <linux/iopoll.h> #include <linux/uaccess.h> #include <linux/termios.h>
@@ -35,6 +36,10 @@ struct serdev_device_ops {* struct serdev_device - Basic representation of an serdev device * @dev: Driver model representation of the device. * @nr: Device number on serdev bus. +* @modalias: Name of the driver to use with this device, or the alias +* for it. Empty for devices enumerated from firmware, which +* are identified through their ACPI companion or their device +* tree node instead. See serdev_device_add(). * @ctrl: serdev controller managing this device. * @ops: Device operations. * @write_comp: Completion used by serdev_device_write() internally
@@ -43,6 +48,7 @@ struct serdev_device_ops {struct serdev_device {struct device dev;int nr; +char modalias[SERDEV_NAME_SIZE ];struct serdev_controller *ctrl;const struct serdev_device_ops *ops;struct completion write_comp;
@@ -55,12 +61,15 @@ struct serdev_device {* struct serdev_device_driver - serdev slave device driver * @driver: serdev device drivers should initialize name field of this * structure. +* @id_table: List of serdev devices supported by this driver, used to +* match devices which are not enumerated from firmware. * @probe: binds this driver to a serdev device. * @remove: unbinds this driver from the serdev device. * @shutdown: shut down this serdev device. */ struct serdev_device_driver {struct device_driver driver; +const struct serdev_device_id *id_table;int (*probe)(struct serdev_device *);void (*remove)(struct serdev_device *);void (*shutdown)(struct serdev_device *);
@@ -158,7 +167,7 @@ static inline void serdev_controller_put(struct serdev_controller *ctrl) }struct serdev_device *serdev_device_alloc (struct serdev_controller *); -int serdev_device_add (struct serdev_device *); +int serdev_device_add (struct serdev_device *serdev,const char *modalias);void serdev_device_remove (struct serdev_device *); struct serdev_controller *serdev_controller_alloc(struct device *host,
scripts/coccinelle/misc/of_table.cocci +8 -8
@@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 -/// Make sure (of/i2c/platform/spi)_device_id tables are NULL terminated +/// Make sure (of/i2c/platform/serdev/spi)_device_id tables are NULL terminated // -// Keywords: of_table i2c_table platform_table +// Keywords: of_table i2c_table platform_table serdev_table // Confidence: Medium // Options: --include-headers
@@ -15,14 +15,14 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { .var = E, * } }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., * { ..., E, ... }, };
@@ -33,7 +33,7 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { .var = E,
@@ -42,7 +42,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) + { } }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { ..., E, ... }, + { },
@@ -55,7 +55,7 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { .var = E,
@@ -63,7 +63,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) @p1 }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { ..., E, ... } @p1
scripts/mod/devicetable-offsets.c +3 -0
@@ -158,6 +158,9 @@ int main(void)DEVID_FIELD (slim_device_id, manf_id);DEVID_FIELD (slim_device_id, prod_code); +DEVID (serdev_device_id); +DEVID_FIELD (serdev_device_id, name); +DEVID (spi_device_id);DEVID_FIELD (spi_device_id, name);
scripts/mod/file2alias.c +9 -0
@@ -886,6 +886,14 @@ static void do_slim_entry(struct module *mod, void *symval)module_alias_printf (mod, false,"slim:%x:%x:*" , manf_id, prod_code); } +/* Looks like: serial:S */ +static void do_serdev_entry (struct module *mod,void *symval) +{ +DEF_FIELD_ADDR (symval, serdev_device_id, name); + +module_alias_printf (mod, false,SERDEV_MODULE_PREFIX "%s" , *name); +} +/* Looks like: spi:S */ static void do_spi_entry (struct module *mod,void *symval) {
@@ -1500,6 +1508,7 @@ static const struct devtable devtable[] = { {"rpmsg", SIZE_rpmsg_device_id, do_rpmsg_entry}, {"i2c", SIZE_i2c_device_id, do_i2c_entry}, {"i3c", SIZE_i3c_device_id, do_i3c_entry}, + {"serdev", SIZE_serdev_device_id, do_serdev_entry}, {"slim", SIZE_slim_device_id, do_slim_entry}, {"spi", SIZE_spi_device_id, do_spi_entry}, {"dmi", SIZE_dmi_system_id, do_dmi_entry},
Signature · 1 line
2.34.1
[email protected]
AI reviewSashiko has reviewed this patch and found no issues.
Quoted 16 lines
Serdev clients instantiated by a driver have no ACPI companion and no device tree node. Their modalias read returns
-ENODEV, their add uevent is suppressed, and they cannot be driver-matched, so creators force a bind withdevice_driver_attach().Resolve both TODOs in the serdev core by modelling matching on the SPI
bus: let the creator pass a modalias toserdev_device_add()and give structserdev_device_driveranid_table. ACPI and OF are still tried first; clients without their own modalias keep returning-ENODEV. The device name, derived from the controller, is unchanged.Split struct
serdev_device_idand its defines into a newinclude/linux/device-id/serdev.h, following the pattern already used by the SPI and I2C buses, and wire the new ID struct up in file2alias and theof_tablecoccinelle check.
Quoted 7 lines
Update the existing
serdev_device_add()callers to pass NULL. The two in-tree firmware-less clients (dell-uart-backlight, yoga-tab2-pro-1380 fastcharger) also pass NULL here so the tree keeps building across the series; the two following patches switch them to a real modalias and drop theirforce_attachworkarounds.Signed-off-by ChunAn Wu <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Quoted 429 lines · ChunAn Wu
Serdev clients instantiated by a driver have no ACPI companion and no device tree node. Their modalias read returns
-ENODEV, their add uevent is suppressed, and they cannot be driver-matched, so creators force a bind withdevice_driver_attach().Resolve both TODOs in the serdev core by modelling matching on the SPI
bus: let the creator pass a modalias toserdev_device_add()and give structserdev_device_driveranid_table. ACPI and OF are still tried first; clients without their own modalias keep returning-ENODEV. The device name, derived from the controller, is unchanged.Split struct
serdev_device_idand its defines into a newinclude/linux/device-id/serdev.h, following the pattern already used by the SPI and I2C buses, and wire the new ID struct up in file2alias and theof_tablecoccinelle check.Update the existing
serdev_device_add()callers to pass NULL. The two in-tree firmware-less clients (dell-uart-backlight, yoga-tab2-pro-1380 fastcharger) also pass NULL here so the tree keeps building across the series; the two following patches switch them to a real modalias and drop theirforce_attachworkarounds.Signed-off-by ChunAn Wu <[email protected]>--- MAINTAINERS | 1 + .../platform/x86/dell/dell-uart-backlight.c | 2 +- .../lenovo/yoga-tab2-pro-1380-fastcharger.c | 2 +- .../platform/x86/x86-android-tablets/core.c | 2 +- drivers/power/sequencing/pwrseq-pcie-m2.c | 2 +- drivers/tty/serdev/core.c | 62 ++++++++++++++++--- include/linux/device-id/serdev.h | 19 ++++++ include/linux/mod_devicetable.h | 1 + include/linux/serdev.h | 11 +++- scripts/coccinelle/misc/of_table.cocci | 16 ++--- scripts/mod/devicetable-offsets.c | 3 + scripts/mod/file2alias.c | 9 +++ 12 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 include/linux/device-id/serdev.hMAINTAINERS +1
-0@@ -24905,6 +24905,7 @@ L: [email protected] S: Maintained F: Documentation/devicetree/bindings/serial/serial.yaml F: drivers/tty/serdev/ +F: include/linux/device-id/serdev.h F: include/linux/serdev.h F: rust/helpers/serdev.c F: rust/kernel/serdev.rsdrivers/platform/x86/dell/dell-uart-backlight.c +1
-1@@ -354,7 +354,7 @@ static int dell_uart_bl_pdev_probe(struct platform_device *pdev)if (!serdev)return -ENOMEM ; - ret =serdev_device_add (serdev); + ret =serdev_device_add (serdev,NULL );if (ret) {dev_err (&pdev->dev,"error %d adding serdev\n" , ret);serdev_device_put (serdev);drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c +1
-1@@ -260,7 +260,7 @@ static int yt2_1380_fc_pdev_probe(struct platform_device *pdev)/* The fwnode is a managed node, so it will be auto-put on serdev_device_put() */ fwnode_handle_get (dev_fwnode (&serdev->dev)); - ret =serdev_device_add (serdev); + ret =serdev_device_add (serdev,NULL );if (ret) {serdev_device_put (serdev);return dev_err_probe (&pdev->dev, ret,"adding serdev\n" );drivers/platform/x86/x86-android-tablets/core.c +1
-1@@ -317,7 +317,7 @@ static __init int x86_instantiate_serdev(const struct x86_dev_info *dev_info, inACPI_COMPANION_SET (&serdev->dev, serdev_adev);acpi_device_set_enumerated (serdev_adev); - ret =serdev_device_add (serdev); + ret =serdev_device_add (serdev,NULL );if (ret) {dev_err (&serdev->dev,"error %d adding serdev\n" , ret);serdev_device_put (serdev);drivers/power/sequencing/pwrseq-pcie-m2.c +1
-1@@ -309,7 +309,7 @@ static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx,if (ret) goto err_free_serdev; - ret =serdev_device_add (pci_dev->serdev); + ret =serdev_device_add (pci_dev->serdev,NULL );if (ret) {dev_err (dev,"Failed to add serdev for PCI device (%s): %d\n" ,pci_name (pdev), ret);drivers/tty/serdev/core.c +53
-9@@ -29,13 +29,21 @@ static DEFINE_IDA(ctrl_ida);static ssize_t modalias_show (struct device *dev,struct device_attribute *attr,char *buf) { +const struct serdev_device *serdev =to_serdev_device (dev);int len; len =acpi_device_modalias (dev, buf,PAGE_SIZE -1 );if (len != -ENODEV )return len; -return of_device_modalias (dev, buf,PAGE_SIZE ); + len =of_device_modalias (dev, buf,PAGE_SIZE ); +if (len != -ENODEV ) +return len; + +if (!serdev->modalias[0 ]) +return -ENODEV ; + +return sysfs_emit (buf,"%s%s\n" ,SERDEV_MODULE_PREFIX , serdev->modalias); }static DEVICE_ATTR_RO (modalias);@@ -47,15 +55,22 @@ ATTRIBUTE_GROUPS(serdev_device);static int serdev_device_uevent (const struct device *dev,struct kobj_uevent_env *env) { +const struct serdev_device *serdev =to_serdev_device (dev);int rc; -/* TODO: platform modalias */ - rc =acpi_device_uevent_modalias (dev, env);if (rc != -ENODEV )return rc; -return of_device_uevent_modalias (dev, env); + rc =of_device_uevent_modalias (dev, env); +if (rc != -ENODEV ) +return rc; + +if (!serdev->modalias[0 ]) +return -ENODEV ; + +return add_uevent_var (env,"MODALIAS=%s%s" ,SERDEV_MODULE_PREFIX , + serdev->modalias); }static void serdev_device_release (struct device *dev)@@ -86,28 +101,57 @@ static const struct device_type serdev_ctrl_type = { .release = serdev_ctrl_release, }; +static const struct serdev_device_id *serdev_match_id (const struct serdev_device_id *id, +const char *modalias) +{ +if (!id || !modalias[0 ]) +return NULL ; + +while (id->name[0 ]) { +if (!strcmp (modalias, id->name)) +return id; + id++; + } + +return NULL ; +} +static int serdev_device_match (struct device *dev,const struct device_driver *drv) { +const struct serdev_device *serdev; +const struct serdev_device_driver *sdrv; +if (!is_serdev_device (dev))return 0 ; -/* TODO: platform matching */ + serdev =to_serdev_device (dev); + sdrv =to_serdev_device_driver (drv); +if (acpi_driver_match_device (dev, drv))return 1 ; -return of_driver_match_device (dev, drv); +if (of_driver_match_device (dev, drv)) +return 1 ; + +return !!serdev_match_id (sdrv->id_table, serdev->modalias); }/** * serdev_device_add() - add a device previously constructed via serdev_device_alloc() * @serdev: serdev_device to be added +* @modalias: Driver name or alias, used to match devices which have neither +* an ACPI companion nor a device tree node. NULL for devices +* enumerated from firmware. */ -int serdev_device_add (struct serdev_device *serdev) +int serdev_device_add (struct serdev_device *serdev,const char *modalias) {struct serdev_controller *ctrl = serdev->ctrl;struct device *parent = serdev->dev.parent;int err; +if (modalias) +strscpy (serdev->modalias, modalias); +dev_set_name (&serdev->dev,"%s-%d" ,dev_name (parent), serdev->nr);/* Only a single slave device is currently supported. */ @@ -553,7 +597,7 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl)device_set_node (&serdev->dev,of_fwnode_handle (node)); - err =serdev_device_add (serdev); + err =serdev_device_add (serdev,NULL );if (err) { dev_err(&serdev->dev, "failure adding device. status %pe\n",@@ -707,7 +751,7 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl,ACPI_COMPANION_SET (&serdev->dev, adev);acpi_device_set_enumerated (adev); - err =serdev_device_add (serdev); + err =serdev_device_add (serdev,NULL );if (err) { dev_err(&serdev->dev, "failure adding ACPI serdev device. status %pe\n",include/linux/device-id/serdev.h new file +19
-0@@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef LINUX_DEVICE_ID_SERDEV_H +#define LINUX_DEVICE_ID_SERDEV_H + +#ifdef __KERNEL__ +typedef unsigned long kernel_ulong_t ; +#endif + +/* serdev */ + +#define SERDEV_NAME_SIZE 40 +#define SERDEV_MODULE_PREFIX "serial:" + +struct serdev_device_id { +char name[SERDEV_NAME_SIZE ]; +kernel_ulong_t driver_data;/* Data private to the driver */ +}; + +#endif /* ifndef LINUX_DEVICE_ID_SERDEV_H */ include/linux/mod_devicetable.h +1
-0@@ -52,6 +52,7 @@#include "device-id/rpmsg.h" #include "device-id/sdio.h" #include "device-id/sdw.h" +#include "device-id/serdev.h" #include "device-id/serio.h" #include "device-id/slim.h" #include "device-id/spi.h" include/linux/serdev.h +10
-1@@ -7,6 +7,7 @@#include <linux/types.h> #include <linux/device.h> +#include <linux/device-id/serdev.h> #include <linux/iopoll.h> #include <linux/uaccess.h> #include <linux/termios.h> @@ -35,6 +36,10 @@ struct serdev_device_ops {* struct serdev_device - Basic representation of an serdev device * @dev: Driver model representation of the device. * @nr: Device number on serdev bus. +* @modalias: Name of the driver to use with this device, or the alias +* for it. Empty for devices enumerated from firmware, which +* are identified through their ACPI companion or their device +* tree node instead. See serdev_device_add(). * @ctrl: serdev controller managing this device. * @ops: Device operations. * @write_comp: Completion used by serdev_device_write() internally @@ -43,6 +48,7 @@ struct serdev_device_ops {struct serdev_device {struct device dev;int nr; +char modalias[SERDEV_NAME_SIZE ];struct serdev_controller *ctrl;const struct serdev_device_ops *ops;struct completion write_comp;@@ -55,12 +61,15 @@ struct serdev_device {* struct serdev_device_driver - serdev slave device driver * @driver: serdev device drivers should initialize name field of this * structure. +* @id_table: List of serdev devices supported by this driver, used to +* match devices which are not enumerated from firmware. * @probe: binds this driver to a serdev device. * @remove: unbinds this driver from the serdev device. * @shutdown: shut down this serdev device. */ struct serdev_device_driver {struct device_driver driver; +const struct serdev_device_id *id_table;int (*probe)(struct serdev_device *);void (*remove)(struct serdev_device *);void (*shutdown)(struct serdev_device *);@@ -158,7 +167,7 @@ static inline void serdev_controller_put(struct serdev_controller *ctrl) }struct serdev_device *serdev_device_alloc (struct serdev_controller *); -int serdev_device_add (struct serdev_device *); +int serdev_device_add (struct serdev_device *serdev,const char *modalias);void serdev_device_remove (struct serdev_device *); struct serdev_controller *serdev_controller_alloc(struct device *host,scripts/coccinelle/misc/of_table.cocci +8
-8@@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 -/// Make sure (of/i2c/platform/spi)_device_id tables are NULL terminated +/// Make sure (of/i2c/platform/serdev/spi)_device_id tables are NULL terminated // -// Keywords: of_table i2c_table platform_table +// Keywords: of_table i2c_table platform_table serdev_table // Confidence: Medium // Options: --include-headers@@ -15,14 +15,14 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { .var = E, * } }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., * { ..., E, ... }, };@@ -33,7 +33,7 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { .var = E,@@ -42,7 +42,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) + { } }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { ..., E, ... }, + { },@@ -55,7 +55,7 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { .var = E,@@ -63,7 +63,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) @p1 }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| serdev_device_id \| spi_device_id\) arr[] = { ..., { ..., E, ... } @p1scripts/mod/devicetable-offsets.c +3
-0@@ -158,6 +158,9 @@ int main(void)DEVID_FIELD (slim_device_id, manf_id);DEVID_FIELD (slim_device_id, prod_code); +DEVID (serdev_device_id); +DEVID_FIELD (serdev_device_id, name); +DEVID (spi_device_id);DEVID_FIELD (spi_device_id, name);scripts/mod/file2alias.c +8
-0@@ -886,6 +886,14 @@ static void do_slim_entry(struct module *mod, void *symval)module_alias_printf (mod, false,"slim:%x:%x:*" , manf_id, prod_code); } +/* Looks like: serial:S */ +static void do_serdev_entry (struct module *mod,void *symval) +{ +DEF_FIELD_ADDR (symval, serdev_device_id, name); + +module_alias_printf (mod, false,SERDEV_MODULE_PREFIX "%s" , *name); +} +/* Looks like: spi:S */ static void do_spi_entry (struct module *mod,void *symval) {
scripts/mod/file2alias.c static const struct devtable devtable[] = { · line 1514
Quoted 7 lines
scripts/mod/file2alias.c +1
-0@@ -1500,6 +1508,7 @@ static const struct devtable devtable[] = { {"rpmsg", SIZE_rpmsg_device_id, do_rpmsg_entry}, {"i2c", SIZE_i2c_device_id, do_i2c_entry}, {"i3c", SIZE_i3c_device_id, do_i3c_entry}, + {"serdev", SIZE_serdev_device_id, do_serdev_entry}, {"slim", SIZE_slim_device_id, do_slim_entry}, {"spi", SIZE_spi_device_id, do_spi_entry}, {"dmi", SIZE_dmi_system_id, do_dmi_entry},
You should add the maintainers for the modpost scripts to To/Cc.
$ ./scripts/get_maintainer.pl scripts/coccinelle/misc/of_table.cocci scripts/mod/file2alias.c scripts/mod/devicetable-offsets.c include/linux/mod_devicetable.h
Thanks
- Markus Probst
Quoted 7 lines · Markus Probst
Reviewed-by Markus Probst <[email protected]>You should add the maintainers for the modpost scripts to To/Cc.
$ ./scripts/get_maintainer.pl scripts/coccinelle/misc/of_table.cocci scripts/mod/file2alias.c scripts/mod/devicetable-offsets.c include/linux/mod_devicetable.h
Hi Markus,
Thank you for the reminder. Adding the Coccinelle and modpost maintainers to Cc now.
BR
An
Quoted 9 lines · ChunAn Wu
Quoted 7 lines · Markus Probst
Reviewed-by Markus Probst <[email protected]>You should add the maintainers for the modpost scripts to To/Cc.
$ ./scripts/get_maintainer.pl scripts/coccinelle/misc/of_table.cocci scripts/mod/file2alias.c scripts/mod/devicetable-offsets.c include/linux/mod_devicetable.h
Quoted 7 lines
Hi Markus,
Thank you for the reminder. Adding the Coccinelle and modpost maintainers to Cc now.
BR
An
gentle ping
PATCH v6 2/3 platform/x86: dell-uart-backlight: Bind through normal serdev matching
In reply to ChunAn WuThe driver creates a serdev client without firmware metadata and force-attaches its driver to it.
Pass the modalias to serdev_device_add() and give the serdev driver a matching id_table, so that the serdev core binds the two through normal matching, then drop the manual attachment.
The client now also reports a "serial:dell_uart_backlight" modalias to userspace instead of failing with -ENODEV. A failing serdev probe no longer propagates out of the platform probe; as for any other bus, the client is simply left unbound.
--- .../platform/x86/dell/dell-uart-backlight.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-)
drivers/platform/x86/dell/dell-uart-backlight.c +7 -11
@@ -325,11 +325,17 @@ static int dell_uart_bl_serdev_probe(struct serdev_device *serdev)return PTR_ERR_OR_ZERO (dell_bl->bl); } +static const struct serdev_device_id dell_uart_bl_serdev_id[] = { + {KBUILD_MODNAME }, + { } +}; +static struct serdev_device_driver dell_uart_bl_serdev_driver = { .probe = dell_uart_bl_serdev_probe, .driver = { .name =KBUILD_MODNAME , }, + .id_table = dell_uart_bl_serdev_id, };static int dell_uart_bl_pdev_probe (struct platform_device *pdev)
@@ -354,7 +360,7 @@ static int dell_uart_bl_pdev_probe(struct platform_device *pdev)if (!serdev)return -ENOMEM ; - ret =serdev_device_add (serdev,NULL ); + ret =serdev_device_add (serdev,KBUILD_MODNAME );if (ret) {dev_err (&pdev->dev,"error %d adding serdev\n" , ret);serdev_device_put (serdev);
@@ -365,20 +371,10 @@ static int dell_uart_bl_pdev_probe(struct platform_device *pdev)if (ret) goto err_remove_serdev; -/* -* serdev device <-> driver matching relies on OF or ACPI matches and -* neither is available here, manually bind the driver. -*/ - ret =device_driver_attach (&dell_uart_bl_serdev_driver.driver, &serdev->dev); -if (ret) - goto err_unregister_serdev_driver; -/* So that dell_uart_bl_pdev_remove() can remove the serdev */ platform_set_drvdata (pdev, serdev);return 0 ; -err_unregister_serdev_driver: -serdev_device_driver_unregister (&dell_uart_bl_serdev_driver); err_remove_serdev:serdev_device_remove (serdev);return ret;
Signature · 1 line
2.34.1
[email protected]
AI reviewRe: [PATCH v6 2/3] platform/x86: dell-uart-backlight: Bind through normal serdev matching
Sashiko has reviewed this patch and found no issues.
Quoted 7 lines
The driver creates a serdev client without firmware metadata and force-attaches its driver to it.
Pass the modalias to
serdev_device_add()and give the serdev driver a matchingid_table, so that the serdev core binds the two through normal matching, then drop the manual attachment.
Quoted 6 lines
The client now also reports a "serial:dell_uart_backlight" modalias to userspace instead of failing with
-ENODEV. A failing serdev probe no longer propagates out of the platform probe; as for any other bus, the client is simply left unbound.Signed-off-by ChunAn Wu <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
PATCH v6 3/3 platform/x86: lenovo-yoga-tab2-pro-1380-fastcharger: Bind through normal serdev matching
In reply to ChunAn WuThe software-node-backed serdev client cannot be matched through ACPI or device tree and is force-attached to its driver.
Pass the modalias to serdev_device_add() and give the serdev driver a matching id_table, so that the serdev core binds the two through normal matching. Deferred probing is then handled by the driver core, so the -EAGAIN translation is no longer needed.
A failing serdev probe no longer propagates out of the platform probe; as for any other bus, the client is simply left unbound.
--- .../lenovo/yoga-tab2-pro-1380-fastcharger.c | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-)
drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c +7 -18
@@ -219,11 +219,17 @@ static int yt2_1380_fc_serdev_probe(struct serdev_device *serdev)return 0 ; } +static const struct serdev_device_id yt2_1380_fc_serdev_id[] = { + {KBUILD_MODNAME }, + { } +}; +static struct serdev_device_driver yt2_1380_fc_serdev_driver = { .probe = yt2_1380_fc_serdev_probe, .driver = { .name =KBUILD_MODNAME , }, + .id_table = yt2_1380_fc_serdev_id, };static const struct pinctrl_map yt2_1380_fc_pinctrl_map[] = {
@@ -260,25 +266,12 @@ static int yt2_1380_fc_pdev_probe(struct platform_device *pdev)/* The fwnode is a managed node, so it will be auto-put on serdev_device_put() */ fwnode_handle_get (dev_fwnode (&serdev->dev)); - ret =serdev_device_add (serdev,NULL ); + ret =serdev_device_add (serdev,KBUILD_MODNAME );if (ret) {serdev_device_put (serdev);return dev_err_probe (&pdev->dev, ret,"adding serdev\n" ); } -/* -* serdev device <-> driver matching relies on OF or ACPI matches and -* neither is available here, manually bind the driver. -*/ - ret =device_driver_attach (&yt2_1380_fc_serdev_driver.driver, &serdev->dev); -if (ret) { -/* device_driver_attach() maps EPROBE_DEFER to EAGAIN, map it back */ -serdev_device_remove (serdev); -return dev_err_probe (&pdev->dev, - (ret == -EAGAIN ) ? -EPROBE_DEFER : ret, -"attaching serdev driver\n" ); - } -/* So that yt2_1380_fc_pdev_remove() can remove the serdev */ platform_set_drvdata (pdev, serdev);return 0 ;
@@ -304,10 +297,6 @@ static int __init yt2_1380_fc_module_init(void) {int ret; -/* -* serdev driver MUST be registered first because pdev driver calls -* device_driver_attach() on the serdev, serdev-driver pair. -*/ ret =serdev_device_driver_register (&yt2_1380_fc_serdev_driver);if (ret)return ret;
Signature · 1 line
2.34.1
[email protected]
AI reviewRe: [PATCH v6 3/3] platform/x86: lenovo-yoga-tab2-pro-1380-fastcharger: Bind through normal serdev matching
Sashiko has reviewed this patch and found no issues.
Quoted 8 lines
The software-node-backed serdev client cannot be matched through ACPI or device tree and is force-attached to its driver.
Pass the modalias to
serdev_device_add()and give the serdev driver a matchingid_table, so that the serdev core binds the two through normal matching. Deferred probing is then handled by the driver core, so the-EAGAINtranslation is no longer needed.
Quoted 4 lines
A failing serdev probe no longer propagates out of the platform probe; as for any other bus, the client is simply left unbound.
Signed-off-by ChunAn Wu <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3