blob: 722babcb05d75b794415aa469d1c2e9bb291bf7c [file] [log] [blame]
/*
* Google LWIS I2C BUS Manager
*
* Copyright (c) 2023 Google, LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME "-i2c-bus-manager: " fmt
#include "lwis_device.h"
#include "lwis_i2c_bus_manager.h"
#include "lwis_device_i2c.h"
#include "lwis_i2c_sched.h"
bool lwis_i2c_bus_manager_debug;
module_param(lwis_i2c_bus_manager_debug, bool, 0644);
/* Defines the global list of bus managers shared among various I2C devices
* Each manager would control the transfers on a single I2C bus */
static struct mutex i2c_bus_manager_list_lock;
static struct lwis_i2c_bus_manager_list i2c_bus_manager_list;
/*
* insert_bus_manager_id_in_list:
* Inserts the newly created instance of I2C bus manager in the list
*/
static int insert_bus_manager_id_in_list(struct lwis_i2c_bus_manager *i2c_bus_manager,
int i2c_bus_handle)
{
struct lwis_i2c_bus_manager_identifier *i2c_bus_manager_identifier_node = NULL;
if (!i2c_bus_manager)
return -EINVAL;
i2c_bus_manager_identifier_node =
kzalloc(sizeof(struct lwis_i2c_bus_manager_identifier), GFP_KERNEL);
if (!i2c_bus_manager_identifier_node) {
return -ENOMEM;
}
i2c_bus_manager_identifier_node->i2c_bus_manager_handle = i2c_bus_handle;
i2c_bus_manager_identifier_node->i2c_bus_manager = i2c_bus_manager;
INIT_LIST_HEAD(&i2c_bus_manager_identifier_node->i2c_bus_manager_list_node);
mutex_lock(&i2c_bus_manager_list_lock);
list_add_tail(&i2c_bus_manager_identifier_node->i2c_bus_manager_list_node,
&i2c_bus_manager_list.i2c_bus_manager_list_head);
mutex_unlock(&i2c_bus_manager_list_lock);
return 0;
}
/*
* delete_bus_manager_id_in_list:
* Deletes the newly created instance of I2C bus manager in the list
*/
static void delete_bus_manager_id_in_list(int i2c_bus_handle)
{
struct lwis_i2c_bus_manager_identifier *i2c_bus_manager_identifier_node = NULL;
struct list_head *i2c_bus_manager_list_node = NULL;
struct list_head *i2c_bus_manager_list_tmp_node = NULL;
mutex_lock(&i2c_bus_manager_list_lock);
list_for_each_safe (i2c_bus_manager_list_node, i2c_bus_manager_list_tmp_node,
&i2c_bus_manager_list.i2c_bus_manager_list_head) {
i2c_bus_manager_identifier_node = list_entry(i2c_bus_manager_list_node,
struct lwis_i2c_bus_manager_identifier,
i2c_bus_manager_list_node);
if (i2c_bus_manager_identifier_node->i2c_bus_manager_handle == i2c_bus_handle) {
list_del(&i2c_bus_manager_identifier_node->i2c_bus_manager_list_node);
kfree(i2c_bus_manager_identifier_node);
i2c_bus_manager_identifier_node = NULL;
break;
}
}
mutex_unlock(&i2c_bus_manager_list_lock);
}
/*
* find_i2c_bus_manager:
* Returns a valid I2C Bus Manager for a valid i2c_bus_handle.
* Returns NULL if the bus manager hasn't been created for this handle.
*/
static struct lwis_i2c_bus_manager *find_i2c_bus_manager(int i2c_bus_handle)
{
struct lwis_i2c_bus_manager *i2c_bus_manager = NULL;
struct list_head *i2c_bus_manager_list_node = NULL;
struct list_head *i2c_bus_manager_list_tmp_node = NULL;
struct lwis_i2c_bus_manager_identifier *i2c_bus_manager_identifier = NULL;
mutex_lock(&i2c_bus_manager_list_lock);
list_for_each_safe (i2c_bus_manager_list_node, i2c_bus_manager_list_tmp_node,
&i2c_bus_manager_list.i2c_bus_manager_list_head) {
i2c_bus_manager_identifier = list_entry(i2c_bus_manager_list_node,
struct lwis_i2c_bus_manager_identifier,
i2c_bus_manager_list_node);
if (i2c_bus_manager_identifier->i2c_bus_manager_handle == i2c_bus_handle) {
i2c_bus_manager = i2c_bus_manager_identifier->i2c_bus_manager;
break;
}
}
mutex_unlock(&i2c_bus_manager_list_lock);
return i2c_bus_manager;
}
/*
* create_i2c_kthread_workers:
* Creates I2C worker threads, one per bus
*/
static int create_i2c_kthread_workers(struct lwis_i2c_bus_manager *i2c_bus_manager,
struct lwis_device *lwis_dev)
{
char i2c_bus_thread_name[LWIS_MAX_NAME_STRING_LEN];
if (!i2c_bus_manager) {
dev_err(lwis_dev->dev, "lwis_create_kthread_workers: I2C Bus Manager is NULL\n");
return -ENODEV;
}
scnprintf(i2c_bus_thread_name, LWIS_MAX_NAME_STRING_LEN, "lwis_%s",
i2c_bus_manager->i2c_bus_name);
kthread_init_worker(&i2c_bus_manager->i2c_bus_worker);
i2c_bus_manager->i2c_bus_worker_thread = kthread_run(
kthread_worker_fn, &i2c_bus_manager->i2c_bus_worker, i2c_bus_thread_name);
if (IS_ERR(i2c_bus_manager->i2c_bus_worker_thread)) {
dev_err(lwis_dev->dev, "Creation of i2c_bus_worker_thread failed for bus %s\n",
i2c_bus_manager->i2c_bus_name);
return -EINVAL;
}
return 0;
}
/*
* check_i2c_thread_priority:
* Checks if the lwis device being connected has the same priority as other I2C threads
* Prints a warning message if there is a difference between the priorities
*/
static void check_i2c_thread_priority(struct lwis_i2c_bus_manager *i2c_bus_manager,
struct lwis_device *lwis_dev)
{
if (i2c_bus_manager->i2c_bus_thread_priority != lwis_dev->transaction_thread_priority) {
dev_warn(
lwis_dev->dev,
"I2C bus manager thread %s priority(%d) is not the same as device thread priority(%d)\n",
i2c_bus_manager->i2c_bus_name, i2c_bus_manager->i2c_bus_thread_priority,
lwis_dev->transaction_thread_priority);
}
}
/*
* set_i2c_thread_priority:
* Sets the priority for I2C threads
*/
static int set_i2c_thread_priority(struct lwis_i2c_bus_manager *i2c_bus_manager,
struct lwis_device *lwis_dev)
{
int ret = 0;
i2c_bus_manager->i2c_bus_thread_priority = lwis_dev->transaction_thread_priority;
if (i2c_bus_manager->i2c_bus_thread_priority != 0) {
ret = lwis_set_kthread_priority(lwis_dev, i2c_bus_manager->i2c_bus_worker_thread,
i2c_bus_manager->i2c_bus_thread_priority);
}
return ret;
}
/*
* is_valid_connected_device:
* Makes sure a valid client connected to this I2C executes the job on this manager
*/
static bool is_valid_connected_device(struct lwis_device *lwis_dev,
struct lwis_i2c_bus_manager *i2c_bus_manager)
{
struct lwis_i2c_connected_device *connected_i2c_device;
struct list_head *i2c_connected_device_node, *i2c_connected_device_tmp_node;
if ((lwis_dev == NULL) || (i2c_bus_manager == NULL)) {
return false;
}
list_for_each_safe (i2c_connected_device_node, i2c_connected_device_tmp_node,
&i2c_bus_manager->i2c_connected_devices) {
connected_i2c_device =
list_entry(i2c_connected_device_node, struct lwis_i2c_connected_device,
connected_device_node);
if (connected_i2c_device->connected_device == lwis_dev) {
return true;
}
}
return false;
}
/*
* set_i2c_bus_manager_name:
* Builds and sets the I2C Bus manager name
*/
static void set_i2c_bus_manager_name(struct lwis_i2c_bus_manager *i2c_bus_manager)
{
scnprintf(i2c_bus_manager->i2c_bus_name, LWIS_MAX_NAME_STRING_LEN, "I2C_Bus_%d",
i2c_bus_manager->i2c_bus_id);
}
/*
* destroy_i2c_bus_manager:
* Destroys this instance of the I2C bus manager
*/
static void destroy_i2c_bus_manager(struct lwis_i2c_bus_manager *i2c_bus_manager,
struct lwis_device *lwis_dev)
{
int i = 0;
if (!i2c_bus_manager) {
return;
}
dev_info(lwis_dev->dev, "Destroying I2C Bus Manager: %s\n", i2c_bus_manager->i2c_bus_name);
mutex_lock(&i2c_bus_manager->i2c_process_queue_lock);
for (i = 0; i < I2C_MAX_PRIORITY_LEVELS; i++) {
lwis_i2c_process_request_queue_destroy(&i2c_bus_manager->i2c_bus_process_queue[i]);
}
mutex_unlock(&i2c_bus_manager->i2c_process_queue_lock);
/* Delete the bus manager instance from the list */
delete_bus_manager_id_in_list(i2c_bus_manager->i2c_bus_id);
/* Free the bus manager */
kfree(i2c_bus_manager);
i2c_bus_manager = NULL;
}
/*
* connect_i2c_bus_manager:
* Connects a lwis device to this instance of the I2C bus manager.
*/
static int connect_i2c_bus_manager(struct lwis_i2c_bus_manager *i2c_bus_manager,
struct lwis_device *lwis_dev)
{
int ret = 0;
struct lwis_i2c_connected_device *connected_i2c_device;
if ((!lwis_dev) || (!i2c_bus_manager)) {
pr_err("Null lwis device or bus manager\n");
return -EINVAL;
}
if (!lwis_check_device_type(lwis_dev, DEVICE_TYPE_I2C)) {
dev_err(lwis_dev->dev,
"Failed trying to connect non I2C device to a I2C bus manager\n");
return -EINVAL;
}
connected_i2c_device = kzalloc(sizeof(struct lwis_i2c_connected_device), GFP_KERNEL);
if (!connected_i2c_device) {
return -ENOMEM;
}
connected_i2c_device->connected_device = lwis_dev;
INIT_LIST_HEAD(&connected_i2c_device->connected_device_node);
list_add_tail(&connected_i2c_device->connected_device_node,
&i2c_bus_manager->i2c_connected_devices);
i2c_bus_manager->number_of_connected_devices++;
return ret;
}
static bool i2c_device_priority_is_valid(int device_priority)
{
if ((device_priority >= I2C_DEVICE_HIGH_PRIORITY) &&
(device_priority <= I2C_DEVICE_LOW_PRIORITY)) {
return true;
}
return false;
}
/*
* lwis_i2c_bus_manager_process_worker_queue:
* Function to be called by i2c bus manager worker thread to
* pick the next I2C client that is scheduled for transfer.
* The process queue will be processed in order of I2C
* device priority.
*/
void lwis_i2c_bus_manager_process_worker_queue(struct lwis_client *client)
{
/* Get the correct I2C Bus manager to process it's queue */
struct lwis_device *lwis_dev = NULL;
struct lwis_i2c_bus_manager *i2c_bus_manager = NULL;
int i = 0;
/* The transfers will be processed in fifo order */
struct lwis_client *client_to_process = NULL;
struct lwis_device *lwis_dev_to_process = NULL;
struct lwis_i2c_process_queue *process_queue = NULL;
struct lwis_i2c_process_request *process_request = NULL;
struct list_head *i2c_client_node, *i2c_client_tmp_node;
lwis_dev = client->lwis_dev;
i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev);
if (lwis_i2c_bus_manager_debug) {
dev_info(lwis_dev->dev, "%s scheduled by %s\n", i2c_bus_manager->i2c_bus_name,
lwis_dev->name);
}
if (!i2c_bus_manager) {
dev_err(lwis_dev->dev, "I2C Bus Manager is null\n");
return;
}
mutex_lock(&i2c_bus_manager->i2c_process_queue_lock);
for (i = 0; i < I2C_MAX_PRIORITY_LEVELS; i++) {
process_queue = &i2c_bus_manager->i2c_bus_process_queue[i];
list_for_each_safe (i2c_client_node, i2c_client_tmp_node, &process_queue->head) {
if (lwis_i2c_bus_manager_debug) {
dev_info(lwis_dev->dev,
"Process request nodes for %s: cur %p tmp %p\n",
i2c_bus_manager->i2c_bus_name, i2c_client_node,
i2c_client_tmp_node);
}
process_request = list_entry(i2c_client_node,
struct lwis_i2c_process_request, request_node);
if (!process_request) {
dev_err(lwis_dev->dev, "I2C Bus Worker process_request is null\n");
break;
}
client_to_process = process_request->requesting_client;
if (!client_to_process) {
dev_err(lwis_dev->dev,
"I2C Bus Worker client_to_process is null\n");
break;
}
lwis_dev_to_process = client_to_process->lwis_dev;
if (!lwis_dev_to_process) {
dev_err(lwis_dev->dev,
"I2C Bus Worker lwis_dev_to_process is null\n");
break;
}
if (lwis_i2c_bus_manager_debug) {
dev_info(lwis_dev_to_process->dev, "Processing client start %s\n",
lwis_dev_to_process->name);
}
if (is_valid_connected_device(lwis_dev_to_process, i2c_bus_manager)) {
lwis_process_transactions_in_queue(client_to_process);
lwis_process_periodic_io_in_queue(client_to_process);
}
if (lwis_i2c_bus_manager_debug) {
dev_info(lwis_dev_to_process->dev, "Processing client end %s\n",
lwis_dev_to_process->name);
}
}
}
mutex_unlock(&i2c_bus_manager->i2c_process_queue_lock);
}
/*
* lwis_i2c_bus_manager_create:
* Creates a new instance of I2C bus manager
*/
int lwis_i2c_bus_manager_create(struct lwis_i2c_device *i2c_dev)
{
int ret = 0;
int i = 0;
struct lwis_i2c_bus_manager *i2c_bus_manager = NULL;
struct lwis_device *i2c_base_device = &i2c_dev->base_dev;
if (!lwis_check_device_type(i2c_base_device, DEVICE_TYPE_I2C)) {
return 0;
}
i2c_bus_manager = find_i2c_bus_manager(i2c_dev->adapter->nr);
if (!i2c_bus_manager) {
/* Allocate memory for I2C Bus Manager */
i2c_bus_manager = kzalloc(sizeof(struct lwis_i2c_bus_manager), GFP_KERNEL);
if (!i2c_bus_manager) {
return -ENOMEM;
}
i2c_bus_manager->i2c_bus_id = i2c_dev->adapter->nr;
set_i2c_bus_manager_name(i2c_bus_manager);
/* Mutex and Lock initializations */
mutex_init(&i2c_bus_manager->i2c_bus_lock);
mutex_init(&i2c_bus_manager->i2c_process_queue_lock);
/* List initializations */
INIT_LIST_HEAD(&i2c_bus_manager->i2c_connected_devices);
/* Create a I2C transfer process queue */
for (i = 0; i < I2C_MAX_PRIORITY_LEVELS; i++) {
lwis_i2c_process_request_queue_initialize(
&i2c_bus_manager->i2c_bus_process_queue[i]);
}
/* Insert this instance of bus manager in the bus manager list */
ret = insert_bus_manager_id_in_list(i2c_bus_manager, i2c_dev->adapter->nr);
if (ret < 0) {
goto error_creating_i2c_bus_manager;
}
/* Create worker thread to serve this bus manager */
ret = create_i2c_kthread_workers(i2c_bus_manager, i2c_base_device);
if (ret < 0) {
goto error_creating_i2c_bus_manager;
}
/* Set priority for the worker threads */
ret = set_i2c_thread_priority(i2c_bus_manager, i2c_base_device);
if (ret < 0) {
goto error_creating_i2c_bus_manager;
}
}
/* Check the current device's thread priority with respect to the bus priority */
check_i2c_thread_priority(i2c_bus_manager, i2c_base_device);
/* Connect this lwis device to the I2C Bus manager found/created */
ret = connect_i2c_bus_manager(i2c_bus_manager, i2c_base_device);
if (ret < 0) {
goto error_creating_i2c_bus_manager;
}
dev_info(i2c_base_device->dev,
"I2C Bus Manager: %s Connected Device: %s Connected device count: %d\n",
i2c_bus_manager->i2c_bus_name, i2c_base_device->name,
i2c_bus_manager->number_of_connected_devices);
i2c_dev->i2c_bus_manager = i2c_bus_manager;
return ret;
error_creating_i2c_bus_manager:
dev_err(i2c_base_device->dev, "Error creating I2C Bus Manager\n");
if (i2c_bus_manager) {
kfree(i2c_bus_manager);
i2c_bus_manager = NULL;
}
return -EINVAL;
}
/*
* lwis_i2c_bus_manager_disconnect:
* Disconnects a lwis device from this instance of the I2C bus manager.
* Doesn't destroy the instance of I2C bus manager
*/
void lwis_i2c_bus_manager_disconnect(struct lwis_device *lwis_dev)
{
struct lwis_i2c_bus_manager *i2c_bus_manager;
struct lwis_i2c_connected_device *connected_i2c_device;
struct list_head *i2c_connected_device_node, *i2c_connected_device_tmp_node;
struct lwis_i2c_device *i2c_dev = NULL;
i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev);
if (!i2c_bus_manager) {
return;
}
list_for_each_safe (i2c_connected_device_node, i2c_connected_device_tmp_node,
&i2c_bus_manager->i2c_connected_devices) {
connected_i2c_device =
list_entry(i2c_connected_device_node, struct lwis_i2c_connected_device,
connected_device_node);
/* Reset the bus manager pointer for this i2c device */
i2c_dev = container_of(lwis_dev, struct lwis_i2c_device, base_dev);
i2c_dev->i2c_bus_manager = NULL;
if (connected_i2c_device->connected_device == lwis_dev) {
list_del(&connected_i2c_device->connected_device_node);
kfree(connected_i2c_device);
connected_i2c_device = NULL;
--i2c_bus_manager->number_of_connected_devices;
/* Destroy the bus manager instance if there
* are no more I2C devices connected to it
*/
if (i2c_bus_manager->number_of_connected_devices == 0) {
destroy_i2c_bus_manager(i2c_bus_manager, lwis_dev);
}
return;
}
}
}
/* lwis_i2c_bus_manager_lock_i2c_bus:
* Locks the I2C bus for a given I2C Lwis Device
*/
void lwis_i2c_bus_manager_lock_i2c_bus(struct lwis_device *lwis_dev)
{
struct lwis_i2c_bus_manager *i2c_bus_manager = NULL;
i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev);
if (i2c_bus_manager) {
mutex_lock(&i2c_bus_manager->i2c_bus_lock);
if (lwis_i2c_bus_manager_debug) {
dev_info(lwis_dev->dev, "%s lock\n", i2c_bus_manager->i2c_bus_name);
}
}
}
/* lwis_i2c_bus_manager_unlock_i2c_bus:
* Unlocks the I2C bus for a given I2C Lwis Device
*/
void lwis_i2c_bus_manager_unlock_i2c_bus(struct lwis_device *lwis_dev)
{
struct lwis_i2c_bus_manager *i2c_bus_manager = NULL;
i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev);
if (i2c_bus_manager) {
if (lwis_i2c_bus_manager_debug) {
dev_info(lwis_dev->dev, "%s unlock\n", i2c_bus_manager->i2c_bus_name);
}
mutex_unlock(&i2c_bus_manager->i2c_bus_lock);
}
}
/* lwis_i2c_bus_managlwis_i2c_bus_manager_get_managerr_get:
* Gets I2C Bus Manager for a given lwis device
*/
struct lwis_i2c_bus_manager *lwis_i2c_bus_manager_get_manager(struct lwis_device *lwis_dev)
{
struct lwis_i2c_device *i2c_dev = NULL;
if (lwis_check_device_type(lwis_dev, DEVICE_TYPE_I2C)) {
i2c_dev = container_of(lwis_dev, struct lwis_i2c_device, base_dev);
if (i2c_dev) {
return i2c_dev->i2c_bus_manager;
}
}
return NULL;
}
/* lwis_i2c_bus_manager_flush_i2c_worker:
* Flushes the I2C Bus Manager worker
*/
void lwis_i2c_bus_manager_flush_i2c_worker(struct lwis_device *lwis_dev)
{
struct lwis_i2c_bus_manager *i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev);
if (i2c_bus_manager == NULL)
return;
kthread_flush_worker(&i2c_bus_manager->i2c_bus_worker);
}
/* lwis_i2c_bus_manager_list_initialize:
* Initializes bus manager global list. This is the list that holds
* actual bus manager pointers for a given physical I2C Bus connection
*/
void lwis_i2c_bus_manager_list_initialize(void)
{
/* initialize_i2c_bus_manager_list */
mutex_init(&i2c_bus_manager_list_lock);
INIT_LIST_HEAD(&i2c_bus_manager_list.i2c_bus_manager_list_head);
}
/* lwis_i2c_bus_manager_list_deinitialize:
* Deinitializes bus manager global list
*/
void lwis_i2c_bus_manager_list_deinitialize(void)
{
struct list_head *i2c_bus_manager_list_node, *i2c_bus_manager_list_tmp_node;
struct lwis_i2c_bus_manager_identifier *i2c_bus_manager_identifier;
/* deinitialize_i2c_bus_manager_list */
mutex_lock(&i2c_bus_manager_list_lock);
list_for_each_safe (i2c_bus_manager_list_node, i2c_bus_manager_list_tmp_node,
&i2c_bus_manager_list.i2c_bus_manager_list_head) {
i2c_bus_manager_identifier = list_entry(i2c_bus_manager_list_node,
struct lwis_i2c_bus_manager_identifier,
i2c_bus_manager_list_node);
i2c_bus_manager_identifier->i2c_bus_manager = NULL;
list_del(&i2c_bus_manager_identifier->i2c_bus_manager_list_node);
kfree(i2c_bus_manager_identifier);
i2c_bus_manager_identifier = NULL;
}
mutex_unlock(&i2c_bus_manager_list_lock);
}
/* lwis_i2c_bus_manager_connect_client:
* Connects a lwis client to the bus manager to be processed by the worker.
* The client will be connected to the appropriate priority queue based
* on the I2C device priority specified in the dts for the I2C device node.
* I2C lwis client is always connected when a new instance of client is
* created.
*/
int lwis_i2c_bus_manager_connect_client(struct lwis_client *connecting_client)
{
int ret = 0;
int device_priority = I2C_MAX_PRIORITY_LEVELS;
bool create_client_node = true;
struct lwis_i2c_process_request *i2c_connecting_client_node;
struct lwis_device *lwis_dev = NULL;
struct lwis_i2c_process_queue *process_queue = NULL;
struct lwis_i2c_device *i2c_dev = NULL;
struct lwis_i2c_bus_manager *i2c_bus_manager = NULL;
struct list_head *request, *request_tmp;
struct lwis_i2c_process_request *search_node;
if (!connecting_client) {
pr_err("Connecting client pointer for I2C Bus Manager is NULL\n");
return -EINVAL;
}
lwis_dev = connecting_client->lwis_dev;
if (!lwis_dev) {
pr_err("Connecting device for I2C Bus Manager is NULL\n");
return -EINVAL;
}
if (!lwis_check_device_type(lwis_dev, DEVICE_TYPE_I2C)) {
return ret;
}
i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev);
if (!i2c_bus_manager) {
dev_err(lwis_dev->dev, "I2C bus manager is NULL\n");
return -EINVAL;
}
i2c_dev = container_of(lwis_dev, struct lwis_i2c_device, base_dev);
if (!i2c_dev) {
dev_err(lwis_dev->dev, "I2C device is NULL\n");
return -EINVAL;
}
device_priority = i2c_dev->device_priority;
if (!i2c_device_priority_is_valid(device_priority)) {
dev_err(lwis_dev->dev, "Invalid I2C device priority %d\n", device_priority);
return -EINVAL;
}
mutex_lock(&i2c_bus_manager->i2c_process_queue_lock);
// Search for existing client node in the queue, if client is already connected
// to this bus then don't create a new client node
process_queue = &i2c_bus_manager->i2c_bus_process_queue[device_priority];
if (!lwis_i2c_process_request_queue_is_empty(process_queue)) {
list_for_each_safe (request, request_tmp, &process_queue->head) {
search_node =
list_entry(request, struct lwis_i2c_process_request, request_node);
if (search_node->requesting_client == connecting_client) {
dev_info(lwis_dev->dev,
"I2C client already connected %s(%p) to bus %s \n",
lwis_dev->name, connecting_client,
i2c_bus_manager->i2c_bus_name);
create_client_node = false;
break;
}
}
}
if (create_client_node) {
i2c_connecting_client_node =
kzalloc(sizeof(struct lwis_i2c_process_request), GFP_KERNEL);
if (!i2c_connecting_client_node) {
return -ENOMEM;
}
i2c_connecting_client_node->requesting_client = connecting_client;
INIT_LIST_HEAD(&i2c_connecting_client_node->request_node);
list_add_tail(&i2c_connecting_client_node->request_node, &process_queue->head);
++process_queue->number_of_nodes;
dev_info(lwis_dev->dev, "Connecting client %s(%p) to bus %s\n", lwis_dev->name,
connecting_client, i2c_bus_manager->i2c_bus_name);
if (lwis_i2c_bus_manager_debug) {
dev_info(lwis_dev->dev, "Adding process request %p\n",
i2c_connecting_client_node);
}
}
mutex_unlock(&i2c_bus_manager->i2c_process_queue_lock);
return ret;
}
/* lwis_i2c_bus_manager_disconnect_client:
* Disconnects a lwis client to the bus manager. This will make sure that
* the released client is not processed further by the I2C worker.
* The client will be disconnected from the appropriate priority queue based
* on the I2C device priority specified in the dts for the I2C device node.
* I2C lwis client is always disconnected when the instance of client is
* released/destroyed.
*/
void lwis_i2c_bus_manager_disconnect_client(struct lwis_client *disconnecting_client)
{
int device_priority = I2C_MAX_PRIORITY_LEVELS;
struct lwis_i2c_process_request *i2c_disconnecting_client_node;
struct lwis_device *lwis_dev = NULL;
struct lwis_i2c_process_queue *process_queue = NULL;
struct lwis_i2c_device *i2c_dev = NULL;
struct list_head *request, *request_tmp;
struct lwis_i2c_bus_manager *i2c_bus_manager = NULL;
if (!disconnecting_client) {
pr_err("Disconnecting client pointer for I2C Bus Manager is NULL\n");
return;
}
lwis_dev = disconnecting_client->lwis_dev;
if (!lwis_dev) {
pr_err("Disconnecting device for I2C Bus Manager is NULL\n");
return;
}
if (!lwis_check_device_type(lwis_dev, DEVICE_TYPE_I2C)) {
return;
}
i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev);
if (!i2c_bus_manager) {
dev_err(lwis_dev->dev, "I2C bus manager is NULL\n");
return;
}
i2c_dev = container_of(lwis_dev, struct lwis_i2c_device, base_dev);
if (!i2c_dev) {
dev_err(lwis_dev->dev, "I2C device is NULL\n");
return;
}
device_priority = i2c_dev->device_priority;
if (!i2c_device_priority_is_valid(device_priority)) {
dev_err(lwis_dev->dev, "Invalid I2C device priority %d\n", device_priority);
return;
}
mutex_lock(&i2c_bus_manager->i2c_process_queue_lock);
process_queue = &i2c_bus_manager->i2c_bus_process_queue[device_priority];
list_for_each_safe (request, request_tmp, &process_queue->head) {
i2c_disconnecting_client_node =
list_entry(request, struct lwis_i2c_process_request, request_node);
if (i2c_disconnecting_client_node->requesting_client == disconnecting_client) {
dev_info(lwis_dev->dev, "Disconnecting I2C client %s(%p) from bus %s\n",
lwis_dev->name, disconnecting_client,
i2c_bus_manager->i2c_bus_name);
list_del(&i2c_disconnecting_client_node->request_node);
i2c_disconnecting_client_node->requesting_client = NULL;
if (lwis_i2c_bus_manager_debug) {
dev_info(lwis_dev->dev, "Freeing process request %p\n",
i2c_disconnecting_client_node);
}
kfree(i2c_disconnecting_client_node);
i2c_disconnecting_client_node = NULL;
--process_queue->number_of_nodes;
break;
}
}
mutex_unlock(&i2c_bus_manager->i2c_process_queue_lock);
}