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