linux/drivers/base/auxiliary.c
Linus Torvalds 2e31b16101 ACPI support updates for 7.1-rc1
- Update maintainers information regarding ACPICA (Rafael Wysocki)
 
  - Replace strncpy() with strscpy_pad() in acpi_ut_safe_strncpy() (Kees
    Cook)
 
  - Trigger an ordered system power off after encountering a fatal error
    operator in AML (Armin Wolf)
 
  - Enable ACPI FPDT parsing on LoongArch (Xi Ruoyao)
 
  - Remove the temporary stop-gap acpi_pptt_cache_v1_full structure from
    the ACPI PPTT parser (Ben Horgan)
 
  - Add support for exposing ACPI FPDT subtables FBPT and S3PT (Nate
    DeSimone)
 
  - Address multiple assorted issues and clean up the code in the ACPI
    processor idle driver (Huisong Li)
 
  - Replace strlcat() in the ACPI processor idle drive with a better
    alternative (Andy Shevchenko)
 
  - Rearrange and clean up acpi_processor_errata_piix4() (Rafael Wysocki)
 
  - Move reference performance to capabilities and fix an uninitialized
    variable in the ACPI CPPC library (Pengjie Zhang)
 
  - Add support for the Performance Limited Register to the ACPI CPPC
    library (Sumit Gupta)
 
  - Add cppc_get_perf() API to read performance controls, extend
    cppc_set_epp_perf() for FFH/SystemMemory, and make the ACPI CPPC
    library warn on missing mandatory DESIRED_PERF register (Sumit Gupta)
 
  - Modify the cpufreq CPPC driver to update MIN_PERF/MAX_PERF in target
    callbacks to allow it to control performance bounds via standard
    scaling_min_freq and scaling_max_freq sysfs attributes and add sysfs
    documentation for the Performance Limited Register to it (Sumit Gupta)
 
  - Add ACPI support to the platform device interface in the CMOS RTC
    driver, make the ACPI core device enumeration code create a platform
    device for the CMOS RTC, and drop CMOS RTC PNP device support (Rafael
    Wysocki)
 
  - Consolidate the x86-specific CMOS RTC handling with the ACPI TAD
    driver and clean up the CMOS RTC ACPI address space handler (Rafael
    Wysocki)
 
  - Enable ACPI alarm in the CMOS RTC driver if advertised in ACPI FADT
    and allow that driver to work without a dedicated IRQ if the ACPI
    alarm is used (Rafael Wysocki)
 
  - Clean up the ACPI TAD driver in various ways and add an RTC class
    device interface, including both the RTC setting/reading and alarm
    timer support, to it (Rafael Wysocki)
 
  - Clean up the ACPI AC and ACPI PAD (processor aggregator device)
    drivers (Rafael Wysocki)
 
  - Rework checking for duplicate video bus devices and consolidate
    pnp.bus_id workarounds handling in the ACPI video bus driver (Rafael
    Wysocki)
 
  - Update the ACPI core device drivers to stop setting acpi_device_name()
    unnecessarily (Rafael Wysocki)
 
  - Rearrange code using acpi_device_class() in the ACPI core device
    drivers and update them to stop setting acpi_device_class()
    unnecessarily (Rafael Wysocki)
 
  - Define ACPI_AC_CLASS in one place (Rafael Wysocki)
 
  - Convert the ni903x_wdt watchdog driver and the xen ACPI PAD driver to
    bind to platform devices instead of ACPI devices (Rafael Wysocki)
 
  - Add devm_ghes_register_vendor_record_notifier(), use it in the PCI
    hisi driver, and Add NVIDIA vendor CPER record handler (Kai-Heng
    Feng)
 
  - Consolidate the interface for obtaining a CPU UID from ACPI across
    architectures and use it to address incorrect PCI TPH Steering Tag
    on ARM64 resulting from the invalid assumption that the ACPI
    Processor UID would always be the same as the corresponding logical
    CPU ID in Linux (Chengwen Feng)
 -----BEGIN PGP SIGNATURE-----
 
 iQFGBAABCAAwFiEEcM8Aw/RY0dgsiRUR7l+9nS/U47UFAmnY/bcSHHJqd0Byand5
 c29ja2kubmV0AAoJEO5fvZ0v1OO1chAH/1cGRzh9lSgQ3ZdzIIA5rpRtwKC+CTNz
 iNDvQ97W73B2N+WYzMaloOh+ZVA1Vdqc+8921aH6HI+v7wtg/ZV3h/hU7TagHNY/
 bRFDYaeRXVj4aBXNfoVdn7G5UU9j/kIDcV25I2ubOBqZaO6T5p8p1BK0j0vEj+sG
 yR7XwpEhr2OUQwlIFGKskJwFaH57QJXPEY8wf+o+lMEx/7o/JQRJzKFwsYu01ZZV
 kQy9Ee08P/rsNJwU2ibmZu5P3JMnhategAT8VAMBvkfLScv2sKX+1Vz19NGXzm71
 ARaT7y8MSPNb7SAvWmNZ/rVYrYIL+D3a76Gd7MOGrbVWEn6oXIbCIhY=
 =6vEK
 -----END PGP SIGNATURE-----

Merge tag 'acpi-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull ACPI support updates from Rafael Wysocki:
 "These include an update of the CMOS RTC driver and the related ACPI
  and x86 code that, among other things, switches it over to using the
  platform device interface for device binding on x86 instead of the PNP
  device driver interface (which allows the code in question to be
  simplified quite a bit), a major update of the ACPI Time and Alarm
  Device (TAD) driver adding an RTC class device interface to it, and
  updates of core ACPI drivers that remove some unnecessary and not
  really useful code from them.

  Apart from that, two drivers are converted to using the platform
  driver interface for device binding instead of the ACPI driver one,
  which is slated for removal, support for the Performance Limited
  register is added to the ACPI CPPC library and there are some
  janitorial updates of it and the related cpufreq CPPC driver, the ACPI
  processor driver is fixed and cleaned up, and NVIDIA vendor CPER
  record handler is added to the APEI GHES code.

  Also, the interface for obtaining a CPU UID from ACPI is consolidated
  across architectures and used for fixing a problem with the PCI TPH
  Steering Tag on ARM64, there are two updates related to ACPICA, a
  minor ACPI OS Services Layer (OSL) update, and a few assorted updates
  related to ACPI tables parsing.

  Specifics:

   - Update maintainers information regarding ACPICA (Rafael Wysocki)

   - Replace strncpy() with strscpy_pad() in acpi_ut_safe_strncpy()
     (Kees Cook)

   - Trigger an ordered system power off after encountering a fatal
     error operator in AML (Armin Wolf)

   - Enable ACPI FPDT parsing on LoongArch (Xi Ruoyao)

   - Remove the temporary stop-gap acpi_pptt_cache_v1_full structure
     from the ACPI PPTT parser (Ben Horgan)

   - Add support for exposing ACPI FPDT subtables FBPT and S3PT (Nate
     DeSimone)

   - Address multiple assorted issues and clean up the code in the ACPI
     processor idle driver (Huisong Li)

   - Replace strlcat() in the ACPI processor idle drive with a better
     alternative (Andy Shevchenko)

   - Rearrange and clean up acpi_processor_errata_piix4() (Rafael
     Wysocki)

   - Move reference performance to capabilities and fix an uninitialized
     variable in the ACPI CPPC library (Pengjie Zhang)

   - Add support for the Performance Limited Register to the ACPI CPPC
     library (Sumit Gupta)

   - Add cppc_get_perf() API to read performance controls, extend
     cppc_set_epp_perf() for FFH/SystemMemory, and make the ACPI CPPC
     library warn on missing mandatory DESIRED_PERF register (Sumit
     Gupta)

   - Modify the cpufreq CPPC driver to update MIN_PERF/MAX_PERF in
     target callbacks to allow it to control performance bounds via
     standard scaling_min_freq and scaling_max_freq sysfs attributes and
     add sysfs documentation for the Performance Limited Register to it
     (Sumit Gupta)

   - Add ACPI support to the platform device interface in the CMOS RTC
     driver, make the ACPI core device enumeration code create a
     platform device for the CMOS RTC, and drop CMOS RTC PNP device
     support (Rafael Wysocki)

   - Consolidate the x86-specific CMOS RTC handling with the ACPI TAD
     driver and clean up the CMOS RTC ACPI address space handler (Rafael
     Wysocki)

   - Enable ACPI alarm in the CMOS RTC driver if advertised in ACPI FADT
     and allow that driver to work without a dedicated IRQ if the ACPI
     alarm is used (Rafael Wysocki)

   - Clean up the ACPI TAD driver in various ways and add an RTC class
     device interface, including both the RTC setting/reading and alarm
     timer support, to it (Rafael Wysocki)

   - Clean up the ACPI AC and ACPI PAD (processor aggregator device)
     drivers (Rafael Wysocki)

   - Rework checking for duplicate video bus devices and consolidate
     pnp.bus_id workarounds handling in the ACPI video bus driver
     (Rafael Wysocki)

   - Update the ACPI core device drivers to stop setting
     acpi_device_name() unnecessarily (Rafael Wysocki)

   - Rearrange code using acpi_device_class() in the ACPI core device
     drivers and update them to stop setting acpi_device_class()
     unnecessarily (Rafael Wysocki)

   - Define ACPI_AC_CLASS in one place (Rafael Wysocki)

   - Convert the ni903x_wdt watchdog driver and the xen ACPI PAD driver
     to bind to platform devices instead of ACPI devices (Rafael
     Wysocki)

   - Add devm_ghes_register_vendor_record_notifier(), use it in the PCI
     hisi driver, and Add NVIDIA vendor CPER record handler (Kai-Heng
     Feng)

   - Consolidate the interface for obtaining a CPU UID from ACPI across
     architectures and use it to address incorrect PCI TPH Steering Tag
     on ARM64 resulting from the invalid assumption that the ACPI
     Processor UID would always be the same as the corresponding logical
     CPU ID in Linux (Chengwen Feng)"

* tag 'acpi-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (73 commits)
  ACPICA: Update maintainers information
  watchdog: ni903x_wdt: Convert to a platform driver
  ACPI: PAD: xen: Convert to a platform driver
  ACPI: processor: idle: Reset cpuidle on C-state list changes
  cpuidle: Extract and export no-lock variants of cpuidle_unregister_device()
  PCI/TPH: Pass ACPI Processor UID to Cache Locality _DSM
  ACPI: PPTT: Use acpi_get_cpu_uid() and remove get_acpi_id_for_cpu()
  perf: arm_cspmu: Switch to acpi_get_cpu_uid() from get_acpi_id_for_cpu()
  ACPI: Centralize acpi_get_cpu_uid() declaration in include/linux/acpi.h
  x86/acpi: Add acpi_get_cpu_uid() for unified ACPI CPU UID retrieval
  RISC-V: ACPI: Add acpi_get_cpu_uid() for unified ACPI CPU UID retrieval
  LoongArch: Add acpi_get_cpu_uid() for unified ACPI CPU UID retrieval
  arm64: acpi: Add acpi_get_cpu_uid() for unified ACPI CPU UID retrieval
  ACPI: APEI: GHES: Add NVIDIA vendor CPER record handler
  PCI: hisi: Use devm_ghes_register_vendor_record_notifier()
  ACPI: APEI: GHES: Add devm_ghes_register_vendor_record_notifier()
  ACPI: tables: Enable FPDT on LoongArch
  ACPI: processor: idle: Fix NULL pointer dereference in hotplug path
  ACPI: processor: idle: Reset power_setup_done flag on initialization failure
  ACPI: TAD: Add alarm support to the RTC class device interface
  ...
2026-04-13 19:25:07 -07:00

513 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2020 Intel Corporation
*
* Please see Documentation/driver-api/auxiliary_bus.rst for more information.
*/
#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
#include <linux/device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/string.h>
#include <linux/auxiliary_bus.h>
#include "base.h"
/**
* DOC: PURPOSE
*
* In some subsystems, the functionality of the core device (PCI/ACPI/other) is
* too complex for a single device to be managed by a monolithic driver (e.g.
* Sound Open Firmware), multiple devices might implement a common intersection
* of functionality (e.g. NICs + RDMA), or a driver may want to export an
* interface for another subsystem to drive (e.g. SIOV Physical Function export
* Virtual Function management). A split of the functionality into child-
* devices representing sub-domains of functionality makes it possible to
* compartmentalize, layer, and distribute domain-specific concerns via a Linux
* device-driver model.
*
* An example for this kind of requirement is the audio subsystem where a
* single IP is handling multiple entities such as HDMI, Soundwire, local
* devices such as mics/speakers etc. The split for the core's functionality
* can be arbitrary or be defined by the DSP firmware topology and include
* hooks for test/debug. This allows for the audio core device to be minimal
* and focused on hardware-specific control and communication.
*
* Each auxiliary_device represents a part of its parent functionality. The
* generic behavior can be extended and specialized as needed by encapsulating
* an auxiliary_device within other domain-specific structures and the use of
* .ops callbacks. Devices on the auxiliary bus do not share any structures and
* the use of a communication channel with the parent is domain-specific.
*
* Note that ops are intended as a way to augment instance behavior within a
* class of auxiliary devices, it is not the mechanism for exporting common
* infrastructure from the parent. Consider EXPORT_SYMBOL_NS() to convey
* infrastructure from the parent module to the auxiliary module(s).
*/
/**
* DOC: USAGE
*
* The auxiliary bus is to be used when a driver and one or more kernel
* modules, who share a common header file with the driver, need a mechanism to
* connect and provide access to a shared object allocated by the
* auxiliary_device's registering driver. The registering driver for the
* auxiliary_device(s) and the kernel module(s) registering auxiliary_drivers
* can be from the same subsystem, or from multiple subsystems.
*
* The emphasis here is on a common generic interface that keeps subsystem
* customization out of the bus infrastructure.
*
* One example is a PCI network device that is RDMA-capable and exports a child
* device to be driven by an auxiliary_driver in the RDMA subsystem. The PCI
* driver allocates and registers an auxiliary_device for each physical
* function on the NIC. The RDMA driver registers an auxiliary_driver that
* claims each of these auxiliary_devices. This conveys data/ops published by
* the parent PCI device/driver to the RDMA auxiliary_driver.
*
* Another use case is for the PCI device to be split out into multiple sub
* functions. For each sub function an auxiliary_device is created. A PCI sub
* function driver binds to such devices that creates its own one or more class
* devices. A PCI sub function auxiliary device is likely to be contained in a
* struct with additional attributes such as user defined sub function number
* and optional attributes such as resources and a link to the parent device.
* These attributes could be used by systemd/udev; and hence should be
* initialized before a driver binds to an auxiliary_device.
*
* A key requirement for utilizing the auxiliary bus is that there is no
* dependency on a physical bus, device, register accesses or regmap support.
* These individual devices split from the core cannot live on the platform bus
* as they are not physical devices that are controlled by DT/ACPI. The same
* argument applies for not using MFD in this scenario as MFD relies on
* individual function devices being physical devices.
*/
/**
* DOC: EXAMPLE
*
* Auxiliary devices are created and registered by a subsystem-level core
* device that needs to break up its functionality into smaller fragments. One
* way to extend the scope of an auxiliary_device is to encapsulate it within a
* domain-specific structure defined by the parent device. This structure
* contains the auxiliary_device and any associated shared data/callbacks
* needed to establish the connection with the parent.
*
* An example is:
*
* .. code-block:: c
*
* struct foo {
* struct auxiliary_device auxdev;
* void (*connect)(struct auxiliary_device *auxdev);
* void (*disconnect)(struct auxiliary_device *auxdev);
* void *data;
* };
*
* The parent device then registers the auxiliary_device by calling
* auxiliary_device_init(), and then auxiliary_device_add(), with the pointer
* to the auxdev member of the above structure. The parent provides a name for
* the auxiliary_device that, combined with the parent's KBUILD_MODNAME,
* creates a match_name that is be used for matching and binding with a driver.
*
* Whenever an auxiliary_driver is registered, based on the match_name, the
* auxiliary_driver's probe() is invoked for the matching devices. The
* auxiliary_driver can also be encapsulated inside custom drivers that make
* the core device's functionality extensible by adding additional
* domain-specific ops as follows:
*
* .. code-block:: c
*
* struct my_ops {
* void (*send)(struct auxiliary_device *auxdev);
* void (*receive)(struct auxiliary_device *auxdev);
* };
*
*
* struct my_driver {
* struct auxiliary_driver auxiliary_drv;
* const struct my_ops ops;
* };
*
* An example of this type of usage is:
*
* .. code-block:: c
*
* const struct auxiliary_device_id my_auxiliary_id_table[] = {
* { .name = "foo_mod.foo_dev" },
* { },
* };
*
* const struct my_ops my_custom_ops = {
* .send = my_tx,
* .receive = my_rx,
* };
*
* const struct my_driver my_drv = {
* .auxiliary_drv = {
* .name = "myauxiliarydrv",
* .id_table = my_auxiliary_id_table,
* .probe = my_probe,
* .remove = my_remove,
* .shutdown = my_shutdown,
* },
* .ops = my_custom_ops,
* };
*
* Please note that such custom ops approach is valid, but it is hard to implement
* it right without global locks per-device to protect from auxiliary_drv removal
* during call to that ops. In addition, this implementation lacks proper module
* dependency, which causes to load/unload races between auxiliary parent and devices
* modules.
*
* The most easiest way to provide these ops reliably without needing to
* have a lock is to EXPORT_SYMBOL*() them and rely on already existing
* modules infrastructure for validity and correct dependencies chains.
*/
static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,
const struct auxiliary_device *auxdev)
{
const char *auxdev_name = dev_name(&auxdev->dev);
const char *p = strrchr(auxdev_name, '.');
int match_size;
if (!p)
return NULL;
match_size = p - auxdev_name;
for (; id->name[0]; id++) {
/* use dev_name(&auxdev->dev) prefix before last '.' char to match to */
if (strlen(id->name) == match_size &&
!strncmp(auxdev_name, id->name, match_size))
return id;
}
return NULL;
}
static int auxiliary_match(struct device *dev, const struct device_driver *drv)
{
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
const struct auxiliary_driver *auxdrv = to_auxiliary_drv(drv);
return !!auxiliary_match_id(auxdrv->id_table, auxdev);
}
static int auxiliary_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
const char *name, *p;
name = dev_name(dev);
p = strrchr(name, '.');
return add_uevent_var(env, "MODALIAS=%s%.*s", AUXILIARY_MODULE_PREFIX,
(int)(p - name), name);
}
static int auxiliary_bus_probe(struct device *dev)
{
const struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
int ret;
ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON |
PD_FLAG_DETACH_POWER_OFF);
if (ret) {
dev_warn(dev, "Failed to attach to PM Domain : %d\n", ret);
return ret;
}
return auxdrv->probe(auxdev, auxiliary_match_id(auxdrv->id_table, auxdev));
}
static void auxiliary_bus_remove(struct device *dev)
{
const struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
if (auxdrv->remove)
auxdrv->remove(auxdev);
}
static void auxiliary_bus_shutdown(struct device *dev)
{
const struct auxiliary_driver *auxdrv = NULL;
struct auxiliary_device *auxdev;
if (dev->driver) {
auxdrv = to_auxiliary_drv(dev->driver);
auxdev = to_auxiliary_dev(dev);
}
if (auxdrv && auxdrv->shutdown)
auxdrv->shutdown(auxdev);
}
static const struct bus_type auxiliary_bus_type = {
.name = "auxiliary",
.probe = auxiliary_bus_probe,
.remove = auxiliary_bus_remove,
.shutdown = auxiliary_bus_shutdown,
.match = auxiliary_match,
.uevent = auxiliary_uevent,
};
/**
* auxiliary_device_init - check auxiliary_device and initialize
* @auxdev: auxiliary device struct
*
* This is the second step in the three-step process to register an
* auxiliary_device.
*
* When this function returns an error code, then the device_initialize will
* *not* have been performed, and the caller will be responsible to free any
* memory allocated for the auxiliary_device in the error path directly.
*
* It returns 0 on success. On success, the device_initialize has been
* performed. After this point any error unwinding will need to include a call
* to auxiliary_device_uninit(). In this post-initialize error scenario, a call
* to the device's .release callback will be triggered, and all memory clean-up
* is expected to be handled there.
*/
int auxiliary_device_init(struct auxiliary_device *auxdev)
{
struct device *dev = &auxdev->dev;
if (!dev->parent) {
pr_err("auxiliary_device has a NULL dev->parent\n");
return -EINVAL;
}
if (!auxdev->name) {
pr_err("auxiliary_device has a NULL name\n");
return -EINVAL;
}
dev->bus = &auxiliary_bus_type;
device_initialize(&auxdev->dev);
mutex_init(&auxdev->sysfs.lock);
return 0;
}
EXPORT_SYMBOL_GPL(auxiliary_device_init);
/**
* __auxiliary_device_add - add an auxiliary bus device
* @auxdev: auxiliary bus device to add to the bus
* @modname: name of the parent device's driver module
*
* This is the third step in the three-step process to register an
* auxiliary_device.
*
* This function must be called after a successful call to
* auxiliary_device_init(), which will perform the device_initialize. This
* means that if this returns an error code, then a call to
* auxiliary_device_uninit() must be performed so that the .release callback
* will be triggered to free the memory associated with the auxiliary_device.
*
* The expectation is that users will call the "auxiliary_device_add" macro so
* that the caller's KBUILD_MODNAME is automatically inserted for the modname
* parameter. Only if a user requires a custom name would this version be
* called directly.
*/
int __auxiliary_device_add(struct auxiliary_device *auxdev, const char *modname)
{
struct device *dev = &auxdev->dev;
int ret;
if (!modname) {
dev_err(dev, "auxiliary device modname is NULL\n");
return -EINVAL;
}
ret = dev_set_name(dev, "%s.%s.%d", modname, auxdev->name, auxdev->id);
if (ret) {
dev_err(dev, "auxiliary device dev_set_name failed: %d\n", ret);
return ret;
}
ret = device_add(dev);
if (ret)
dev_err(dev, "adding auxiliary device failed!: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(__auxiliary_device_add);
/**
* __auxiliary_driver_register - register a driver for auxiliary bus devices
* @auxdrv: auxiliary_driver structure
* @owner: owning module/driver
* @modname: KBUILD_MODNAME for parent driver
*
* The expectation is that users will call the "auxiliary_driver_register"
* macro so that the caller's KBUILD_MODNAME is automatically inserted for the
* modname parameter. Only if a user requires a custom name would this version
* be called directly.
*/
int __auxiliary_driver_register(struct auxiliary_driver *auxdrv,
struct module *owner, const char *modname)
{
int ret;
if (WARN_ON(!auxdrv->probe) || WARN_ON(!auxdrv->id_table))
return -EINVAL;
if (auxdrv->name)
auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s.%s", modname,
auxdrv->name);
else
auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s", modname);
if (!auxdrv->driver.name)
return -ENOMEM;
auxdrv->driver.owner = owner;
auxdrv->driver.bus = &auxiliary_bus_type;
auxdrv->driver.mod_name = modname;
ret = driver_register(&auxdrv->driver);
if (ret)
kfree(auxdrv->driver.name);
return ret;
}
EXPORT_SYMBOL_GPL(__auxiliary_driver_register);
/**
* auxiliary_driver_unregister - unregister a driver
* @auxdrv: auxiliary_driver structure
*/
void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv)
{
driver_unregister(&auxdrv->driver);
kfree(auxdrv->driver.name);
}
EXPORT_SYMBOL_GPL(auxiliary_driver_unregister);
static void auxiliary_device_release(struct device *dev)
{
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
of_node_put(dev->of_node);
kfree(auxdev);
}
/**
* auxiliary_device_create - create a device on the auxiliary bus
* @dev: parent device
* @modname: module name used to create the auxiliary driver name.
* @devname: auxiliary bus device name
* @platform_data: auxiliary bus device platform data
* @id: auxiliary bus device id
*
* Helper to create an auxiliary bus device.
* The device created matches driver 'modname.devname' on the auxiliary bus.
*/
struct auxiliary_device *auxiliary_device_create(struct device *dev,
const char *modname,
const char *devname,
void *platform_data,
int id)
{
struct auxiliary_device *auxdev;
int ret;
auxdev = kzalloc_obj(*auxdev);
if (!auxdev)
return NULL;
auxdev->id = id;
auxdev->name = devname;
auxdev->dev.parent = dev;
auxdev->dev.platform_data = platform_data;
auxdev->dev.release = auxiliary_device_release;
device_set_of_node_from_dev(&auxdev->dev, dev);
ret = auxiliary_device_init(auxdev);
if (ret) {
of_node_put(auxdev->dev.of_node);
kfree(auxdev);
return NULL;
}
ret = __auxiliary_device_add(auxdev, modname);
if (ret) {
/*
* It may look odd but auxdev should not be freed here.
* auxiliary_device_uninit() calls device_put() which call
* the device release function, freeing auxdev.
*/
auxiliary_device_uninit(auxdev);
return NULL;
}
return auxdev;
}
EXPORT_SYMBOL_GPL(auxiliary_device_create);
/**
* auxiliary_device_destroy - remove an auxiliary device
* @auxdev: pointer to the auxdev to be removed
*
* Helper to remove an auxiliary device created with
* auxiliary_device_create()
*/
void auxiliary_device_destroy(void *auxdev)
{
struct auxiliary_device *_auxdev = auxdev;
auxiliary_device_delete(_auxdev);
auxiliary_device_uninit(_auxdev);
}
EXPORT_SYMBOL_GPL(auxiliary_device_destroy);
/**
* __devm_auxiliary_device_create - create a managed device on the auxiliary bus
* @dev: parent device
* @modname: module name used to create the auxiliary driver name.
* @devname: auxiliary bus device name
* @platform_data: auxiliary bus device platform data
* @id: auxiliary bus device id
*
* Device managed helper to create an auxiliary bus device.
* The device created matches driver 'modname.devname' on the auxiliary bus.
*/
struct auxiliary_device *__devm_auxiliary_device_create(struct device *dev,
const char *modname,
const char *devname,
void *platform_data,
int id)
{
struct auxiliary_device *auxdev;
int ret;
auxdev = auxiliary_device_create(dev, modname, devname, platform_data, id);
if (!auxdev)
return NULL;
ret = devm_add_action_or_reset(dev, auxiliary_device_destroy,
auxdev);
if (ret)
return NULL;
return auxdev;
}
EXPORT_SYMBOL_GPL(__devm_auxiliary_device_create);
/**
* dev_is_auxiliary - check if the device is an auxiliary one
* @dev: device to check
*/
bool dev_is_auxiliary(struct device *dev)
{
return dev->bus == &auxiliary_bus_type;
}
EXPORT_SYMBOL_GPL(dev_is_auxiliary);
void __init auxiliary_bus_init(void)
{
WARN_ON(bus_register(&auxiliary_bus_type));
}