mirror of
https://github.com/torvalds/linux.git
synced 2026-05-13 00:28:54 +02:00
- 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
...
513 lines
16 KiB
C
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));
|
|
}
|