162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2011-2017, The Linux Foundation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/errno.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/idr.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_device.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci#include <linux/slimbus.h>
1562306a36Sopenharmony_ci#include "slimbus.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic DEFINE_IDA(ctrl_ida);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic const struct slim_device_id *slim_match(const struct slim_device_id *id,
2062306a36Sopenharmony_ci					       const struct slim_device *sbdev)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	while (id->manf_id != 0 || id->prod_code != 0) {
2362306a36Sopenharmony_ci		if (id->manf_id == sbdev->e_addr.manf_id &&
2462306a36Sopenharmony_ci		    id->prod_code == sbdev->e_addr.prod_code &&
2562306a36Sopenharmony_ci		    id->dev_index == sbdev->e_addr.dev_index &&
2662306a36Sopenharmony_ci		    id->instance == sbdev->e_addr.instance)
2762306a36Sopenharmony_ci			return id;
2862306a36Sopenharmony_ci		id++;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci	return NULL;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int slim_device_match(struct device *dev, struct device_driver *drv)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct slim_device *sbdev = to_slim_device(dev);
3662306a36Sopenharmony_ci	struct slim_driver *sbdrv = to_slim_driver(drv);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* Attempt an OF style match first */
3962306a36Sopenharmony_ci	if (of_driver_match_device(dev, drv))
4062306a36Sopenharmony_ci		return 1;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return !!slim_match(sbdrv->id_table, sbdev);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void slim_device_update_status(struct slim_device *sbdev,
4662306a36Sopenharmony_ci				      enum slim_device_status status)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct slim_driver *sbdrv;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (sbdev->status == status)
5162306a36Sopenharmony_ci		return;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	sbdev->status = status;
5462306a36Sopenharmony_ci	if (!sbdev->dev.driver)
5562306a36Sopenharmony_ci		return;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	sbdrv = to_slim_driver(sbdev->dev.driver);
5862306a36Sopenharmony_ci	if (sbdrv->device_status)
5962306a36Sopenharmony_ci		sbdrv->device_status(sbdev, sbdev->status);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int slim_device_probe(struct device *dev)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct slim_device	*sbdev = to_slim_device(dev);
6562306a36Sopenharmony_ci	struct slim_driver	*sbdrv = to_slim_driver(dev->driver);
6662306a36Sopenharmony_ci	int ret;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ret = sbdrv->probe(sbdev);
6962306a36Sopenharmony_ci	if (ret)
7062306a36Sopenharmony_ci		return ret;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* try getting the logical address after probe */
7362306a36Sopenharmony_ci	ret = slim_get_logical_addr(sbdev);
7462306a36Sopenharmony_ci	if (!ret) {
7562306a36Sopenharmony_ci		slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_UP);
7662306a36Sopenharmony_ci	} else {
7762306a36Sopenharmony_ci		dev_err(&sbdev->dev, "Failed to get logical address\n");
7862306a36Sopenharmony_ci		ret = -EPROBE_DEFER;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return ret;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void slim_device_remove(struct device *dev)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct slim_device *sbdev = to_slim_device(dev);
8762306a36Sopenharmony_ci	struct slim_driver *sbdrv;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (dev->driver) {
9062306a36Sopenharmony_ci		sbdrv = to_slim_driver(dev->driver);
9162306a36Sopenharmony_ci		if (sbdrv->remove)
9262306a36Sopenharmony_ci			sbdrv->remove(sbdev);
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int slim_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	const struct slim_device *sbdev = to_slim_device(dev);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return add_uevent_var(env, "MODALIAS=slim:%s", dev_name(&sbdev->dev));
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistruct bus_type slimbus_bus = {
10462306a36Sopenharmony_ci	.name		= "slimbus",
10562306a36Sopenharmony_ci	.match		= slim_device_match,
10662306a36Sopenharmony_ci	.probe		= slim_device_probe,
10762306a36Sopenharmony_ci	.remove		= slim_device_remove,
10862306a36Sopenharmony_ci	.uevent		= slim_device_uevent,
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slimbus_bus);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * __slim_driver_register() - Client driver registration with SLIMbus
11462306a36Sopenharmony_ci *
11562306a36Sopenharmony_ci * @drv:Client driver to be associated with client-device.
11662306a36Sopenharmony_ci * @owner: owning module/driver
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * This API will register the client driver with the SLIMbus
11962306a36Sopenharmony_ci * It is called from the driver's module-init function.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ciint __slim_driver_register(struct slim_driver *drv, struct module *owner)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	/* ID table and probe are mandatory */
12462306a36Sopenharmony_ci	if (!(drv->driver.of_match_table || drv->id_table) || !drv->probe)
12562306a36Sopenharmony_ci		return -EINVAL;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	drv->driver.bus = &slimbus_bus;
12862306a36Sopenharmony_ci	drv->driver.owner = owner;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return driver_register(&drv->driver);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__slim_driver_register);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/*
13562306a36Sopenharmony_ci * slim_driver_unregister() - Undo effect of slim_driver_register
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * @drv: Client driver to be unregistered
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_civoid slim_driver_unregister(struct slim_driver *drv)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	driver_unregister(&drv->driver);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_driver_unregister);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void slim_dev_release(struct device *dev)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct slim_device *sbdev = to_slim_device(dev);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	kfree(sbdev);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int slim_add_device(struct slim_controller *ctrl,
15362306a36Sopenharmony_ci			   struct slim_device *sbdev,
15462306a36Sopenharmony_ci			   struct device_node *node)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	sbdev->dev.bus = &slimbus_bus;
15762306a36Sopenharmony_ci	sbdev->dev.parent = ctrl->dev;
15862306a36Sopenharmony_ci	sbdev->dev.release = slim_dev_release;
15962306a36Sopenharmony_ci	sbdev->dev.driver = NULL;
16062306a36Sopenharmony_ci	sbdev->ctrl = ctrl;
16162306a36Sopenharmony_ci	INIT_LIST_HEAD(&sbdev->stream_list);
16262306a36Sopenharmony_ci	spin_lock_init(&sbdev->stream_list_lock);
16362306a36Sopenharmony_ci	sbdev->dev.of_node = of_node_get(node);
16462306a36Sopenharmony_ci	sbdev->dev.fwnode = of_fwnode_handle(node);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	dev_set_name(&sbdev->dev, "%x:%x:%x:%x",
16762306a36Sopenharmony_ci				  sbdev->e_addr.manf_id,
16862306a36Sopenharmony_ci				  sbdev->e_addr.prod_code,
16962306a36Sopenharmony_ci				  sbdev->e_addr.dev_index,
17062306a36Sopenharmony_ci				  sbdev->e_addr.instance);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return device_register(&sbdev->dev);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic struct slim_device *slim_alloc_device(struct slim_controller *ctrl,
17662306a36Sopenharmony_ci					     struct slim_eaddr *eaddr,
17762306a36Sopenharmony_ci					     struct device_node *node)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct slim_device *sbdev;
18062306a36Sopenharmony_ci	int ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	sbdev = kzalloc(sizeof(*sbdev), GFP_KERNEL);
18362306a36Sopenharmony_ci	if (!sbdev)
18462306a36Sopenharmony_ci		return NULL;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	sbdev->e_addr = *eaddr;
18762306a36Sopenharmony_ci	ret = slim_add_device(ctrl, sbdev, node);
18862306a36Sopenharmony_ci	if (ret) {
18962306a36Sopenharmony_ci		put_device(&sbdev->dev);
19062306a36Sopenharmony_ci		return NULL;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return sbdev;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void of_register_slim_devices(struct slim_controller *ctrl)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct device *dev = ctrl->dev;
19962306a36Sopenharmony_ci	struct device_node *node;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (!ctrl->dev->of_node)
20262306a36Sopenharmony_ci		return;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	for_each_child_of_node(ctrl->dev->of_node, node) {
20562306a36Sopenharmony_ci		struct slim_device *sbdev;
20662306a36Sopenharmony_ci		struct slim_eaddr e_addr;
20762306a36Sopenharmony_ci		const char *compat = NULL;
20862306a36Sopenharmony_ci		int reg[2], ret;
20962306a36Sopenharmony_ci		int manf_id, prod_code;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		compat = of_get_property(node, "compatible", NULL);
21262306a36Sopenharmony_ci		if (!compat)
21362306a36Sopenharmony_ci			continue;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		ret = sscanf(compat, "slim%x,%x", &manf_id, &prod_code);
21662306a36Sopenharmony_ci		if (ret != 2) {
21762306a36Sopenharmony_ci			dev_err(dev, "Manf ID & Product code not found %s\n",
21862306a36Sopenharmony_ci				compat);
21962306a36Sopenharmony_ci			continue;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		ret = of_property_read_u32_array(node, "reg", reg, 2);
22362306a36Sopenharmony_ci		if (ret) {
22462306a36Sopenharmony_ci			dev_err(dev, "Device and Instance id not found:%d\n",
22562306a36Sopenharmony_ci				ret);
22662306a36Sopenharmony_ci			continue;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		e_addr.dev_index = reg[0];
23062306a36Sopenharmony_ci		e_addr.instance = reg[1];
23162306a36Sopenharmony_ci		e_addr.manf_id = manf_id;
23262306a36Sopenharmony_ci		e_addr.prod_code = prod_code;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		sbdev = slim_alloc_device(ctrl, &e_addr, node);
23562306a36Sopenharmony_ci		if (!sbdev)
23662306a36Sopenharmony_ci			continue;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/*
24162306a36Sopenharmony_ci * slim_register_controller() - Controller bring-up and registration.
24262306a36Sopenharmony_ci *
24362306a36Sopenharmony_ci * @ctrl: Controller to be registered.
24462306a36Sopenharmony_ci *
24562306a36Sopenharmony_ci * A controller is registered with the framework using this API.
24662306a36Sopenharmony_ci * If devices on a controller were registered before controller,
24762306a36Sopenharmony_ci * this will make sure that they get probed when controller is up
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_ciint slim_register_controller(struct slim_controller *ctrl)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	int id;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	id = ida_alloc(&ctrl_ida, GFP_KERNEL);
25462306a36Sopenharmony_ci	if (id < 0)
25562306a36Sopenharmony_ci		return id;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	ctrl->id = id;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (!ctrl->min_cg)
26062306a36Sopenharmony_ci		ctrl->min_cg = SLIM_MIN_CLK_GEAR;
26162306a36Sopenharmony_ci	if (!ctrl->max_cg)
26262306a36Sopenharmony_ci		ctrl->max_cg = SLIM_MAX_CLK_GEAR;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	ida_init(&ctrl->laddr_ida);
26562306a36Sopenharmony_ci	idr_init(&ctrl->tid_idr);
26662306a36Sopenharmony_ci	mutex_init(&ctrl->lock);
26762306a36Sopenharmony_ci	mutex_init(&ctrl->sched.m_reconf);
26862306a36Sopenharmony_ci	init_completion(&ctrl->sched.pause_comp);
26962306a36Sopenharmony_ci	spin_lock_init(&ctrl->txn_lock);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	dev_dbg(ctrl->dev, "Bus [%s] registered:dev:%p\n",
27262306a36Sopenharmony_ci		ctrl->name, ctrl->dev);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	of_register_slim_devices(ctrl);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_register_controller);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/* slim_remove_device: Remove the effect of slim_add_device() */
28162306a36Sopenharmony_cistatic void slim_remove_device(struct slim_device *sbdev)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	of_node_put(sbdev->dev.of_node);
28462306a36Sopenharmony_ci	device_unregister(&sbdev->dev);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int slim_ctrl_remove_device(struct device *dev, void *null)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	slim_remove_device(to_slim_device(dev));
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/**
29462306a36Sopenharmony_ci * slim_unregister_controller() - Controller tear-down.
29562306a36Sopenharmony_ci *
29662306a36Sopenharmony_ci * @ctrl: Controller to tear-down.
29762306a36Sopenharmony_ci */
29862306a36Sopenharmony_ciint slim_unregister_controller(struct slim_controller *ctrl)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	/* Remove all clients */
30162306a36Sopenharmony_ci	device_for_each_child(ctrl->dev, NULL, slim_ctrl_remove_device);
30262306a36Sopenharmony_ci	ida_free(&ctrl_ida, ctrl->id);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return 0;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_unregister_controller);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/**
30962306a36Sopenharmony_ci * slim_report_absent() - Controller calls this function when a device
31062306a36Sopenharmony_ci *	reports absent, OR when the device cannot be communicated with
31162306a36Sopenharmony_ci *
31262306a36Sopenharmony_ci * @sbdev: Device that cannot be reached, or sent report absent
31362306a36Sopenharmony_ci */
31462306a36Sopenharmony_civoid slim_report_absent(struct slim_device *sbdev)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct slim_controller *ctrl = sbdev->ctrl;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (!ctrl)
31962306a36Sopenharmony_ci		return;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* invalidate logical addresses */
32262306a36Sopenharmony_ci	mutex_lock(&ctrl->lock);
32362306a36Sopenharmony_ci	sbdev->is_laddr_valid = false;
32462306a36Sopenharmony_ci	mutex_unlock(&ctrl->lock);
32562306a36Sopenharmony_ci	if (!ctrl->get_laddr)
32662306a36Sopenharmony_ci		ida_free(&ctrl->laddr_ida, sbdev->laddr);
32762306a36Sopenharmony_ci	slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_DOWN);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_report_absent);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	return (a->manf_id == b->manf_id &&
33462306a36Sopenharmony_ci		a->prod_code == b->prod_code &&
33562306a36Sopenharmony_ci		a->dev_index == b->dev_index &&
33662306a36Sopenharmony_ci		a->instance == b->instance);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int slim_match_dev(struct device *dev, void *data)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct slim_eaddr *e_addr = data;
34262306a36Sopenharmony_ci	struct slim_device *sbdev = to_slim_device(dev);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return slim_eaddr_equal(&sbdev->e_addr, e_addr);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic struct slim_device *find_slim_device(struct slim_controller *ctrl,
34862306a36Sopenharmony_ci					    struct slim_eaddr *eaddr)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct slim_device *sbdev;
35162306a36Sopenharmony_ci	struct device *dev;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	dev = device_find_child(ctrl->dev, eaddr, slim_match_dev);
35462306a36Sopenharmony_ci	if (dev) {
35562306a36Sopenharmony_ci		sbdev = to_slim_device(dev);
35662306a36Sopenharmony_ci		return sbdev;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return NULL;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/**
36362306a36Sopenharmony_ci * slim_get_device() - get handle to a device.
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci * @ctrl: Controller on which this device will be added/queried
36662306a36Sopenharmony_ci * @e_addr: Enumeration address of the device to be queried
36762306a36Sopenharmony_ci *
36862306a36Sopenharmony_ci * Return: pointer to a device if it has already reported. Creates a new
36962306a36Sopenharmony_ci * device and returns pointer to it if the device has not yet enumerated.
37062306a36Sopenharmony_ci */
37162306a36Sopenharmony_cistruct slim_device *slim_get_device(struct slim_controller *ctrl,
37262306a36Sopenharmony_ci				    struct slim_eaddr *e_addr)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct slim_device *sbdev;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	sbdev = find_slim_device(ctrl, e_addr);
37762306a36Sopenharmony_ci	if (!sbdev) {
37862306a36Sopenharmony_ci		sbdev = slim_alloc_device(ctrl, e_addr, NULL);
37962306a36Sopenharmony_ci		if (!sbdev)
38062306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return sbdev;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_get_device);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic int of_slim_match_dev(struct device *dev, void *data)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct device_node *np = data;
39062306a36Sopenharmony_ci	struct slim_device *sbdev = to_slim_device(dev);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return (sbdev->dev.of_node == np);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic struct slim_device *of_find_slim_device(struct slim_controller *ctrl,
39662306a36Sopenharmony_ci					       struct device_node *np)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct slim_device *sbdev;
39962306a36Sopenharmony_ci	struct device *dev;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	dev = device_find_child(ctrl->dev, np, of_slim_match_dev);
40262306a36Sopenharmony_ci	if (dev) {
40362306a36Sopenharmony_ci		sbdev = to_slim_device(dev);
40462306a36Sopenharmony_ci		return sbdev;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return NULL;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/**
41162306a36Sopenharmony_ci * of_slim_get_device() - get handle to a device using dt node.
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * @ctrl: Controller on which this device will be added/queried
41462306a36Sopenharmony_ci * @np: node pointer to device
41562306a36Sopenharmony_ci *
41662306a36Sopenharmony_ci * Return: pointer to a device if it has already reported. Creates a new
41762306a36Sopenharmony_ci * device and returns pointer to it if the device has not yet enumerated.
41862306a36Sopenharmony_ci */
41962306a36Sopenharmony_cistruct slim_device *of_slim_get_device(struct slim_controller *ctrl,
42062306a36Sopenharmony_ci				       struct device_node *np)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	return of_find_slim_device(ctrl, np);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_slim_get_device);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int slim_device_alloc_laddr(struct slim_device *sbdev,
42762306a36Sopenharmony_ci				   bool report_present)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct slim_controller *ctrl = sbdev->ctrl;
43062306a36Sopenharmony_ci	u8 laddr;
43162306a36Sopenharmony_ci	int ret;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	mutex_lock(&ctrl->lock);
43462306a36Sopenharmony_ci	if (ctrl->get_laddr) {
43562306a36Sopenharmony_ci		ret = ctrl->get_laddr(ctrl, &sbdev->e_addr, &laddr);
43662306a36Sopenharmony_ci		if (ret < 0)
43762306a36Sopenharmony_ci			goto err;
43862306a36Sopenharmony_ci	} else if (report_present) {
43962306a36Sopenharmony_ci		ret = ida_alloc_max(&ctrl->laddr_ida,
44062306a36Sopenharmony_ci				    SLIM_LA_MANAGER - 1, GFP_KERNEL);
44162306a36Sopenharmony_ci		if (ret < 0)
44262306a36Sopenharmony_ci			goto err;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		laddr = ret;
44562306a36Sopenharmony_ci	} else {
44662306a36Sopenharmony_ci		ret = -EINVAL;
44762306a36Sopenharmony_ci		goto err;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (ctrl->set_laddr) {
45162306a36Sopenharmony_ci		ret = ctrl->set_laddr(ctrl, &sbdev->e_addr, laddr);
45262306a36Sopenharmony_ci		if (ret) {
45362306a36Sopenharmony_ci			ret = -EINVAL;
45462306a36Sopenharmony_ci			goto err;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	sbdev->laddr = laddr;
45962306a36Sopenharmony_ci	sbdev->is_laddr_valid = true;
46062306a36Sopenharmony_ci	mutex_unlock(&ctrl->lock);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_UP);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	dev_dbg(ctrl->dev, "setting slimbus l-addr:%x, ea:%x,%x,%x,%x\n",
46562306a36Sopenharmony_ci		laddr, sbdev->e_addr.manf_id, sbdev->e_addr.prod_code,
46662306a36Sopenharmony_ci		sbdev->e_addr.dev_index, sbdev->e_addr.instance);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return 0;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cierr:
47162306a36Sopenharmony_ci	mutex_unlock(&ctrl->lock);
47262306a36Sopenharmony_ci	return ret;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/**
47762306a36Sopenharmony_ci * slim_device_report_present() - Report enumerated device.
47862306a36Sopenharmony_ci *
47962306a36Sopenharmony_ci * @ctrl: Controller with which device is enumerated.
48062306a36Sopenharmony_ci * @e_addr: Enumeration address of the device.
48162306a36Sopenharmony_ci * @laddr: Return logical address (if valid flag is false)
48262306a36Sopenharmony_ci *
48362306a36Sopenharmony_ci * Called by controller in response to REPORT_PRESENT. Framework will assign
48462306a36Sopenharmony_ci * a logical address to this enumeration address.
48562306a36Sopenharmony_ci * Function returns -EXFULL to indicate that all logical addresses are already
48662306a36Sopenharmony_ci * taken.
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_ciint slim_device_report_present(struct slim_controller *ctrl,
48962306a36Sopenharmony_ci			       struct slim_eaddr *e_addr, u8 *laddr)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct slim_device *sbdev;
49262306a36Sopenharmony_ci	int ret;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	ret = pm_runtime_get_sync(ctrl->dev);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
49762306a36Sopenharmony_ci		dev_err(ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n",
49862306a36Sopenharmony_ci				    ctrl->sched.clk_state, ret);
49962306a36Sopenharmony_ci		goto slimbus_not_active;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	sbdev = slim_get_device(ctrl, e_addr);
50362306a36Sopenharmony_ci	if (IS_ERR(sbdev))
50462306a36Sopenharmony_ci		return -ENODEV;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (sbdev->is_laddr_valid) {
50762306a36Sopenharmony_ci		*laddr = sbdev->laddr;
50862306a36Sopenharmony_ci		return 0;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	ret = slim_device_alloc_laddr(sbdev, true);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cislimbus_not_active:
51462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(ctrl->dev);
51562306a36Sopenharmony_ci	pm_runtime_put_autosuspend(ctrl->dev);
51662306a36Sopenharmony_ci	return ret;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_device_report_present);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/**
52162306a36Sopenharmony_ci * slim_get_logical_addr() - get/allocate logical address of a SLIMbus device.
52262306a36Sopenharmony_ci *
52362306a36Sopenharmony_ci * @sbdev: client handle requesting the address.
52462306a36Sopenharmony_ci *
52562306a36Sopenharmony_ci * Return: zero if a logical address is valid or a new logical address
52662306a36Sopenharmony_ci * has been assigned. error code in case of error.
52762306a36Sopenharmony_ci */
52862306a36Sopenharmony_ciint slim_get_logical_addr(struct slim_device *sbdev)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	if (!sbdev->is_laddr_valid)
53162306a36Sopenharmony_ci		return slim_device_alloc_laddr(sbdev, false);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	return 0;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_get_logical_addr);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic void __exit slimbus_exit(void)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	bus_unregister(&slimbus_bus);
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_cimodule_exit(slimbus_exit);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int __init slimbus_init(void)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	return bus_register(&slimbus_bus);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_cipostcore_initcall(slimbus_init);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
55062306a36Sopenharmony_ciMODULE_DESCRIPTION("SLIMbus core");
551