diff options
Diffstat (limited to 'lwis_i2c_bus_manager.c')
-rw-r--r-- | lwis_i2c_bus_manager.c | 293 |
1 files changed, 51 insertions, 242 deletions
diff --git a/lwis_i2c_bus_manager.c b/lwis_i2c_bus_manager.c index 04711f7..1b3525e 100644 --- a/lwis_i2c_bus_manager.c +++ b/lwis_i2c_bus_manager.c @@ -208,24 +208,20 @@ static void destroy_i2c_bus_manager(struct lwis_i2c_bus_manager *i2c_bus_manager struct lwis_device *lwis_dev) { unsigned long flags; - 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); - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); - for (i = 0; i < I2C_MAX_PRIORITY_LEVELS; i++) { - lwis_i2c_process_request_queue_destroy(&i2c_bus_manager->i2c_bus_process_queue[i]); - } - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); + if (i2c_bus_manager) { + dev_info(lwis_dev->dev, "Destroying I2C Bus Manager: %s\n", + i2c_bus_manager->i2c_bus_name); + spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); + lwis_i2c_process_request_queue_destroy(&i2c_bus_manager->i2c_bus_process_queue); + spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); - /* Delete the bus manager instance from the list */ - delete_bus_manager_id_in_list(i2c_bus_manager->i2c_bus_id); + /* 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; + /* Free the bus manager */ + kfree(i2c_bus_manager); + i2c_bus_manager = NULL; + } } /* @@ -263,76 +259,42 @@ static int connect_i2c_bus_manager(struct lwis_i2c_bus_manager *i2c_bus_manager, 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. + * pick the next I2C device that is scheduled for transfer */ 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_device **dequeuing_dev = NULL; struct lwis_client *client_to_process = NULL; struct lwis_device *lwis_dev_to_process = NULL; unsigned long flags; - 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 (!i2c_bus_manager) { - dev_err(lwis_dev->dev, "I2C Bus Manager is null\n"); - return; - } - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); - 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) { - 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; - } - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); + if (i2c_bus_manager) { + spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); + dequeuing_dev = lwis_i2c_process_request_queue_dequeue_request( + &i2c_bus_manager->i2c_bus_process_queue); + spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); + + if (dequeuing_dev) { + lwis_dev_to_process = *dequeuing_dev; if (is_valid_connected_device(lwis_dev_to_process, i2c_bus_manager)) { + client_to_process = + container_of(dequeuing_dev, struct lwis_client, lwis_dev); lwis_process_transactions_in_queue(client_to_process); lwis_process_periodic_io_in_queue(client_to_process); } - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); } } - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); } /* @@ -342,7 +304,6 @@ void lwis_i2c_bus_manager_process_worker_queue(struct lwis_client *client) 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; @@ -370,10 +331,7 @@ int lwis_i2c_bus_manager_create(struct lwis_i2c_device *i2c_dev) 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]); - } + lwis_i2c_process_request_queue_initialize(&i2c_bus_manager->i2c_bus_process_queue); /* 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); @@ -463,6 +421,25 @@ void lwis_i2c_bus_manager_disconnect(struct lwis_device *lwis_dev) } } +/* lwis_i2c_bus_manager_enqueue_transfer_request: + * Enqueues I2C transfer request from a requesting device on the I2C Scheduler + */ +int lwis_i2c_bus_manager_enqueue_transfer_request(struct lwis_i2c_bus_manager *i2c_bus_manager, + struct lwis_device **lwis_dev) +{ + int ret = 0; + struct lwis_device *enqueuing_dev = *lwis_dev; + unsigned long flags; + + if (lwis_check_device_type(enqueuing_dev, DEVICE_TYPE_I2C)) { + spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); + ret = lwis_i2c_process_request_queue_enqueue_request( + &i2c_bus_manager->i2c_bus_process_queue, lwis_dev); + spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); + } + return ret; +} + /* lwis_i2c_bus_manager_lock_i2c_bus: * Locks the I2C bus for a given I2C Lwis Device */ @@ -502,23 +479,23 @@ struct lwis_i2c_bus_manager *lwis_i2c_bus_manager_get_manager(struct lwis_device 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) { + unsigned long process_queue_flags; 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); + + /* After flushing the worker the process queue should be empty. + * This destroy is to make sure there are no more requests to be handled. */ + spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, process_queue_flags); + lwis_i2c_process_request_queue_destroy(&i2c_bus_manager->i2c_bus_process_queue); + spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, process_queue_flags); } -/* 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 */ @@ -526,9 +503,6 @@ void lwis_i2c_bus_manager_list_initialize(void) 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; @@ -547,169 +521,4 @@ void lwis_i2c_bus_manager_list_deinitialize(void) 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; - unsigned long flags; - 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; - } - - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); - - // 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_ATOMIC); - if (!i2c_connecting_client_node) { - dev_err(lwis_dev->dev, "Failed to connect client to I2C Bus Manager\n"); - 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); - } - - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); - 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; - unsigned long flags; - 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; - } - - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); - 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; - kfree(i2c_disconnecting_client_node); - i2c_disconnecting_client_node = NULL; - --process_queue->number_of_nodes; - break; - } - } - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); }
\ No newline at end of file |