18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011-2017, The Linux Foundation 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/errno.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/idr.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci#include <linux/slimbus.h> 158c2ecf20Sopenharmony_ci#include "slimbus.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic DEFINE_IDA(ctrl_ida); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic const struct slim_device_id *slim_match(const struct slim_device_id *id, 208c2ecf20Sopenharmony_ci const struct slim_device *sbdev) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci while (id->manf_id != 0 || id->prod_code != 0) { 238c2ecf20Sopenharmony_ci if (id->manf_id == sbdev->e_addr.manf_id && 248c2ecf20Sopenharmony_ci id->prod_code == sbdev->e_addr.prod_code && 258c2ecf20Sopenharmony_ci id->dev_index == sbdev->e_addr.dev_index && 268c2ecf20Sopenharmony_ci id->instance == sbdev->e_addr.instance) 278c2ecf20Sopenharmony_ci return id; 288c2ecf20Sopenharmony_ci id++; 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci return NULL; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int slim_device_match(struct device *dev, struct device_driver *drv) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct slim_device *sbdev = to_slim_device(dev); 368c2ecf20Sopenharmony_ci struct slim_driver *sbdrv = to_slim_driver(drv); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* Attempt an OF style match first */ 398c2ecf20Sopenharmony_ci if (of_driver_match_device(dev, drv)) 408c2ecf20Sopenharmony_ci return 1; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return !!slim_match(sbdrv->id_table, sbdev); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void slim_device_update_status(struct slim_device *sbdev, 468c2ecf20Sopenharmony_ci enum slim_device_status status) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct slim_driver *sbdrv; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (sbdev->status == status) 518c2ecf20Sopenharmony_ci return; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci sbdev->status = status; 548c2ecf20Sopenharmony_ci if (!sbdev->dev.driver) 558c2ecf20Sopenharmony_ci return; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci sbdrv = to_slim_driver(sbdev->dev.driver); 588c2ecf20Sopenharmony_ci if (sbdrv->device_status) 598c2ecf20Sopenharmony_ci sbdrv->device_status(sbdev, sbdev->status); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int slim_device_probe(struct device *dev) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct slim_device *sbdev = to_slim_device(dev); 658c2ecf20Sopenharmony_ci struct slim_driver *sbdrv = to_slim_driver(dev->driver); 668c2ecf20Sopenharmony_ci int ret; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci ret = sbdrv->probe(sbdev); 698c2ecf20Sopenharmony_ci if (ret) 708c2ecf20Sopenharmony_ci return ret; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* try getting the logical address after probe */ 738c2ecf20Sopenharmony_ci ret = slim_get_logical_addr(sbdev); 748c2ecf20Sopenharmony_ci if (!ret) { 758c2ecf20Sopenharmony_ci slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_UP); 768c2ecf20Sopenharmony_ci } else { 778c2ecf20Sopenharmony_ci dev_err(&sbdev->dev, "Failed to get logical address\n"); 788c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int slim_device_remove(struct device *dev) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct slim_device *sbdev = to_slim_device(dev); 878c2ecf20Sopenharmony_ci struct slim_driver *sbdrv; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (dev->driver) { 908c2ecf20Sopenharmony_ci sbdrv = to_slim_driver(dev->driver); 918c2ecf20Sopenharmony_ci if (sbdrv->remove) 928c2ecf20Sopenharmony_ci sbdrv->remove(sbdev); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int slim_device_uevent(struct device *dev, struct kobj_uevent_env *env) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct slim_device *sbdev = to_slim_device(dev); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return add_uevent_var(env, "MODALIAS=slim:%s", dev_name(&sbdev->dev)); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistruct bus_type slimbus_bus = { 1068c2ecf20Sopenharmony_ci .name = "slimbus", 1078c2ecf20Sopenharmony_ci .match = slim_device_match, 1088c2ecf20Sopenharmony_ci .probe = slim_device_probe, 1098c2ecf20Sopenharmony_ci .remove = slim_device_remove, 1108c2ecf20Sopenharmony_ci .uevent = slim_device_uevent, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slimbus_bus); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * __slim_driver_register() - Client driver registration with SLIMbus 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * @drv:Client driver to be associated with client-device. 1188c2ecf20Sopenharmony_ci * @owner: owning module/driver 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * This API will register the client driver with the SLIMbus 1218c2ecf20Sopenharmony_ci * It is called from the driver's module-init function. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ciint __slim_driver_register(struct slim_driver *drv, struct module *owner) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci /* ID table and probe are mandatory */ 1268c2ecf20Sopenharmony_ci if (!(drv->driver.of_match_table || drv->id_table) || !drv->probe) 1278c2ecf20Sopenharmony_ci return -EINVAL; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci drv->driver.bus = &slimbus_bus; 1308c2ecf20Sopenharmony_ci drv->driver.owner = owner; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return driver_register(&drv->driver); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__slim_driver_register); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * slim_driver_unregister() - Undo effect of slim_driver_register 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * @drv: Client driver to be unregistered 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_civoid slim_driver_unregister(struct slim_driver *drv) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci driver_unregister(&drv->driver); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_driver_unregister); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void slim_dev_release(struct device *dev) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct slim_device *sbdev = to_slim_device(dev); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci kfree(sbdev); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int slim_add_device(struct slim_controller *ctrl, 1558c2ecf20Sopenharmony_ci struct slim_device *sbdev, 1568c2ecf20Sopenharmony_ci struct device_node *node) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci sbdev->dev.bus = &slimbus_bus; 1598c2ecf20Sopenharmony_ci sbdev->dev.parent = ctrl->dev; 1608c2ecf20Sopenharmony_ci sbdev->dev.release = slim_dev_release; 1618c2ecf20Sopenharmony_ci sbdev->dev.driver = NULL; 1628c2ecf20Sopenharmony_ci sbdev->ctrl = ctrl; 1638c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sbdev->stream_list); 1648c2ecf20Sopenharmony_ci spin_lock_init(&sbdev->stream_list_lock); 1658c2ecf20Sopenharmony_ci sbdev->dev.of_node = of_node_get(node); 1668c2ecf20Sopenharmony_ci sbdev->dev.fwnode = of_fwnode_handle(node); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci dev_set_name(&sbdev->dev, "%x:%x:%x:%x", 1698c2ecf20Sopenharmony_ci sbdev->e_addr.manf_id, 1708c2ecf20Sopenharmony_ci sbdev->e_addr.prod_code, 1718c2ecf20Sopenharmony_ci sbdev->e_addr.dev_index, 1728c2ecf20Sopenharmony_ci sbdev->e_addr.instance); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return device_register(&sbdev->dev); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic struct slim_device *slim_alloc_device(struct slim_controller *ctrl, 1788c2ecf20Sopenharmony_ci struct slim_eaddr *eaddr, 1798c2ecf20Sopenharmony_ci struct device_node *node) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct slim_device *sbdev; 1828c2ecf20Sopenharmony_ci int ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci sbdev = kzalloc(sizeof(*sbdev), GFP_KERNEL); 1858c2ecf20Sopenharmony_ci if (!sbdev) 1868c2ecf20Sopenharmony_ci return NULL; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci sbdev->e_addr = *eaddr; 1898c2ecf20Sopenharmony_ci ret = slim_add_device(ctrl, sbdev, node); 1908c2ecf20Sopenharmony_ci if (ret) { 1918c2ecf20Sopenharmony_ci put_device(&sbdev->dev); 1928c2ecf20Sopenharmony_ci return NULL; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return sbdev; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void of_register_slim_devices(struct slim_controller *ctrl) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct device *dev = ctrl->dev; 2018c2ecf20Sopenharmony_ci struct device_node *node; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (!ctrl->dev->of_node) 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for_each_child_of_node(ctrl->dev->of_node, node) { 2078c2ecf20Sopenharmony_ci struct slim_device *sbdev; 2088c2ecf20Sopenharmony_ci struct slim_eaddr e_addr; 2098c2ecf20Sopenharmony_ci const char *compat = NULL; 2108c2ecf20Sopenharmony_ci int reg[2], ret; 2118c2ecf20Sopenharmony_ci int manf_id, prod_code; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci compat = of_get_property(node, "compatible", NULL); 2148c2ecf20Sopenharmony_ci if (!compat) 2158c2ecf20Sopenharmony_ci continue; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ret = sscanf(compat, "slim%x,%x", &manf_id, &prod_code); 2188c2ecf20Sopenharmony_ci if (ret != 2) { 2198c2ecf20Sopenharmony_ci dev_err(dev, "Manf ID & Product code not found %s\n", 2208c2ecf20Sopenharmony_ci compat); 2218c2ecf20Sopenharmony_ci continue; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(node, "reg", reg, 2); 2258c2ecf20Sopenharmony_ci if (ret) { 2268c2ecf20Sopenharmony_ci dev_err(dev, "Device and Instance id not found:%d\n", 2278c2ecf20Sopenharmony_ci ret); 2288c2ecf20Sopenharmony_ci continue; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci e_addr.dev_index = reg[0]; 2328c2ecf20Sopenharmony_ci e_addr.instance = reg[1]; 2338c2ecf20Sopenharmony_ci e_addr.manf_id = manf_id; 2348c2ecf20Sopenharmony_ci e_addr.prod_code = prod_code; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci sbdev = slim_alloc_device(ctrl, &e_addr, node); 2378c2ecf20Sopenharmony_ci if (!sbdev) 2388c2ecf20Sopenharmony_ci continue; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * slim_register_controller() - Controller bring-up and registration. 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * @ctrl: Controller to be registered. 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * A controller is registered with the framework using this API. 2488c2ecf20Sopenharmony_ci * If devices on a controller were registered before controller, 2498c2ecf20Sopenharmony_ci * this will make sure that they get probed when controller is up 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ciint slim_register_controller(struct slim_controller *ctrl) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int id; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); 2568c2ecf20Sopenharmony_ci if (id < 0) 2578c2ecf20Sopenharmony_ci return id; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ctrl->id = id; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (!ctrl->min_cg) 2628c2ecf20Sopenharmony_ci ctrl->min_cg = SLIM_MIN_CLK_GEAR; 2638c2ecf20Sopenharmony_ci if (!ctrl->max_cg) 2648c2ecf20Sopenharmony_ci ctrl->max_cg = SLIM_MAX_CLK_GEAR; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci ida_init(&ctrl->laddr_ida); 2678c2ecf20Sopenharmony_ci idr_init(&ctrl->tid_idr); 2688c2ecf20Sopenharmony_ci mutex_init(&ctrl->lock); 2698c2ecf20Sopenharmony_ci mutex_init(&ctrl->sched.m_reconf); 2708c2ecf20Sopenharmony_ci init_completion(&ctrl->sched.pause_comp); 2718c2ecf20Sopenharmony_ci spin_lock_init(&ctrl->txn_lock); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci dev_dbg(ctrl->dev, "Bus [%s] registered:dev:%p\n", 2748c2ecf20Sopenharmony_ci ctrl->name, ctrl->dev); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci of_register_slim_devices(ctrl); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_register_controller); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/* slim_remove_device: Remove the effect of slim_add_device() */ 2838c2ecf20Sopenharmony_cistatic void slim_remove_device(struct slim_device *sbdev) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci of_node_put(sbdev->dev.of_node); 2868c2ecf20Sopenharmony_ci device_unregister(&sbdev->dev); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int slim_ctrl_remove_device(struct device *dev, void *null) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci slim_remove_device(to_slim_device(dev)); 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/** 2968c2ecf20Sopenharmony_ci * slim_unregister_controller() - Controller tear-down. 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * @ctrl: Controller to tear-down. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ciint slim_unregister_controller(struct slim_controller *ctrl) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci /* Remove all clients */ 3038c2ecf20Sopenharmony_ci device_for_each_child(ctrl->dev, NULL, slim_ctrl_remove_device); 3048c2ecf20Sopenharmony_ci ida_simple_remove(&ctrl_ida, ctrl->id); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_unregister_controller); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/** 3118c2ecf20Sopenharmony_ci * slim_report_absent() - Controller calls this function when a device 3128c2ecf20Sopenharmony_ci * reports absent, OR when the device cannot be communicated with 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * @sbdev: Device that cannot be reached, or sent report absent 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_civoid slim_report_absent(struct slim_device *sbdev) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct slim_controller *ctrl = sbdev->ctrl; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (!ctrl) 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* invalidate logical addresses */ 3248c2ecf20Sopenharmony_ci mutex_lock(&ctrl->lock); 3258c2ecf20Sopenharmony_ci sbdev->is_laddr_valid = false; 3268c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->lock); 3278c2ecf20Sopenharmony_ci if (!ctrl->get_laddr) 3288c2ecf20Sopenharmony_ci ida_simple_remove(&ctrl->laddr_ida, sbdev->laddr); 3298c2ecf20Sopenharmony_ci slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_DOWN); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_report_absent); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci return (a->manf_id == b->manf_id && 3368c2ecf20Sopenharmony_ci a->prod_code == b->prod_code && 3378c2ecf20Sopenharmony_ci a->dev_index == b->dev_index && 3388c2ecf20Sopenharmony_ci a->instance == b->instance); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int slim_match_dev(struct device *dev, void *data) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct slim_eaddr *e_addr = data; 3448c2ecf20Sopenharmony_ci struct slim_device *sbdev = to_slim_device(dev); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return slim_eaddr_equal(&sbdev->e_addr, e_addr); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic struct slim_device *find_slim_device(struct slim_controller *ctrl, 3508c2ecf20Sopenharmony_ci struct slim_eaddr *eaddr) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct slim_device *sbdev; 3538c2ecf20Sopenharmony_ci struct device *dev; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci dev = device_find_child(ctrl->dev, eaddr, slim_match_dev); 3568c2ecf20Sopenharmony_ci if (dev) { 3578c2ecf20Sopenharmony_ci sbdev = to_slim_device(dev); 3588c2ecf20Sopenharmony_ci return sbdev; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return NULL; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/** 3658c2ecf20Sopenharmony_ci * slim_get_device() - get handle to a device. 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci * @ctrl: Controller on which this device will be added/queried 3688c2ecf20Sopenharmony_ci * @e_addr: Enumeration address of the device to be queried 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci * Return: pointer to a device if it has already reported. Creates a new 3718c2ecf20Sopenharmony_ci * device and returns pointer to it if the device has not yet enumerated. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_cistruct slim_device *slim_get_device(struct slim_controller *ctrl, 3748c2ecf20Sopenharmony_ci struct slim_eaddr *e_addr) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct slim_device *sbdev; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci sbdev = find_slim_device(ctrl, e_addr); 3798c2ecf20Sopenharmony_ci if (!sbdev) { 3808c2ecf20Sopenharmony_ci sbdev = slim_alloc_device(ctrl, e_addr, NULL); 3818c2ecf20Sopenharmony_ci if (!sbdev) 3828c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return sbdev; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_get_device); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int of_slim_match_dev(struct device *dev, void *data) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct device_node *np = data; 3928c2ecf20Sopenharmony_ci struct slim_device *sbdev = to_slim_device(dev); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return (sbdev->dev.of_node == np); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic struct slim_device *of_find_slim_device(struct slim_controller *ctrl, 3988c2ecf20Sopenharmony_ci struct device_node *np) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct slim_device *sbdev; 4018c2ecf20Sopenharmony_ci struct device *dev; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci dev = device_find_child(ctrl->dev, np, of_slim_match_dev); 4048c2ecf20Sopenharmony_ci if (dev) { 4058c2ecf20Sopenharmony_ci sbdev = to_slim_device(dev); 4068c2ecf20Sopenharmony_ci return sbdev; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return NULL; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/** 4138c2ecf20Sopenharmony_ci * of_slim_get_device() - get handle to a device using dt node. 4148c2ecf20Sopenharmony_ci * 4158c2ecf20Sopenharmony_ci * @ctrl: Controller on which this device will be added/queried 4168c2ecf20Sopenharmony_ci * @np: node pointer to device 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * Return: pointer to a device if it has already reported. Creates a new 4198c2ecf20Sopenharmony_ci * device and returns pointer to it if the device has not yet enumerated. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_cistruct slim_device *of_slim_get_device(struct slim_controller *ctrl, 4228c2ecf20Sopenharmony_ci struct device_node *np) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci return of_find_slim_device(ctrl, np); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_slim_get_device); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int slim_device_alloc_laddr(struct slim_device *sbdev, 4298c2ecf20Sopenharmony_ci bool report_present) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct slim_controller *ctrl = sbdev->ctrl; 4328c2ecf20Sopenharmony_ci u8 laddr; 4338c2ecf20Sopenharmony_ci int ret; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci mutex_lock(&ctrl->lock); 4368c2ecf20Sopenharmony_ci if (ctrl->get_laddr) { 4378c2ecf20Sopenharmony_ci ret = ctrl->get_laddr(ctrl, &sbdev->e_addr, &laddr); 4388c2ecf20Sopenharmony_ci if (ret < 0) 4398c2ecf20Sopenharmony_ci goto err; 4408c2ecf20Sopenharmony_ci } else if (report_present) { 4418c2ecf20Sopenharmony_ci ret = ida_simple_get(&ctrl->laddr_ida, 4428c2ecf20Sopenharmony_ci 0, SLIM_LA_MANAGER - 1, GFP_KERNEL); 4438c2ecf20Sopenharmony_ci if (ret < 0) 4448c2ecf20Sopenharmony_ci goto err; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci laddr = ret; 4478c2ecf20Sopenharmony_ci } else { 4488c2ecf20Sopenharmony_ci ret = -EINVAL; 4498c2ecf20Sopenharmony_ci goto err; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (ctrl->set_laddr) { 4538c2ecf20Sopenharmony_ci ret = ctrl->set_laddr(ctrl, &sbdev->e_addr, laddr); 4548c2ecf20Sopenharmony_ci if (ret) { 4558c2ecf20Sopenharmony_ci ret = -EINVAL; 4568c2ecf20Sopenharmony_ci goto err; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci sbdev->laddr = laddr; 4618c2ecf20Sopenharmony_ci sbdev->is_laddr_valid = true; 4628c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->lock); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_UP); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci dev_dbg(ctrl->dev, "setting slimbus l-addr:%x, ea:%x,%x,%x,%x\n", 4678c2ecf20Sopenharmony_ci laddr, sbdev->e_addr.manf_id, sbdev->e_addr.prod_code, 4688c2ecf20Sopenharmony_ci sbdev->e_addr.dev_index, sbdev->e_addr.instance); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cierr: 4738c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->lock); 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/** 4798c2ecf20Sopenharmony_ci * slim_device_report_present() - Report enumerated device. 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * @ctrl: Controller with which device is enumerated. 4828c2ecf20Sopenharmony_ci * @e_addr: Enumeration address of the device. 4838c2ecf20Sopenharmony_ci * @laddr: Return logical address (if valid flag is false) 4848c2ecf20Sopenharmony_ci * 4858c2ecf20Sopenharmony_ci * Called by controller in response to REPORT_PRESENT. Framework will assign 4868c2ecf20Sopenharmony_ci * a logical address to this enumeration address. 4878c2ecf20Sopenharmony_ci * Function returns -EXFULL to indicate that all logical addresses are already 4888c2ecf20Sopenharmony_ci * taken. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ciint slim_device_report_present(struct slim_controller *ctrl, 4918c2ecf20Sopenharmony_ci struct slim_eaddr *e_addr, u8 *laddr) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct slim_device *sbdev; 4948c2ecf20Sopenharmony_ci int ret; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(ctrl->dev); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) { 4998c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n", 5008c2ecf20Sopenharmony_ci ctrl->sched.clk_state, ret); 5018c2ecf20Sopenharmony_ci goto slimbus_not_active; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci sbdev = slim_get_device(ctrl, e_addr); 5058c2ecf20Sopenharmony_ci if (IS_ERR(sbdev)) 5068c2ecf20Sopenharmony_ci return -ENODEV; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (sbdev->is_laddr_valid) { 5098c2ecf20Sopenharmony_ci *laddr = sbdev->laddr; 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci ret = slim_device_alloc_laddr(sbdev, true); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cislimbus_not_active: 5168c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(ctrl->dev); 5178c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(ctrl->dev); 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_device_report_present); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/** 5238c2ecf20Sopenharmony_ci * slim_get_logical_addr() - get/allocate logical address of a SLIMbus device. 5248c2ecf20Sopenharmony_ci * 5258c2ecf20Sopenharmony_ci * @sbdev: client handle requesting the address. 5268c2ecf20Sopenharmony_ci * 5278c2ecf20Sopenharmony_ci * Return: zero if a logical address is valid or a new logical address 5288c2ecf20Sopenharmony_ci * has been assigned. error code in case of error. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ciint slim_get_logical_addr(struct slim_device *sbdev) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci if (!sbdev->is_laddr_valid) 5338c2ecf20Sopenharmony_ci return slim_device_alloc_laddr(sbdev, false); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_get_logical_addr); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void __exit slimbus_exit(void) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci bus_unregister(&slimbus_bus); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_cimodule_exit(slimbus_exit); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int __init slimbus_init(void) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci return bus_register(&slimbus_bus); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_cipostcore_initcall(slimbus_init); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SLIMbus core"); 553