mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 18:13:04 +00:00
8142a46c50
There's an effort to move the remove() callback in the driver core to not return an int, as nothing can be done if this function fails. To make that effort easier, make the aux bus remove function void to start with so that no users have to be changed sometime in the future. Cc: Dan Williams <dan.j.williams@intel.com> Cc: Dave Ertman <david.m.ertman@intel.com> Cc: Fred Oh <fred.oh@linux.intel.com> Cc: Kiran Patil <kiran.patil@intel.com> Cc: Leon Romanovsky <leonro@nvidia.com> Cc: Martin Habets <mhabets@solarflare.com> Cc: Parav Pandit <parav@mellanox.com> Cc: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Cc: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Cc: Shiraz Saleem <shiraz.saleem@intel.com> Link: https://lore.kernel.org/r/X8ohB1ks1NK7kPop@kroah.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
269 lines
7.7 KiB
C
269 lines
7.7 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>
|
|
|
|
static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,
|
|
const struct auxiliary_device *auxdev)
|
|
{
|
|
for (; id->name[0]; id++) {
|
|
const char *p = strrchr(dev_name(&auxdev->dev), '.');
|
|
int match_size;
|
|
|
|
if (!p)
|
|
continue;
|
|
match_size = p - dev_name(&auxdev->dev);
|
|
|
|
/* use dev_name(&auxdev->dev) prefix before last '.' char to match to */
|
|
if (strlen(id->name) == match_size &&
|
|
!strncmp(dev_name(&auxdev->dev), id->name, match_size))
|
|
return id;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int auxiliary_match(struct device *dev, struct device_driver *drv)
|
|
{
|
|
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
|
|
struct auxiliary_driver *auxdrv = to_auxiliary_drv(drv);
|
|
|
|
return !!auxiliary_match_id(auxdrv->id_table, auxdev);
|
|
}
|
|
|
|
static int auxiliary_uevent(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 const struct dev_pm_ops auxiliary_dev_pm_ops = {
|
|
SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, pm_generic_runtime_resume, NULL)
|
|
SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)
|
|
};
|
|
|
|
static int auxiliary_bus_probe(struct device *dev)
|
|
{
|
|
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, true);
|
|
if (ret) {
|
|
dev_warn(dev, "Failed to attach to PM Domain : %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = auxdrv->probe(auxdev, auxiliary_match_id(auxdrv->id_table, auxdev));
|
|
if (ret)
|
|
dev_pm_domain_detach(dev, true);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int auxiliary_bus_remove(struct device *dev)
|
|
{
|
|
struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
|
|
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
|
|
|
|
if (auxdrv->remove)
|
|
auxdrv->remove(auxdev);
|
|
dev_pm_domain_detach(dev, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void auxiliary_bus_shutdown(struct device *dev)
|
|
{
|
|
struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
|
|
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
|
|
|
|
if (auxdrv->shutdown)
|
|
auxdrv->shutdown(auxdev);
|
|
}
|
|
|
|
static 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,
|
|
.pm = &auxiliary_dev_pm_ops,
|
|
};
|
|
|
|
/**
|
|
* auxiliary_device_init - check auxiliary_device and initialize
|
|
* @auxdev: auxiliary device struct
|
|
*
|
|
* This is the first step in the two-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);
|
|
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 second step in the two-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) {
|
|
pr_err("auxiliary device modname is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = dev_set_name(dev, "%s.%s.%d", modname, auxdev->name, auxdev->id);
|
|
if (ret) {
|
|
pr_err("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_find_device - auxiliary device iterator for locating a particular device.
|
|
* @start: Device to begin with
|
|
* @data: Data to pass to match function
|
|
* @match: Callback function to check device
|
|
*
|
|
* This function returns a reference to a device that is 'found'
|
|
* for later use, as determined by the @match callback.
|
|
*
|
|
* The callback should return 0 if the device doesn't match and non-zero
|
|
* if it does. If the callback returns non-zero, this function will
|
|
* return to the caller and not iterate over any more devices.
|
|
*/
|
|
struct auxiliary_device *
|
|
auxiliary_find_device(struct device *start, const void *data,
|
|
int (*match)(struct device *dev, const void *data))
|
|
{
|
|
struct device *dev;
|
|
|
|
dev = bus_find_device(&auxiliary_bus_type, start, data, match);
|
|
if (!dev)
|
|
return NULL;
|
|
|
|
return to_auxiliary_dev(dev);
|
|
}
|
|
EXPORT_SYMBOL_GPL(auxiliary_find_device);
|
|
|
|
/**
|
|
* __auxiliary_driver_register - register a driver for auxiliary bus devices
|
|
* @auxdrv: auxiliary_driver structure
|
|
* @owner: owning module/driver
|
|
* @modname: KBUILD_MODNAME for parent driver
|
|
*/
|
|
int __auxiliary_driver_register(struct auxiliary_driver *auxdrv, struct module *owner,
|
|
const char *modname)
|
|
{
|
|
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;
|
|
|
|
return driver_register(&auxdrv->driver);
|
|
}
|
|
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 int __init auxiliary_bus_init(void)
|
|
{
|
|
return bus_register(&auxiliary_bus_type);
|
|
}
|
|
|
|
static void __exit auxiliary_bus_exit(void)
|
|
{
|
|
bus_unregister(&auxiliary_bus_type);
|
|
}
|
|
|
|
module_init(auxiliary_bus_init);
|
|
module_exit(auxiliary_bus_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("Auxiliary Bus");
|
|
MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>");
|
|
MODULE_AUTHOR("Kiran Patil <kiran.patil@intel.com>");
|