18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Cadence Design Systems Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@bootlin.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/atomic.h> 98c2ecf20Sopenharmony_ci#include <linux/bug.h> 108c2ecf20Sopenharmony_ci#include <linux/completion.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/mutex.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "internals.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/** 188c2ecf20Sopenharmony_ci * i3c_device_do_priv_xfers() - do I3C SDR private transfers directed to a 198c2ecf20Sopenharmony_ci * specific device 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * @dev: device with which the transfers should be done 228c2ecf20Sopenharmony_ci * @xfers: array of transfers 238c2ecf20Sopenharmony_ci * @nxfers: number of transfers 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Initiate one or several private SDR transfers with @dev. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * This function can sleep and thus cannot be called in atomic context. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ciint i3c_device_do_priv_xfers(struct i3c_device *dev, 328c2ecf20Sopenharmony_ci struct i3c_priv_xfer *xfers, 338c2ecf20Sopenharmony_ci int nxfers) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci int ret, i; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (nxfers < 1) 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci for (i = 0; i < nxfers; i++) { 418c2ecf20Sopenharmony_ci if (!xfers[i].len || !xfers[i].data.in) 428c2ecf20Sopenharmony_ci return -EINVAL; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 468c2ecf20Sopenharmony_ci ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers); 478c2ecf20Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return ret; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_do_priv_xfers); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/** 548c2ecf20Sopenharmony_ci * i3c_device_get_info() - get I3C device information 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * @dev: device we want information on 578c2ecf20Sopenharmony_ci * @info: the information object to fill in 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * Retrieve I3C dev info. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_civoid i3c_device_get_info(struct i3c_device *dev, 628c2ecf20Sopenharmony_ci struct i3c_device_info *info) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci if (!info) 658c2ecf20Sopenharmony_ci return; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 688c2ecf20Sopenharmony_ci if (dev->desc) 698c2ecf20Sopenharmony_ci *info = dev->desc->info; 708c2ecf20Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_get_info); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/** 758c2ecf20Sopenharmony_ci * i3c_device_disable_ibi() - Disable IBIs coming from a specific device 768c2ecf20Sopenharmony_ci * @dev: device on which IBIs should be disabled 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * This function disable IBIs coming from a specific device and wait for 798c2ecf20Sopenharmony_ci * all pending IBIs to be processed. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ciint i3c_device_disable_ibi(struct i3c_device *dev) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int ret = -ENOENT; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 888c2ecf20Sopenharmony_ci if (dev->desc) { 898c2ecf20Sopenharmony_ci mutex_lock(&dev->desc->ibi_lock); 908c2ecf20Sopenharmony_ci ret = i3c_dev_disable_ibi_locked(dev->desc); 918c2ecf20Sopenharmony_ci mutex_unlock(&dev->desc->ibi_lock); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_disable_ibi); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/** 1008c2ecf20Sopenharmony_ci * i3c_device_enable_ibi() - Enable IBIs coming from a specific device 1018c2ecf20Sopenharmony_ci * @dev: device on which IBIs should be enabled 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * This function enable IBIs coming from a specific device and wait for 1048c2ecf20Sopenharmony_ci * all pending IBIs to be processed. This should be called on a device 1058c2ecf20Sopenharmony_ci * where i3c_device_request_ibi() has succeeded. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * Note that IBIs from this device might be received before this function 1088c2ecf20Sopenharmony_ci * returns to its caller. 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ciint i3c_device_enable_ibi(struct i3c_device *dev) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int ret = -ENOENT; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 1178c2ecf20Sopenharmony_ci if (dev->desc) { 1188c2ecf20Sopenharmony_ci mutex_lock(&dev->desc->ibi_lock); 1198c2ecf20Sopenharmony_ci ret = i3c_dev_enable_ibi_locked(dev->desc); 1208c2ecf20Sopenharmony_ci mutex_unlock(&dev->desc->ibi_lock); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_enable_ibi); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/** 1298c2ecf20Sopenharmony_ci * i3c_device_request_ibi() - Request an IBI 1308c2ecf20Sopenharmony_ci * @dev: device for which we should enable IBIs 1318c2ecf20Sopenharmony_ci * @req: setup requested for this IBI 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * This function is responsible for pre-allocating all resources needed to 1348c2ecf20Sopenharmony_ci * process IBIs coming from @dev. When this function returns, the IBI is not 1358c2ecf20Sopenharmony_ci * enabled until i3c_device_enable_ibi() is called. 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ciint i3c_device_request_ibi(struct i3c_device *dev, 1408c2ecf20Sopenharmony_ci const struct i3c_ibi_setup *req) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int ret = -ENOENT; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!req->handler || !req->num_slots) 1458c2ecf20Sopenharmony_ci return -EINVAL; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 1488c2ecf20Sopenharmony_ci if (dev->desc) { 1498c2ecf20Sopenharmony_ci mutex_lock(&dev->desc->ibi_lock); 1508c2ecf20Sopenharmony_ci ret = i3c_dev_request_ibi_locked(dev->desc, req); 1518c2ecf20Sopenharmony_ci mutex_unlock(&dev->desc->ibi_lock); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_request_ibi); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * i3c_device_free_ibi() - Free all resources needed for IBI handling 1618c2ecf20Sopenharmony_ci * @dev: device on which you want to release IBI resources 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * This function is responsible for de-allocating resources previously 1648c2ecf20Sopenharmony_ci * allocated by i3c_device_request_ibi(). It should be called after disabling 1658c2ecf20Sopenharmony_ci * IBIs with i3c_device_disable_ibi(). 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_civoid i3c_device_free_ibi(struct i3c_device *dev) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 1708c2ecf20Sopenharmony_ci if (dev->desc) { 1718c2ecf20Sopenharmony_ci mutex_lock(&dev->desc->ibi_lock); 1728c2ecf20Sopenharmony_ci i3c_dev_free_ibi_locked(dev->desc); 1738c2ecf20Sopenharmony_ci mutex_unlock(&dev->desc->ibi_lock); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_free_ibi); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/** 1808c2ecf20Sopenharmony_ci * i3cdev_to_dev() - Returns the device embedded in @i3cdev 1818c2ecf20Sopenharmony_ci * @i3cdev: I3C device 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * Return: a pointer to a device object. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistruct device *i3cdev_to_dev(struct i3c_device *i3cdev) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci return &i3cdev->dev; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3cdev_to_dev); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/** 1928c2ecf20Sopenharmony_ci * dev_to_i3cdev() - Returns the I3C device containing @dev 1938c2ecf20Sopenharmony_ci * @dev: device object 1948c2ecf20Sopenharmony_ci * 1958c2ecf20Sopenharmony_ci * Return: a pointer to an I3C device object. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistruct i3c_device *dev_to_i3cdev(struct device *dev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return container_of(dev, struct i3c_device, dev); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_to_i3cdev); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/** 2048c2ecf20Sopenharmony_ci * i3c_device_match_id() - Returns the i3c_device_id entry matching @i3cdev 2058c2ecf20Sopenharmony_ci * @i3cdev: I3C device 2068c2ecf20Sopenharmony_ci * @id_table: I3C device match table 2078c2ecf20Sopenharmony_ci * 2088c2ecf20Sopenharmony_ci * Return: a pointer to an i3c_device_id object or NULL if there's no match. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ciconst struct i3c_device_id * 2118c2ecf20Sopenharmony_cii3c_device_match_id(struct i3c_device *i3cdev, 2128c2ecf20Sopenharmony_ci const struct i3c_device_id *id_table) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct i3c_device_info devinfo; 2158c2ecf20Sopenharmony_ci const struct i3c_device_id *id; 2168c2ecf20Sopenharmony_ci u16 manuf, part, ext_info; 2178c2ecf20Sopenharmony_ci bool rndpid; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci i3c_device_get_info(i3cdev, &devinfo); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci manuf = I3C_PID_MANUF_ID(devinfo.pid); 2228c2ecf20Sopenharmony_ci part = I3C_PID_PART_ID(devinfo.pid); 2238c2ecf20Sopenharmony_ci ext_info = I3C_PID_EXTRA_INFO(devinfo.pid); 2248c2ecf20Sopenharmony_ci rndpid = I3C_PID_RND_LOWER_32BITS(devinfo.pid); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci for (id = id_table; id->match_flags != 0; id++) { 2278c2ecf20Sopenharmony_ci if ((id->match_flags & I3C_MATCH_DCR) && 2288c2ecf20Sopenharmony_ci id->dcr != devinfo.dcr) 2298c2ecf20Sopenharmony_ci continue; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if ((id->match_flags & I3C_MATCH_MANUF) && 2328c2ecf20Sopenharmony_ci id->manuf_id != manuf) 2338c2ecf20Sopenharmony_ci continue; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if ((id->match_flags & I3C_MATCH_PART) && 2368c2ecf20Sopenharmony_ci (rndpid || id->part_id != part)) 2378c2ecf20Sopenharmony_ci continue; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if ((id->match_flags & I3C_MATCH_EXTRA_INFO) && 2408c2ecf20Sopenharmony_ci (rndpid || id->extra_info != ext_info)) 2418c2ecf20Sopenharmony_ci continue; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return id; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return NULL; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_match_id); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/** 2518c2ecf20Sopenharmony_ci * i3c_driver_register_with_owner() - register an I3C device driver 2528c2ecf20Sopenharmony_ci * 2538c2ecf20Sopenharmony_ci * @drv: driver to register 2548c2ecf20Sopenharmony_ci * @owner: module that owns this driver 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * Register @drv to the core. 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ciint i3c_driver_register_with_owner(struct i3c_driver *drv, struct module *owner) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci drv->driver.owner = owner; 2638c2ecf20Sopenharmony_ci drv->driver.bus = &i3c_bus_type; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return driver_register(&drv->driver); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_driver_register_with_owner); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/** 2708c2ecf20Sopenharmony_ci * i3c_driver_unregister() - unregister an I3C device driver 2718c2ecf20Sopenharmony_ci * 2728c2ecf20Sopenharmony_ci * @drv: driver to unregister 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * Unregister @drv. 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_civoid i3c_driver_unregister(struct i3c_driver *drv) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci driver_unregister(&drv->driver); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_driver_unregister); 281