2017-12-11 23:42:57 +00:00
|
|
|
// 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>
|
2017-12-11 23:42:58 +00:00
|
|
|
#include <linux/idr.h>
|
2017-12-11 23:42:59 +00:00
|
|
|
#include <linux/of.h>
|
2018-09-16 16:45:40 -07:00
|
|
|
#include <linux/of_device.h>
|
2017-12-11 23:43:01 +00:00
|
|
|
#include <linux/pm_runtime.h>
|
2017-12-11 23:42:57 +00:00
|
|
|
#include <linux/slimbus.h>
|
2017-12-11 23:42:58 +00:00
|
|
|
#include "slimbus.h"
|
|
|
|
|
|
|
|
static DEFINE_IDA(ctrl_ida);
|
2017-12-11 23:42:57 +00:00
|
|
|
|
|
|
|
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 &&
|
2019-01-14 13:44:46 +00:00
|
|
|
id->prod_code == sbdev->e_addr.prod_code &&
|
|
|
|
id->dev_index == sbdev->e_addr.dev_index &&
|
|
|
|
id->instance == sbdev->e_addr.instance)
|
2017-12-11 23:42:57 +00:00
|
|
|
return id;
|
|
|
|
id++;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-07-01 14:07:37 +02:00
|
|
|
static int slim_device_match(struct device *dev, const struct device_driver *drv)
|
2017-12-11 23:42:57 +00:00
|
|
|
{
|
|
|
|
struct slim_device *sbdev = to_slim_device(dev);
|
2024-07-01 14:07:37 +02:00
|
|
|
const struct slim_driver *sbdrv = to_slim_driver(drv);
|
2017-12-11 23:42:57 +00:00
|
|
|
|
2018-09-16 16:45:42 -07:00
|
|
|
/* Attempt an OF style match first */
|
|
|
|
if (of_driver_match_device(dev, drv))
|
|
|
|
return 1;
|
|
|
|
|
2017-12-11 23:42:57 +00:00
|
|
|
return !!slim_match(sbdrv->id_table, sbdev);
|
|
|
|
}
|
|
|
|
|
2019-01-14 13:44:48 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-12-11 23:42:57 +00:00
|
|
|
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);
|
2018-09-16 16:45:41 -07:00
|
|
|
int ret;
|
2017-12-11 23:42:57 +00:00
|
|
|
|
2018-09-16 16:45:41 -07:00
|
|
|
ret = sbdrv->probe(sbdev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* try getting the logical address after probe */
|
|
|
|
ret = slim_get_logical_addr(sbdev);
|
|
|
|
if (!ret) {
|
2019-01-14 13:44:48 +00:00
|
|
|
slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_UP);
|
2018-09-16 16:45:41 -07:00
|
|
|
} else {
|
|
|
|
dev_err(&sbdev->dev, "Failed to get logical address\n");
|
|
|
|
ret = -EPROBE_DEFER;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-12-11 23:42:57 +00:00
|
|
|
}
|
|
|
|
|
2021-07-13 21:35:22 +02:00
|
|
|
static void slim_device_remove(struct device *dev)
|
2017-12-11 23:42:57 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-11 12:30:17 +01:00
|
|
|
static int slim_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
|
2018-09-16 16:45:40 -07:00
|
|
|
{
|
2023-01-11 12:30:17 +01:00
|
|
|
const struct slim_device *sbdev = to_slim_device(dev);
|
2018-09-16 16:45:40 -07:00
|
|
|
|
|
|
|
return add_uevent_var(env, "MODALIAS=slim:%s", dev_name(&sbdev->dev));
|
|
|
|
}
|
|
|
|
|
2024-02-24 11:44:03 +00:00
|
|
|
const struct bus_type slimbus_bus = {
|
2017-12-11 23:42:57 +00:00
|
|
|
.name = "slimbus",
|
|
|
|
.match = slim_device_match,
|
|
|
|
.probe = slim_device_probe,
|
|
|
|
.remove = slim_device_remove,
|
2018-09-16 16:45:40 -07:00
|
|
|
.uevent = slim_device_uevent,
|
2017-12-11 23:42:57 +00:00
|
|
|
};
|
|
|
|
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 */
|
2018-09-16 16:45:42 -07:00
|
|
|
if (!(drv->driver.of_match_table || drv->id_table) || !drv->probe)
|
2017-12-11 23:42:57 +00:00
|
|
|
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);
|
|
|
|
|
2017-12-11 23:42:58 +00:00
|
|
|
static void slim_dev_release(struct device *dev)
|
|
|
|
{
|
|
|
|
struct slim_device *sbdev = to_slim_device(dev);
|
|
|
|
|
|
|
|
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;
|
2018-07-05 14:54:25 +01:00
|
|
|
INIT_LIST_HEAD(&sbdev->stream_list);
|
|
|
|
spin_lock_init(&sbdev->stream_list_lock);
|
2020-05-11 16:13:33 +01:00
|
|
|
sbdev->dev.of_node = of_node_get(node);
|
|
|
|
sbdev->dev.fwnode = of_fwnode_handle(node);
|
2017-12-11 23:42:59 +00:00
|
|
|
|
2017-12-11 23:42:58 +00:00
|
|
|
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(sizeof(*sbdev), GFP_KERNEL);
|
|
|
|
if (!sbdev)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sbdev->e_addr = *eaddr;
|
|
|
|
ret = slim_add_device(ctrl, sbdev, node);
|
|
|
|
if (ret) {
|
2018-03-09 14:09:59 +00:00
|
|
|
put_device(&sbdev->dev);
|
2017-12-11 23:42:58 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sbdev;
|
|
|
|
}
|
|
|
|
|
2017-12-11 23:42:59 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-11 23:42:58 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2022-07-06 11:11:30 +01:00
|
|
|
id = ida_alloc(&ctrl_ida, GFP_KERNEL);
|
2017-12-11 23:42:58 +00:00
|
|
|
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);
|
2017-12-11 23:43:01 +00:00
|
|
|
mutex_init(&ctrl->sched.m_reconf);
|
|
|
|
init_completion(&ctrl->sched.pause_comp);
|
2019-01-14 13:44:49 +00:00
|
|
|
spin_lock_init(&ctrl->txn_lock);
|
2017-12-11 23:42:58 +00:00
|
|
|
|
|
|
|
dev_dbg(ctrl->dev, "Bus [%s] registered:dev:%p\n",
|
|
|
|
ctrl->name, ctrl->dev);
|
|
|
|
|
2017-12-11 23:42:59 +00:00
|
|
|
of_register_slim_devices(ctrl);
|
|
|
|
|
2017-12-11 23:42:58 +00:00
|
|
|
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)
|
|
|
|
{
|
2020-05-11 16:13:34 +01:00
|
|
|
of_node_put(sbdev->dev.of_node);
|
2017-12-11 23:42:58 +00:00
|
|
|
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);
|
2022-07-06 11:11:30 +01:00
|
|
|
ida_free(&ctrl_ida, ctrl->id);
|
2017-12-11 23:42:58 +00:00
|
|
|
|
|
|
|
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);
|
2020-09-25 10:55:18 +01:00
|
|
|
if (!ctrl->get_laddr)
|
2022-07-06 11:11:30 +01:00
|
|
|
ida_free(&ctrl->laddr_ida, sbdev->laddr);
|
2017-12-11 23:42:58 +00:00
|
|
|
slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_DOWN);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(slim_report_absent);
|
|
|
|
|
2024-12-24 21:05:01 +08:00
|
|
|
static bool slim_eaddr_equal(const struct slim_eaddr *a,
|
|
|
|
const struct slim_eaddr *b)
|
2017-12-11 23:42:58 +00:00
|
|
|
{
|
|
|
|
return (a->manf_id == b->manf_id &&
|
|
|
|
a->prod_code == b->prod_code &&
|
|
|
|
a->dev_index == b->dev_index &&
|
|
|
|
a->instance == b->instance);
|
|
|
|
}
|
|
|
|
|
driver core: Constify API device_find_child() and adapt for various usages
Constify the following API:
struct device *device_find_child(struct device *dev, void *data,
int (*match)(struct device *dev, void *data));
To :
struct device *device_find_child(struct device *dev, const void *data,
device_match_t match);
typedef int (*device_match_t)(struct device *dev, const void *data);
with the following reasons:
- Protect caller's match data @*data which is for comparison and lookup
and the API does not actually need to modify @*data.
- Make the API's parameters (@match)() and @data have the same type as
all of other device finding APIs (bus|class|driver)_find_device().
- All kinds of existing device match functions can be directly taken
as the API's argument, they were exported by driver core.
Constify the API and adapt for various existing usages.
BTW, various subsystem changes are squashed into this commit to meet
'git bisect' requirement, and this commit has the minimal and simplest
changes to complement squashing shortcoming, and that may bring extra
code improvement.
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Acked-by: Uwe Kleine-König <ukleinek@kernel.org> # for drivers/pwm
Signed-off-by: Zijun Hu <quic_zijuhu@quicinc.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-4-6623037414d4@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-12-24 21:05:03 +08:00
|
|
|
static int slim_match_dev(struct device *dev, const void *data)
|
2017-12-11 23:42:58 +00:00
|
|
|
{
|
driver core: Constify API device_find_child() and adapt for various usages
Constify the following API:
struct device *device_find_child(struct device *dev, void *data,
int (*match)(struct device *dev, void *data));
To :
struct device *device_find_child(struct device *dev, const void *data,
device_match_t match);
typedef int (*device_match_t)(struct device *dev, const void *data);
with the following reasons:
- Protect caller's match data @*data which is for comparison and lookup
and the API does not actually need to modify @*data.
- Make the API's parameters (@match)() and @data have the same type as
all of other device finding APIs (bus|class|driver)_find_device().
- All kinds of existing device match functions can be directly taken
as the API's argument, they were exported by driver core.
Constify the API and adapt for various existing usages.
BTW, various subsystem changes are squashed into this commit to meet
'git bisect' requirement, and this commit has the minimal and simplest
changes to complement squashing shortcoming, and that may bring extra
code improvement.
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Acked-by: Uwe Kleine-König <ukleinek@kernel.org> # for drivers/pwm
Signed-off-by: Zijun Hu <quic_zijuhu@quicinc.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-4-6623037414d4@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-12-24 21:05:03 +08:00
|
|
|
const struct slim_eaddr *e_addr = data;
|
2017-12-11 23:42:58 +00:00
|
|
|
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
|
|
|
|
*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sbdev;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(slim_get_device);
|
|
|
|
|
2018-06-19 17:12:57 +01:00
|
|
|
static struct slim_device *of_find_slim_device(struct slim_controller *ctrl,
|
|
|
|
struct device_node *np)
|
|
|
|
{
|
|
|
|
struct slim_device *sbdev;
|
|
|
|
struct device *dev;
|
|
|
|
|
2024-12-24 21:05:06 +08:00
|
|
|
dev = device_find_child(ctrl->dev, np, device_match_of_node);
|
2018-06-19 17:12:57 +01:00
|
|
|
if (dev) {
|
|
|
|
sbdev = to_slim_device(dev);
|
|
|
|
return sbdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* of_slim_get_device() - get handle to a device using dt node.
|
|
|
|
*
|
|
|
|
* @ctrl: Controller on which this device will be added/queried
|
|
|
|
* @np: node pointer to device
|
|
|
|
*
|
|
|
|
* 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 *of_slim_get_device(struct slim_controller *ctrl,
|
|
|
|
struct device_node *np)
|
|
|
|
{
|
|
|
|
return of_find_slim_device(ctrl, np);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(of_slim_get_device);
|
|
|
|
|
2017-12-11 23:42:58 +00:00
|
|
|
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) {
|
2024-02-24 11:41:37 +00:00
|
|
|
ret = ida_alloc_max(&ctrl->laddr_ida,
|
|
|
|
SLIM_LA_MANAGER - 1, GFP_KERNEL);
|
2017-12-11 23:42:58 +00:00
|
|
|
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;
|
2019-01-14 13:44:47 +00:00
|
|
|
mutex_unlock(&ctrl->lock);
|
2017-12-11 23:42:58 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2019-01-14 13:44:47 +00:00
|
|
|
return 0;
|
|
|
|
|
2017-12-11 23:42:58 +00:00
|
|
|
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;
|
|
|
|
|
2017-12-11 23:43:01 +00:00
|
|
|
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 slimbus_not_active;
|
|
|
|
}
|
|
|
|
|
2017-12-11 23:42:58 +00:00
|
|
|
sbdev = slim_get_device(ctrl, e_addr);
|
|
|
|
if (IS_ERR(sbdev))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (sbdev->is_laddr_valid) {
|
|
|
|
*laddr = sbdev->laddr;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = slim_device_alloc_laddr(sbdev, true);
|
|
|
|
|
2017-12-11 23:43:01 +00:00
|
|
|
slimbus_not_active:
|
|
|
|
pm_runtime_mark_last_busy(ctrl->dev);
|
|
|
|
pm_runtime_put_autosuspend(ctrl->dev);
|
2017-12-11 23:42:58 +00:00
|
|
|
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);
|
|
|
|
|
2017-12-11 23:42:57 +00:00
|
|
|
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");
|