Files
linux-stable-mirror/drivers/slimbus/core.c
T
Linus Torvalds bf4afc53b7 Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
This was done entirely with mindless brute force, using

    git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
        xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'

to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.

Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.

For the same reason the 'flex' versions will be done as a separate
conversion.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-02-21 17:09:51 -08:00

548 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2011-2017, The Linux Foundation
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/idr.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/slimbus.h>
#include "slimbus.h"
static DEFINE_IDA(ctrl_ida);
static const struct slim_device_id *slim_match(const struct slim_device_id *id,
const struct slim_device *sbdev)
{
while (id->manf_id != 0 || id->prod_code != 0) {
if (id->manf_id == sbdev->e_addr.manf_id &&
id->prod_code == sbdev->e_addr.prod_code &&
id->dev_index == sbdev->e_addr.dev_index &&
id->instance == sbdev->e_addr.instance)
return id;
id++;
}
return NULL;
}
static int slim_device_match(struct device *dev, const struct device_driver *drv)
{
struct slim_device *sbdev = to_slim_device(dev);
const struct slim_driver *sbdrv = to_slim_driver(drv);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
return !!slim_match(sbdrv->id_table, sbdev);
}
static void slim_device_update_status(struct slim_device *sbdev,
enum slim_device_status status)
{
struct slim_driver *sbdrv;
if (sbdev->status == status)
return;
sbdev->status = status;
if (!sbdev->dev.driver)
return;
sbdrv = to_slim_driver(sbdev->dev.driver);
if (sbdrv->device_status)
sbdrv->device_status(sbdev, sbdev->status);
}
static int slim_device_probe(struct device *dev)
{
struct slim_device *sbdev = to_slim_device(dev);
struct slim_driver *sbdrv = to_slim_driver(dev->driver);
int ret;
ret = sbdrv->probe(sbdev);
if (ret)
return ret;
/* try getting the logical address after probe */
ret = slim_get_logical_addr(sbdev);
if (!ret) {
slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_UP);
} else {
dev_err(&sbdev->dev, "Failed to get logical address\n");
ret = -EPROBE_DEFER;
}
return ret;
}
static void slim_device_remove(struct device *dev)
{
struct slim_device *sbdev = to_slim_device(dev);
struct slim_driver *sbdrv;
if (dev->driver) {
sbdrv = to_slim_driver(dev->driver);
if (sbdrv->remove)
sbdrv->remove(sbdev);
}
}
static int slim_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
const struct slim_device *sbdev = to_slim_device(dev);
return add_uevent_var(env, "MODALIAS=slim:%s", dev_name(&sbdev->dev));
}
const struct bus_type slimbus_bus = {
.name = "slimbus",
.match = slim_device_match,
.probe = slim_device_probe,
.remove = slim_device_remove,
.uevent = slim_device_uevent,
};
EXPORT_SYMBOL_GPL(slimbus_bus);
/*
* __slim_driver_register() - Client driver registration with SLIMbus
*
* @drv:Client driver to be associated with client-device.
* @owner: owning module/driver
*
* This API will register the client driver with the SLIMbus
* It is called from the driver's module-init function.
*/
int __slim_driver_register(struct slim_driver *drv, struct module *owner)
{
/* ID table and probe are mandatory */
if (!(drv->driver.of_match_table || drv->id_table) || !drv->probe)
return -EINVAL;
drv->driver.bus = &slimbus_bus;
drv->driver.owner = owner;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__slim_driver_register);
/*
* slim_driver_unregister() - Undo effect of slim_driver_register
*
* @drv: Client driver to be unregistered
*/
void slim_driver_unregister(struct slim_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(slim_driver_unregister);
static void slim_dev_release(struct device *dev)
{
struct slim_device *sbdev = to_slim_device(dev);
of_node_put(sbdev->dev.of_node);
kfree(sbdev);
}
static int slim_add_device(struct slim_controller *ctrl,
struct slim_device *sbdev,
struct device_node *node)
{
sbdev->dev.bus = &slimbus_bus;
sbdev->dev.parent = ctrl->dev;
sbdev->dev.release = slim_dev_release;
sbdev->dev.driver = NULL;
sbdev->ctrl = ctrl;
INIT_LIST_HEAD(&sbdev->stream_list);
spin_lock_init(&sbdev->stream_list_lock);
sbdev->dev.of_node = of_node_get(node);
sbdev->dev.fwnode = of_fwnode_handle(node);
dev_set_name(&sbdev->dev, "%x:%x:%x:%x",
sbdev->e_addr.manf_id,
sbdev->e_addr.prod_code,
sbdev->e_addr.dev_index,
sbdev->e_addr.instance);
return device_register(&sbdev->dev);
}
static struct slim_device *slim_alloc_device(struct slim_controller *ctrl,
struct slim_eaddr *eaddr,
struct device_node *node)
{
struct slim_device *sbdev;
int ret;
sbdev = kzalloc_obj(*sbdev);
if (!sbdev)
return NULL;
sbdev->e_addr = *eaddr;
ret = slim_add_device(ctrl, sbdev, node);
if (ret) {
put_device(&sbdev->dev);
return NULL;
}
return sbdev;
}
static void of_register_slim_devices(struct slim_controller *ctrl)
{
struct device *dev = ctrl->dev;
struct device_node *node;
if (!ctrl->dev->of_node)
return;
for_each_child_of_node(ctrl->dev->of_node, node) {
struct slim_device *sbdev;
struct slim_eaddr e_addr;
const char *compat = NULL;
int reg[2], ret;
int manf_id, prod_code;
compat = of_get_property(node, "compatible", NULL);
if (!compat)
continue;
ret = sscanf(compat, "slim%x,%x", &manf_id, &prod_code);
if (ret != 2) {
dev_err(dev, "Manf ID & Product code not found %s\n",
compat);
continue;
}
ret = of_property_read_u32_array(node, "reg", reg, 2);
if (ret) {
dev_err(dev, "Device and Instance id not found:%d\n",
ret);
continue;
}
e_addr.dev_index = reg[0];
e_addr.instance = reg[1];
e_addr.manf_id = manf_id;
e_addr.prod_code = prod_code;
sbdev = slim_alloc_device(ctrl, &e_addr, node);
if (!sbdev)
continue;
}
}
/*
* slim_register_controller() - Controller bring-up and registration.
*
* @ctrl: Controller to be registered.
*
* A controller is registered with the framework using this API.
* If devices on a controller were registered before controller,
* this will make sure that they get probed when controller is up
*/
int slim_register_controller(struct slim_controller *ctrl)
{
int id;
id = ida_alloc(&ctrl_ida, GFP_KERNEL);
if (id < 0)
return id;
ctrl->id = id;
if (!ctrl->min_cg)
ctrl->min_cg = SLIM_MIN_CLK_GEAR;
if (!ctrl->max_cg)
ctrl->max_cg = SLIM_MAX_CLK_GEAR;
ida_init(&ctrl->laddr_ida);
idr_init(&ctrl->tid_idr);
mutex_init(&ctrl->lock);
mutex_init(&ctrl->sched.m_reconf);
init_completion(&ctrl->sched.pause_comp);
spin_lock_init(&ctrl->txn_lock);
dev_dbg(ctrl->dev, "Bus [%s] registered:dev:%p\n",
ctrl->name, ctrl->dev);
of_register_slim_devices(ctrl);
return 0;
}
EXPORT_SYMBOL_GPL(slim_register_controller);
/* slim_remove_device: Remove the effect of slim_add_device() */
static void slim_remove_device(struct slim_device *sbdev)
{
device_unregister(&sbdev->dev);
}
static int slim_ctrl_remove_device(struct device *dev, void *null)
{
slim_remove_device(to_slim_device(dev));
return 0;
}
/**
* slim_unregister_controller() - Controller tear-down.
*
* @ctrl: Controller to tear-down.
*/
int slim_unregister_controller(struct slim_controller *ctrl)
{
/* Remove all clients */
device_for_each_child(ctrl->dev, NULL, slim_ctrl_remove_device);
ida_free(&ctrl_ida, ctrl->id);
return 0;
}
EXPORT_SYMBOL_GPL(slim_unregister_controller);
/**
* slim_report_absent() - Controller calls this function when a device
* reports absent, OR when the device cannot be communicated with
*
* @sbdev: Device that cannot be reached, or sent report absent
*/
void slim_report_absent(struct slim_device *sbdev)
{
struct slim_controller *ctrl = sbdev->ctrl;
if (!ctrl)
return;
/* invalidate logical addresses */
mutex_lock(&ctrl->lock);
sbdev->is_laddr_valid = false;
mutex_unlock(&ctrl->lock);
if (!ctrl->get_laddr)
ida_free(&ctrl->laddr_ida, sbdev->laddr);
slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_DOWN);
}
EXPORT_SYMBOL_GPL(slim_report_absent);
static bool slim_eaddr_equal(const struct slim_eaddr *a,
const struct slim_eaddr *b)
{
return (a->manf_id == b->manf_id &&
a->prod_code == b->prod_code &&
a->dev_index == b->dev_index &&
a->instance == b->instance);
}
static int slim_match_dev(struct device *dev, const void *data)
{
const struct slim_eaddr *e_addr = data;
struct slim_device *sbdev = to_slim_device(dev);
return slim_eaddr_equal(&sbdev->e_addr, e_addr);
}
static struct slim_device *find_slim_device(struct slim_controller *ctrl,
struct slim_eaddr *eaddr)
{
struct slim_device *sbdev;
struct device *dev;
dev = device_find_child(ctrl->dev, eaddr, slim_match_dev);
if (dev) {
sbdev = to_slim_device(dev);
return sbdev;
}
return NULL;
}
/**
* slim_get_device() - get handle to a device.
*
* @ctrl: Controller on which this device will be added/queried
* @e_addr: Enumeration address of the device to be queried
*
* Takes a reference to the embedded struct device which needs to be dropped
* after use.
*
* Return: pointer to a device if it has already reported. Creates a new
* device and returns pointer to it if the device has not yet enumerated.
*/
struct slim_device *slim_get_device(struct slim_controller *ctrl,
struct slim_eaddr *e_addr)
{
struct slim_device *sbdev;
sbdev = find_slim_device(ctrl, e_addr);
if (!sbdev) {
sbdev = slim_alloc_device(ctrl, e_addr, NULL);
if (!sbdev)
return ERR_PTR(-ENOMEM);
get_device(&sbdev->dev);
}
return sbdev;
}
EXPORT_SYMBOL_GPL(slim_get_device);
/**
* of_slim_get_device() - get handle to a device using dt node.
*
* @ctrl: Controller on which this device will be queried
* @np: node pointer to device
*
* Takes a reference to the embedded struct device which needs to be dropped
* after use.
*
* Return: pointer to a device if it has been registered, otherwise NULL.
*/
struct slim_device *of_slim_get_device(struct slim_controller *ctrl,
struct device_node *np)
{
struct slim_device *sbdev;
struct device *dev;
dev = device_find_child(ctrl->dev, np, device_match_of_node);
if (dev) {
sbdev = to_slim_device(dev);
return sbdev;
}
return NULL;
}
EXPORT_SYMBOL_GPL(of_slim_get_device);
static int slim_device_alloc_laddr(struct slim_device *sbdev,
bool report_present)
{
struct slim_controller *ctrl = sbdev->ctrl;
u8 laddr;
int ret;
mutex_lock(&ctrl->lock);
if (ctrl->get_laddr) {
ret = ctrl->get_laddr(ctrl, &sbdev->e_addr, &laddr);
if (ret < 0)
goto err;
} else if (report_present) {
ret = ida_alloc_max(&ctrl->laddr_ida,
SLIM_LA_MANAGER - 1, GFP_KERNEL);
if (ret < 0)
goto err;
laddr = ret;
} else {
ret = -EINVAL;
goto err;
}
if (ctrl->set_laddr) {
ret = ctrl->set_laddr(ctrl, &sbdev->e_addr, laddr);
if (ret) {
ret = -EINVAL;
goto err;
}
}
sbdev->laddr = laddr;
sbdev->is_laddr_valid = true;
mutex_unlock(&ctrl->lock);
slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_UP);
dev_dbg(ctrl->dev, "setting slimbus l-addr:%x, ea:%x,%x,%x,%x\n",
laddr, sbdev->e_addr.manf_id, sbdev->e_addr.prod_code,
sbdev->e_addr.dev_index, sbdev->e_addr.instance);
return 0;
err:
mutex_unlock(&ctrl->lock);
return ret;
}
/**
* slim_device_report_present() - Report enumerated device.
*
* @ctrl: Controller with which device is enumerated.
* @e_addr: Enumeration address of the device.
* @laddr: Return logical address (if valid flag is false)
*
* Called by controller in response to REPORT_PRESENT. Framework will assign
* a logical address to this enumeration address.
* Function returns -EXFULL to indicate that all logical addresses are already
* taken.
*/
int slim_device_report_present(struct slim_controller *ctrl,
struct slim_eaddr *e_addr, u8 *laddr)
{
struct slim_device *sbdev;
int ret;
ret = pm_runtime_get_sync(ctrl->dev);
if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
dev_err(ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n",
ctrl->sched.clk_state, ret);
goto out_put_rpm;
}
sbdev = slim_get_device(ctrl, e_addr);
if (IS_ERR(sbdev)) {
ret = -ENODEV;
goto out_put_rpm;
}
if (sbdev->is_laddr_valid) {
*laddr = sbdev->laddr;
ret = 0;
} else {
ret = slim_device_alloc_laddr(sbdev, true);
}
put_device(&sbdev->dev);
out_put_rpm:
pm_runtime_mark_last_busy(ctrl->dev);
pm_runtime_put_autosuspend(ctrl->dev);
return ret;
}
EXPORT_SYMBOL_GPL(slim_device_report_present);
/**
* slim_get_logical_addr() - get/allocate logical address of a SLIMbus device.
*
* @sbdev: client handle requesting the address.
*
* Return: zero if a logical address is valid or a new logical address
* has been assigned. error code in case of error.
*/
int slim_get_logical_addr(struct slim_device *sbdev)
{
if (!sbdev->is_laddr_valid)
return slim_device_alloc_laddr(sbdev, false);
return 0;
}
EXPORT_SYMBOL_GPL(slim_get_logical_addr);
static void __exit slimbus_exit(void)
{
bus_unregister(&slimbus_bus);
}
module_exit(slimbus_exit);
static int __init slimbus_init(void)
{
return bus_register(&slimbus_bus);
}
postcore_initcall(slimbus_init);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SLIMbus core");