162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2018 Cadence Design Systems Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@bootlin.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/atomic.h> 962306a36Sopenharmony_ci#include <linux/bug.h> 1062306a36Sopenharmony_ci#include <linux/completion.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/mutex.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "internals.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * i3c_device_do_priv_xfers() - do I3C SDR private transfers directed to a 1962306a36Sopenharmony_ci * specific device 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * @dev: device with which the transfers should be done 2262306a36Sopenharmony_ci * @xfers: array of transfers 2362306a36Sopenharmony_ci * @nxfers: number of transfers 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Initiate one or several private SDR transfers with @dev. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * This function can sleep and thus cannot be called in atomic context. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ciint i3c_device_do_priv_xfers(struct i3c_device *dev, 3262306a36Sopenharmony_ci struct i3c_priv_xfer *xfers, 3362306a36Sopenharmony_ci int nxfers) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int ret, i; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (nxfers < 1) 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci for (i = 0; i < nxfers; i++) { 4162306a36Sopenharmony_ci if (!xfers[i].len || !xfers[i].data.in) 4262306a36Sopenharmony_ci return -EINVAL; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 4662306a36Sopenharmony_ci ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers); 4762306a36Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return ret; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_do_priv_xfers); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** 5462306a36Sopenharmony_ci * i3c_device_do_setdasa() - do I3C dynamic address assignement with 5562306a36Sopenharmony_ci * static address 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * @dev: device with which the DAA should be done 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ciint i3c_device_do_setdasa(struct i3c_device *dev) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int ret; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 6662306a36Sopenharmony_ci ret = i3c_dev_setdasa_locked(dev->desc); 6762306a36Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return ret; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_do_setdasa); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/** 7462306a36Sopenharmony_ci * i3c_device_get_info() - get I3C device information 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * @dev: device we want information on 7762306a36Sopenharmony_ci * @info: the information object to fill in 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Retrieve I3C dev info. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_civoid i3c_device_get_info(const struct i3c_device *dev, 8262306a36Sopenharmony_ci struct i3c_device_info *info) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci if (!info) 8562306a36Sopenharmony_ci return; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 8862306a36Sopenharmony_ci if (dev->desc) 8962306a36Sopenharmony_ci *info = dev->desc->info; 9062306a36Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_get_info); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * i3c_device_disable_ibi() - Disable IBIs coming from a specific device 9662306a36Sopenharmony_ci * @dev: device on which IBIs should be disabled 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * This function disable IBIs coming from a specific device and wait for 9962306a36Sopenharmony_ci * all pending IBIs to be processed. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ciint i3c_device_disable_ibi(struct i3c_device *dev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int ret = -ENOENT; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 10862306a36Sopenharmony_ci if (dev->desc) { 10962306a36Sopenharmony_ci mutex_lock(&dev->desc->ibi_lock); 11062306a36Sopenharmony_ci ret = i3c_dev_disable_ibi_locked(dev->desc); 11162306a36Sopenharmony_ci mutex_unlock(&dev->desc->ibi_lock); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_disable_ibi); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * i3c_device_enable_ibi() - Enable IBIs coming from a specific device 12162306a36Sopenharmony_ci * @dev: device on which IBIs should be enabled 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * This function enable IBIs coming from a specific device and wait for 12462306a36Sopenharmony_ci * all pending IBIs to be processed. This should be called on a device 12562306a36Sopenharmony_ci * where i3c_device_request_ibi() has succeeded. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * Note that IBIs from this device might be received before this function 12862306a36Sopenharmony_ci * returns to its caller. 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ciint i3c_device_enable_ibi(struct i3c_device *dev) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int ret = -ENOENT; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 13762306a36Sopenharmony_ci if (dev->desc) { 13862306a36Sopenharmony_ci mutex_lock(&dev->desc->ibi_lock); 13962306a36Sopenharmony_ci ret = i3c_dev_enable_ibi_locked(dev->desc); 14062306a36Sopenharmony_ci mutex_unlock(&dev->desc->ibi_lock); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_enable_ibi); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/** 14962306a36Sopenharmony_ci * i3c_device_request_ibi() - Request an IBI 15062306a36Sopenharmony_ci * @dev: device for which we should enable IBIs 15162306a36Sopenharmony_ci * @req: setup requested for this IBI 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * This function is responsible for pre-allocating all resources needed to 15462306a36Sopenharmony_ci * process IBIs coming from @dev. When this function returns, the IBI is not 15562306a36Sopenharmony_ci * enabled until i3c_device_enable_ibi() is called. 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ciint i3c_device_request_ibi(struct i3c_device *dev, 16062306a36Sopenharmony_ci const struct i3c_ibi_setup *req) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci int ret = -ENOENT; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!req->handler || !req->num_slots) 16562306a36Sopenharmony_ci return -EINVAL; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 16862306a36Sopenharmony_ci if (dev->desc) { 16962306a36Sopenharmony_ci mutex_lock(&dev->desc->ibi_lock); 17062306a36Sopenharmony_ci ret = i3c_dev_request_ibi_locked(dev->desc, req); 17162306a36Sopenharmony_ci mutex_unlock(&dev->desc->ibi_lock); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return ret; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_request_ibi); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/** 18062306a36Sopenharmony_ci * i3c_device_free_ibi() - Free all resources needed for IBI handling 18162306a36Sopenharmony_ci * @dev: device on which you want to release IBI resources 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * This function is responsible for de-allocating resources previously 18462306a36Sopenharmony_ci * allocated by i3c_device_request_ibi(). It should be called after disabling 18562306a36Sopenharmony_ci * IBIs with i3c_device_disable_ibi(). 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_civoid i3c_device_free_ibi(struct i3c_device *dev) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci i3c_bus_normaluse_lock(dev->bus); 19062306a36Sopenharmony_ci if (dev->desc) { 19162306a36Sopenharmony_ci mutex_lock(&dev->desc->ibi_lock); 19262306a36Sopenharmony_ci i3c_dev_free_ibi_locked(dev->desc); 19362306a36Sopenharmony_ci mutex_unlock(&dev->desc->ibi_lock); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci i3c_bus_normaluse_unlock(dev->bus); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_free_ibi); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/** 20062306a36Sopenharmony_ci * i3cdev_to_dev() - Returns the device embedded in @i3cdev 20162306a36Sopenharmony_ci * @i3cdev: I3C device 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * Return: a pointer to a device object. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistruct device *i3cdev_to_dev(struct i3c_device *i3cdev) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci return &i3cdev->dev; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3cdev_to_dev); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/** 21262306a36Sopenharmony_ci * i3c_device_match_id() - Returns the i3c_device_id entry matching @i3cdev 21362306a36Sopenharmony_ci * @i3cdev: I3C device 21462306a36Sopenharmony_ci * @id_table: I3C device match table 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * Return: a pointer to an i3c_device_id object or NULL if there's no match. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ciconst struct i3c_device_id * 21962306a36Sopenharmony_cii3c_device_match_id(struct i3c_device *i3cdev, 22062306a36Sopenharmony_ci const struct i3c_device_id *id_table) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct i3c_device_info devinfo; 22362306a36Sopenharmony_ci const struct i3c_device_id *id; 22462306a36Sopenharmony_ci u16 manuf, part, ext_info; 22562306a36Sopenharmony_ci bool rndpid; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci i3c_device_get_info(i3cdev, &devinfo); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci manuf = I3C_PID_MANUF_ID(devinfo.pid); 23062306a36Sopenharmony_ci part = I3C_PID_PART_ID(devinfo.pid); 23162306a36Sopenharmony_ci ext_info = I3C_PID_EXTRA_INFO(devinfo.pid); 23262306a36Sopenharmony_ci rndpid = I3C_PID_RND_LOWER_32BITS(devinfo.pid); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci for (id = id_table; id->match_flags != 0; id++) { 23562306a36Sopenharmony_ci if ((id->match_flags & I3C_MATCH_DCR) && 23662306a36Sopenharmony_ci id->dcr != devinfo.dcr) 23762306a36Sopenharmony_ci continue; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if ((id->match_flags & I3C_MATCH_MANUF) && 24062306a36Sopenharmony_ci id->manuf_id != manuf) 24162306a36Sopenharmony_ci continue; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if ((id->match_flags & I3C_MATCH_PART) && 24462306a36Sopenharmony_ci (rndpid || id->part_id != part)) 24562306a36Sopenharmony_ci continue; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if ((id->match_flags & I3C_MATCH_EXTRA_INFO) && 24862306a36Sopenharmony_ci (rndpid || id->extra_info != ext_info)) 24962306a36Sopenharmony_ci continue; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return id; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return NULL; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_device_match_id); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/** 25962306a36Sopenharmony_ci * i3c_driver_register_with_owner() - register an I3C device driver 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * @drv: driver to register 26262306a36Sopenharmony_ci * @owner: module that owns this driver 26362306a36Sopenharmony_ci * 26462306a36Sopenharmony_ci * Register @drv to the core. 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ciint i3c_driver_register_with_owner(struct i3c_driver *drv, struct module *owner) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci drv->driver.owner = owner; 27162306a36Sopenharmony_ci drv->driver.bus = &i3c_bus_type; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (!drv->probe) { 27462306a36Sopenharmony_ci pr_err("Trying to register an i3c driver without probe callback\n"); 27562306a36Sopenharmony_ci return -EINVAL; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return driver_register(&drv->driver); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_driver_register_with_owner); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/** 28362306a36Sopenharmony_ci * i3c_driver_unregister() - unregister an I3C device driver 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * @drv: driver to unregister 28662306a36Sopenharmony_ci * 28762306a36Sopenharmony_ci * Unregister @drv. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_civoid i3c_driver_unregister(struct i3c_driver *drv) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci driver_unregister(&drv->driver); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i3c_driver_unregister); 294