diff options
author | Meghana Barkalle <mbarkalle@google.com> | 2023-05-01 18:23:41 +0000 |
---|---|---|
committer | Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> | 2023-12-13 16:36:31 +0000 |
commit | cd16abbed20870bc6475b0596038360540718761 (patch) | |
tree | aef0db150841b19de7eb1ec4f7884efd3901bdb3 | |
parent | 50df50b1ab8de8be637562725b0ab195f07593d2 (diff) | |
download | lwis-cd16abbed20870bc6475b0596038360540718761.tar.gz |
Revert^3 "Revert "LWIS: Implement I2C Bus Manager""
5c4f7604ed8e6ae71de1c3adc1cacd25b6c0b2db
Bug: 299130975
Change-Id: I6b051888281116938e21f00e85d04c15cf7bfd9a
(cherry picked from commit 60a58c6b9c5254cddb5304e0b9d06dca9eaa2d5c)
Signed-off-by: Meghana Barkalle <mbarkalle@google.com>
-rw-r--r-- | Kbuild | 2 | ||||
-rw-r--r-- | lwis_device.c | 14 | ||||
-rw-r--r-- | lwis_device.h | 2 | ||||
-rw-r--r-- | lwis_device_i2c.c | 22 | ||||
-rw-r--r-- | lwis_device_i2c.h | 3 | ||||
-rw-r--r-- | lwis_i2c_bus_manager.c | 524 | ||||
-rw-r--r-- | lwis_i2c_bus_manager.h | 106 | ||||
-rw-r--r-- | lwis_i2c_sched.c | 119 | ||||
-rw-r--r-- | lwis_i2c_sched.h | 37 | ||||
-rw-r--r-- | lwis_ioctl.c | 3 | ||||
-rw-r--r-- | lwis_periodic_io.c | 73 | ||||
-rw-r--r-- | lwis_transaction.c | 66 | ||||
-rw-r--r-- | lwis_util.c | 5 | ||||
-rw-r--r-- | lwis_util.h | 5 |
14 files changed, 946 insertions, 35 deletions
@@ -24,6 +24,8 @@ lwis-objs += lwis_io_entry.o lwis-objs += lwis_allocator.o lwis-objs += lwis_version.o lwis-objs += lwis_fence.o +lwis-objs += lwis_i2c_bus_manager.o +lwis-objs += lwis_i2c_sched.o # Anchorage specific files ifeq ($(CONFIG_SOC_GS101), y) diff --git a/lwis_device.c b/lwis_device.c index 1ff65b2..60b5dc4 100644 --- a/lwis_device.c +++ b/lwis_device.c @@ -41,6 +41,7 @@ #include "lwis_util.h" #include "lwis_version.h" #include "lwis_trace.h" +#include "lwis_i2c_bus_manager.h" #ifdef CONFIG_OF #include "lwis_dt.h" @@ -84,6 +85,13 @@ static void transaction_work_func(struct kthread_work *work) lwis_process_worker_queue(client); } +static void i2c_process_work_func(struct kthread_work *work) +{ + /* i2c_work stores the context of the lwis_client submitting the transfer request */ + struct lwis_client *client = container_of(work, struct lwis_client, i2c_work); + lwis_i2c_bus_manager_process_worker_queue(client); +} + /* * lwis_open: Opening an instance of a LWIS device */ @@ -135,6 +143,7 @@ static int lwis_open(struct inode *node, struct file *fp) lwis_allocator_init(lwis_dev); kthread_init_work(&lwis_client->transaction_work, transaction_work_func); + kthread_init_work(&lwis_client->i2c_work, i2c_process_work_func); /* Start transaction processor task */ lwis_transaction_init(lwis_client); @@ -226,6 +235,7 @@ static int lwis_release_client(struct lwis_client *lwis_client) spin_unlock_irqrestore(&lwis_dev->lock, flags); kfree(lwis_client); + return 0; } @@ -1588,6 +1598,10 @@ void lwis_base_unprobe(struct lwis_device *unprobe_lwis_dev) &lwis_dev->plat_dev->dev); lwis_dev->irq_gpios_info.gpios = NULL; } + + /* Disconnect from the bus manager */ + lwis_i2c_bus_manager_disconnect(lwis_dev); + /* Destroy device */ if (!IS_ERR_OR_NULL(lwis_dev->dev)) { device_destroy(core.dev_class, diff --git a/lwis_device.h b/lwis_device.h index c011775..75881fa 100644 --- a/lwis_device.h +++ b/lwis_device.h @@ -345,6 +345,8 @@ struct lwis_client { struct list_head node; /* Mark if the client called device enable */ bool is_enabled; + /* Work item to schedule I2C transfers */ + struct kthread_work i2c_work; }; /* diff --git a/lwis_device_i2c.c b/lwis_device_i2c.c index ae74232..ad513b8 100644 --- a/lwis_device_i2c.c +++ b/lwis_device_i2c.c @@ -274,26 +274,14 @@ static int lwis_i2c_device_probe(struct platform_device *plat_dev) goto error_probe; } - /* Create associated kworker threads */ - ret = lwis_create_kthread_workers(&i2c_dev->base_dev); + /* Create I2C Bus Manager */ + ret = lwis_i2c_bus_manager_create(i2c_dev); if (ret) { - dev_err(i2c_dev->base_dev.dev, "Failed to create lwis_i2c_kthread"); + dev_err(i2c_dev->base_dev.dev, "Error in i2c bus manager creation\n"); lwis_base_unprobe(&i2c_dev->base_dev); goto error_probe; } - if (i2c_dev->base_dev.transaction_thread_priority != 0) { - ret = lwis_set_kthread_priority(&i2c_dev->base_dev, - i2c_dev->base_dev.transaction_worker_thread, - i2c_dev->base_dev.transaction_thread_priority); - if (ret) { - dev_err(i2c_dev->base_dev.dev, - "Failed to set LWIS I2C transaction kthread priority (%d)", ret); - lwis_base_unprobe(&i2c_dev->base_dev); - goto error_probe; - } - } - dev_info(i2c_dev->base_dev.dev, "I2C Device Probe: Success\n"); return 0; @@ -375,6 +363,8 @@ int __init lwis_i2c_device_init(void) pr_info("I2C device initialization\n"); + lwis_i2c_bus_manager_list_initialize(); + ret = platform_driver_register(&lwis_driver); if (ret) { pr_err("platform_driver_register failed: %d\n", ret); @@ -389,6 +379,8 @@ int __init lwis_i2c_device_init(void) int lwis_i2c_device_deinit(void) { + lwis_i2c_bus_manager_list_deinitialize(); + platform_driver_unregister(&lwis_driver); return 0; } diff --git a/lwis_device_i2c.h b/lwis_device_i2c.h index 7d52d1f..03c393f 100644 --- a/lwis_device_i2c.h +++ b/lwis_device_i2c.h @@ -15,6 +15,7 @@ #include <linux/pinctrl/consumer.h> #include "lwis_device.h" +#include "lwis_i2c_bus_manager.h" #define MAX_I2C_LOCK_NUM 8 @@ -33,6 +34,8 @@ struct lwis_i2c_device { u32 i2c_lock_group_id; /* Mutex shared by the same group id's I2C devices */ struct mutex *group_i2c_lock; + /* Pointer to the I2C bus manager for this device */ + struct lwis_i2c_bus_manager *i2c_bus_manager; }; int lwis_i2c_device_deinit(void); diff --git a/lwis_i2c_bus_manager.c b/lwis_i2c_bus_manager.c new file mode 100644 index 0000000..1b3525e --- /dev/null +++ b/lwis_i2c_bus_manager.c @@ -0,0 +1,524 @@ +/* + * 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" + +/* 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) { + pr_err("Failed to allocate lwis i2c bus manager id list node\n"); + 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) +{ + unsigned long 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); + + /* 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) { + dev_err(lwis_dev->dev, "Failed to connect device to I2C Bus Manager\n"); + 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; +} + +/* + * lwis_i2c_bus_manager_process_worker_queue: + * Function to be called by i2c bus manager worker thread to + * 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; + + /* 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; + + lwis_dev = client->lwis_dev; + i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); + + 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); + } + } + } +} + +/* + * 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; + 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) { + dev_err(i2c_base_device->dev, "Failed to allocate lwis i2c bus manager\n"); + 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); + spin_lock_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 */ + 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); + 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_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 + */ +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); + } +} + +/* 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) { + 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; +} + +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); +} + +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); +} + +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); +}
\ No newline at end of file diff --git a/lwis_i2c_bus_manager.h b/lwis_i2c_bus_manager.h new file mode 100644 index 0000000..ca40ae8 --- /dev/null +++ b/lwis_i2c_bus_manager.h @@ -0,0 +1,106 @@ + +/* + * 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. + */ + +#ifndef LWIS_I2C_BUS_MANAGER_H_ +#define LWIS_I2C_BUS_MANAGER_H_ + +#include "lwis_device.h" +#include "lwis_util.h" +#include "lwis_periodic_io.h" +#include "lwis_transaction.h" + +// Forward declaration +struct lwis_i2c_device; + +/* struct lwis_i2c_bus_manager_list: + * Holds I2C bus manager list */ +struct lwis_i2c_bus_manager_list { + struct list_head i2c_bus_manager_list_head; +}; + +/* struct lwis_i2c_bus_manager_identifier: + * Holds a pointer to the I2C bus manager */ +struct lwis_i2c_bus_manager_identifier { + struct list_head i2c_bus_manager_list_node; + struct lwis_i2c_bus_manager *i2c_bus_manager; + int i2c_bus_manager_handle; +}; + +/* lwis_i2c_process_queue: + * This maintains the process queue for a given I2C bus. + * This is a collection of process request nodes that identify + * the lwis device requests in order they were queued. + * The scheduler is set to operate requests in a + * first in-first out manner, starting and updating the head + * and working towards the tail end. */ +struct lwis_i2c_process_queue { + /* Head node for the process queue */ + struct list_head head; + /* Total number of devices that are queued to be processed */ + int number_of_requests; +}; + +/* + * struct lwis_i2c_bus_manager + * This defines the main attributes for I2C Bus Manager. + */ +struct lwis_i2c_bus_manager { + /* Unique identifier for this I2C bus manager */ + int i2c_bus_id; + /* Name of I2C Bus manager corresponds to the name of the I2C Bus*/ + char i2c_bus_name[LWIS_MAX_NAME_STRING_LEN]; + /* Lock to control access to bus transfers */ + struct mutex i2c_bus_lock; + /* Lock to control access to the I2C process queue for this bus */ + spinlock_t i2c_process_queue_lock; + /* I2C Bus thread priority */ + u32 i2c_bus_thread_priority; + /* Worker thread */ + struct kthread_worker i2c_bus_worker; + struct task_struct *i2c_bus_worker_thread; + /* Queue of all I2C devices that have data to transfer in their process queues */ + struct lwis_i2c_process_queue i2c_bus_process_queue; + /* List of I2C devices using this bus */ + struct list_head i2c_connected_devices; + /* Total number of physically connected devices to the bus + * This count is set while probe/unprobe sequence */ + int number_of_connected_devices; +}; + +/* This maintains the structure to identify the connected devices to a given I2C bus. + * This will be used to guard the bus against processing any illegal device entries */ +struct lwis_i2c_connected_device { + struct lwis_device *connected_device; + struct list_head connected_device_node; +}; + +void lwis_i2c_bus_manager_lock_i2c_bus(struct lwis_device *lwis_dev); + +void lwis_i2c_bus_manager_unlock_i2c_bus(struct lwis_device *lwis_dev); + +struct lwis_i2c_bus_manager *lwis_i2c_bus_manager_get_manager(struct lwis_device *lwis_dev); + +int lwis_i2c_bus_manager_create(struct lwis_i2c_device *i2c_dev); + +void lwis_i2c_bus_manager_disconnect(struct lwis_device *lwis_dev); + +void lwis_i2c_bus_manager_process_worker_queue(struct lwis_client *client); + +int lwis_i2c_bus_manager_enqueue_transfer_request(struct lwis_i2c_bus_manager *i2c_bus_manager, + struct lwis_device **lwis_dev); + +void lwis_i2c_bus_manager_flush_i2c_worker(struct lwis_device *lwis_dev); + +void lwis_i2c_bus_manager_list_initialize(void); + +void lwis_i2c_bus_manager_list_deinitialize(void); + +#endif /* LWIS_I2C_BUS_MANAGER_H */ diff --git a/lwis_i2c_sched.c b/lwis_i2c_sched.c new file mode 100644 index 0000000..3c61fcf --- /dev/null +++ b/lwis_i2c_sched.c @@ -0,0 +1,119 @@ +/* + * 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-sched: " fmt + +#include "lwis_i2c_sched.h" +#include "lwis_i2c_bus_manager.h" + +/* + * lwis_i2c_process_request_queue_is_empty: + * Checks if the I2C process request queue is empty +*/ +static bool lwis_i2c_process_request_queue_is_empty(struct lwis_i2c_process_queue *process_queue) +{ + if ((!process_queue) || ((process_queue) && (process_queue->number_of_requests == 0))) { + return true; + } + return false; +} + +/* + * lwis_i2c_process_request_queue_initialize: + * Initializes the I2C process request queue for a given I2C Bus +*/ +void lwis_i2c_process_request_queue_initialize(struct lwis_i2c_process_queue *process_queue) +{ + process_queue->number_of_requests = 0; + INIT_LIST_HEAD(&process_queue->head); +} + +/* + * lwis_i2c_process_request_queue_destroy: + * Frees all the requests in the queue +*/ +void lwis_i2c_process_request_queue_destroy(struct lwis_i2c_process_queue *process_queue) +{ + struct list_head *request; + struct list_head *request_tmp; + struct lwis_i2c_process_request *process_request; + + if (!process_queue) + return; + + if (lwis_i2c_process_request_queue_is_empty(process_queue)) + return; + + list_for_each_safe (request, request_tmp, &process_queue->head) { + process_request = + list_entry(request, struct lwis_i2c_process_request, request_node); + list_del(&process_request->request_node); + process_request->requesting_device = NULL; + kfree(process_request); + process_request = NULL; + --process_queue->number_of_requests; + } +} + +/* + * lwis_i2c_process_request_queue_enqueue_request: + * Enqueues a requesting device on tail of the I2C Scheduler +*/ +int lwis_i2c_process_request_queue_enqueue_request(struct lwis_i2c_process_queue *process_queue, + struct lwis_device **requesting_device) +{ + int ret = 0; + struct lwis_i2c_process_request *request; + struct lwis_device *lwis_dev = *requesting_device; + + if ((!process_queue) || (!requesting_device) || (!lwis_dev)) { + pr_err("Invalid pointer\n"); + return -EINVAL; + } + + // Atomic allocation needed here since this memory is allocated within + // transition and periodic io locks of various I2C devices + request = kzalloc(sizeof(struct lwis_i2c_process_request), GFP_ATOMIC); + if (!request) { + dev_err(lwis_dev->dev, "Failed to allocate I2C Process Request Node memory\n"); + return -ENOMEM; + } + request->requesting_device = requesting_device; + INIT_LIST_HEAD(&request->request_node); + list_add_tail(&request->request_node, &process_queue->head); + process_queue->number_of_requests++; + + return ret; +} + +/* + * lwis_i2c_process_request_queue_dequeue_request: + * Dequeues a lwis device from head of the I2C Scheduler +*/ +struct lwis_device ** +lwis_i2c_process_request_queue_dequeue_request(struct lwis_i2c_process_queue *process_queue) +{ + struct lwis_i2c_process_request *request; + struct lwis_device **requested_device = NULL; + + if (lwis_i2c_process_request_queue_is_empty(process_queue)) { + return requested_device; + } + + request = list_first_entry_or_null(&process_queue->head, struct lwis_i2c_process_request, + request_node); + if (request) { + requested_device = request->requesting_device; + list_del(&request->request_node); + kfree(request); + request = NULL; + process_queue->number_of_requests--; + } + return requested_device; +}
\ No newline at end of file diff --git a/lwis_i2c_sched.h b/lwis_i2c_sched.h new file mode 100644 index 0000000..de3390d --- /dev/null +++ b/lwis_i2c_sched.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef LWIS_I2C_SCHED_H_ +#define LWIS_I2C_SCHED_H_ + +#include "lwis_device.h" + +// Forward declaration +struct lwis_i2c_process_queue; + +/* lwis_i2c_process_request: + * This maintains the node to identify the devices that + * have a request to be processed on a given I2C bus */ +struct lwis_i2c_process_request { + struct lwis_device **requesting_device; + struct list_head request_node; +}; + +void lwis_i2c_process_request_queue_initialize(struct lwis_i2c_process_queue *process_queue); + +void lwis_i2c_process_request_queue_destroy(struct lwis_i2c_process_queue *process_queue); + +int lwis_i2c_process_request_queue_enqueue_request(struct lwis_i2c_process_queue *process_queue, + struct lwis_device **requesting_device); + +struct lwis_device ** +lwis_i2c_process_request_queue_dequeue_request(struct lwis_i2c_process_queue *process_queue); + +#endif /* LWIS_I2C_SCHED_H_ */
\ No newline at end of file diff --git a/lwis_ioctl.c b/lwis_ioctl.c index 64cb1e2..1599149 100644 --- a/lwis_ioctl.c +++ b/lwis_ioctl.c @@ -36,6 +36,7 @@ #include "lwis_regulator.h" #include "lwis_transaction.h" #include "lwis_util.h" +#include "lwis_i2c_bus_manager.h" #define IOCTL_TO_ENUM(x) _IOC_NR(x) #define IOCTL_ARG_SIZE(x) _IOC_SIZE(x) @@ -189,6 +190,7 @@ static int synchronous_process_io_entries(struct lwis_device *lwis_dev, int num_ { int ret = 0, i = 0; + lwis_i2c_bus_manager_lock_i2c_bus(lwis_dev); /* Use write memory barrier at the beginning of I/O entries if the access protocol * allows it */ if (lwis_dev->vops.register_io_barrier != NULL) { @@ -238,6 +240,7 @@ exit: /*use_read_barrier=*/true, /*use_write_barrier=*/false); } + lwis_i2c_bus_manager_unlock_i2c_bus(lwis_dev); return ret; } diff --git a/lwis_periodic_io.c b/lwis_periodic_io.c index 6ac3378..bdd86e5 100644 --- a/lwis_periodic_io.c +++ b/lwis_periodic_io.c @@ -23,6 +23,7 @@ #include "lwis_ioreg.h" #include "lwis_transaction.h" #include "lwis_util.h" +#include "lwis_i2c_bus_manager.h" static enum hrtimer_restart periodic_io_timer_func(struct hrtimer *timer) { @@ -34,10 +35,16 @@ static enum hrtimer_restart periodic_io_timer_func(struct hrtimer *timer) struct lwis_periodic_io_proxy *periodic_io_proxy; struct lwis_client *client; bool active_periodic_io_present = false; + struct lwis_device *lwis_dev; + struct lwis_i2c_bus_manager *i2c_bus_manager = NULL; + int ret = 0; periodic_io_list = container_of(timer, struct lwis_periodic_io_list, hr_timer); client = periodic_io_list->client; + lwis_dev = client->lwis_dev; + i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); + /* Go through all periodic io under the chosen periodic list */ spin_lock_irqsave(&client->periodic_io_lock, flags); list_for_each_safe (it_period, it_period_tmp, &periodic_io_list->list) { @@ -47,7 +54,7 @@ static enum hrtimer_restart periodic_io_timer_func(struct hrtimer *timer) client->lwis_dev, sizeof(*periodic_io_proxy), GFP_ATOMIC); if (!periodic_io_proxy) { /* Non-fatal, skip this period */ - pr_warn("Cannot allocate new periodic io proxy.\n"); + dev_warn(lwis_dev->dev, "Cannot allocate new periodic io proxy.\n"); } else { periodic_io_proxy->periodic_io = periodic_io; list_add_tail(&periodic_io_proxy->process_queue_node, @@ -57,8 +64,17 @@ static enum hrtimer_restart periodic_io_timer_func(struct hrtimer *timer) } } if (active_periodic_io_present) { - kthread_queue_work(&client->lwis_dev->transaction_worker, - &client->transaction_work); + if (i2c_bus_manager) { + ret = lwis_i2c_bus_manager_enqueue_transfer_request(i2c_bus_manager, + &client->lwis_dev); + if (!ret) { + kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, + &client->i2c_work); + } + } else { + kthread_queue_work(&client->lwis_dev->transaction_worker, + &client->transaction_work); + } } spin_unlock_irqrestore(&client->periodic_io_lock, flags); if (!active_periodic_io_present) { @@ -89,10 +105,11 @@ static struct lwis_periodic_io_list *periodic_io_list_create_locked(struct lwis_ int64_t period_ns) { ktime_t ktime; + struct lwis_device *lwis_dev = client->lwis_dev; struct lwis_periodic_io_list *periodic_io_list = kmalloc(sizeof(struct lwis_periodic_io_list), GFP_ATOMIC); if (!periodic_io_list) { - pr_err("Cannot allocate new event list\n"); + dev_err(lwis_dev->dev, "Cannot allocate new event list\n"); return NULL; } @@ -104,7 +121,7 @@ static struct lwis_periodic_io_list *periodic_io_list_create_locked(struct lwis_ * into the client timer list */ INIT_LIST_HEAD(&periodic_io_list->list); hash_add(client->timer_list, &periodic_io_list->node, period_ns); - pr_info("Created hrtimer with timeout time %lldns", period_ns); + dev_info(lwis_dev->dev, "Created hrtimer with timeout time %lldns", period_ns); /* Initialize and start the hrtimer for this periodic io list */ hrtimer_init(&periodic_io_list->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); @@ -184,6 +201,7 @@ static int process_io_entries(struct lwis_client *client, } reinit_completion(&periodic_io->io_done); + lwis_i2c_bus_manager_lock_i2c_bus(lwis_dev); for (i = 0; i < info->num_io_entries; ++i) { /* Abort if periodic io is deactivated during processing. * Abort can only apply to <= 1 write entries to prevent partial writes, @@ -267,6 +285,8 @@ static int process_io_entries(struct lwis_client *client, event_push: complete(&periodic_io->io_done); + lwis_i2c_bus_manager_unlock_i2c_bus(lwis_dev); + /* Use read memory barrier at the beginning of I/O entries if the access protocol * allows it */ if (lwis_dev->vops.register_io_barrier != NULL) { @@ -280,8 +300,10 @@ event_push: * there is an error */ if (!pending_events) { if (resp->error_code && resp->error_code != -ECANCELED) { - pr_err("process_io_entries fails with error code %d, periodic io %lld, io_entries[%d], entry_type %d", - resp->error_code, info->id, i, entry->type); + dev_info( + lwis_dev->dev, + "process_io_entries fails with error code %d, periodic io %lld, io_entries[%d], entry_type %d", + resp->error_code, info->id, i, entry->type); } return ret; } @@ -336,7 +358,6 @@ void lwis_process_periodic_io_in_queue(struct lwis_client *client) process_io_entries(client, periodic_io_proxy, &pending_events); spin_lock_irqsave(&client->periodic_io_lock, flags); } - lwis_allocator_free(client->lwis_dev, periodic_io_proxy); } spin_unlock_irqrestore(&client->periodic_io_lock, flags); lwis_pending_events_emit(client->lwis_dev, &pending_events); @@ -507,6 +528,15 @@ int lwis_periodic_io_client_flush(struct lwis_client *client) struct lwis_periodic_io_list *it_periodic_io_list; unsigned long flags; + struct lwis_device *lwis_dev = client->lwis_dev; + struct lwis_i2c_bus_manager *i2c_bus_manager = NULL; + + struct lwis_periodic_io *periodic_cleanup_io; + struct lwis_periodic_io_proxy *periodic_cleanup_io_proxy; + struct list_head *it_cleanup_period, *it_cleanup_period_tmp; + + i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); + /* First, cancel all timers */ hash_for_each_safe (client->timer_list, i, tmp, it_periodic_io_list, node) { spin_lock_irqsave(&client->periodic_io_lock, flags); @@ -521,11 +551,30 @@ int lwis_periodic_io_client_flush(struct lwis_client *client) } /* Wait until all workload in process queue are processed */ - if (client->lwis_dev->transaction_worker_thread) { - kthread_flush_worker(&client->lwis_dev->transaction_worker); + if (i2c_bus_manager) { + lwis_i2c_bus_manager_flush_i2c_worker(lwis_dev); + } else { + if (client->lwis_dev->transaction_worker_thread) { + kthread_flush_worker(&client->lwis_dev->transaction_worker); + } } spin_lock_irqsave(&client->periodic_io_lock, flags); + /* Cleanup any stale entries remaining after the flush */ + list_for_each_safe (it_cleanup_period, it_cleanup_period_tmp, + &client->periodic_io_process_queue) { + periodic_cleanup_io_proxy = list_entry( + it_cleanup_period, struct lwis_periodic_io_proxy, process_queue_node); + if (periodic_cleanup_io_proxy) { + periodic_cleanup_io = periodic_cleanup_io_proxy->periodic_io; + list_del(&periodic_cleanup_io_proxy->process_queue_node); + if (periodic_cleanup_io) { + periodic_cleanup_io->active = false; + } + lwis_allocator_free(client->lwis_dev, periodic_cleanup_io_proxy); + } + } + /* Release the periodic io list of from all timers */ hash_for_each_safe (client->timer_list, i, tmp, it_periodic_io_list, node) { list_for_each_safe (it_period, it_period_tmp, &it_periodic_io_list->list) { @@ -535,6 +584,7 @@ int lwis_periodic_io_client_flush(struct lwis_client *client) lwis_periodic_io_free(client->lwis_dev, periodic_io); } } + spin_unlock_irqrestore(&client->periodic_io_lock, flags); return 0; } @@ -548,7 +598,8 @@ int lwis_periodic_io_client_cleanup(struct lwis_client *client) ret = lwis_periodic_io_client_flush(client); if (ret) { - pr_err("Failed to wait for all in-process periodic io to complete\n"); + dev_err(client->lwis_dev->dev, + "Failed to wait for all in-process periodic io to complete\n"); return ret; } diff --git a/lwis_transaction.c b/lwis_transaction.c index 3212623..983cd5c 100644 --- a/lwis_transaction.c +++ b/lwis_transaction.c @@ -27,6 +27,7 @@ #include "lwis_io_entry.h" #include "lwis_ioreg.h" #include "lwis_util.h" +#include "lwis_i2c_bus_manager.h" #define CREATE_TRACE_POINTS #include "lwis_trace.h" @@ -186,7 +187,7 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti /*use_read_barrier=*/false, /*use_write_barrier=*/true); } - + lwis_i2c_bus_manager_lock_i2c_bus(lwis_dev); for (i = 0; i < info->num_io_entries; ++i) { entry = &info->io_entries[i]; if (entry->type == LWIS_IO_ENTRY_WRITE || @@ -312,6 +313,7 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti resp->completion_index = i; } + lwis_i2c_bus_manager_unlock_i2c_bus(lwis_dev); if (lwis_transaction_debug) { process_duration_ns = ktime_to_ns(lwis_get_time() - process_timestamp); } @@ -465,12 +467,17 @@ int lwis_transaction_client_flush(struct lwis_client *client) int i; struct hlist_node *tmp; struct lwis_transaction_event_list *it_evt_list; + struct lwis_device *lwis_dev = NULL; + struct lwis_i2c_bus_manager *i2c_bus_manager = NULL; if (!client) { pr_err("Client pointer cannot be NULL while flushing transactions.\n"); return -ENODEV; } + lwis_dev = client->lwis_dev; + i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); + spin_lock_irqsave(&client->transaction_lock, flags); hash_for_each_safe (client->transaction_list, i, tmp, it_evt_list, node) { if ((it_evt_list->event_id & 0xFFFF0000FFFFFFFFll) == @@ -491,8 +498,13 @@ int lwis_transaction_client_flush(struct lwis_client *client) } spin_unlock_irqrestore(&client->transaction_lock, flags); - if (client->lwis_dev->transaction_worker_thread) - kthread_flush_worker(&client->lwis_dev->transaction_worker); + if (i2c_bus_manager) { + lwis_i2c_bus_manager_flush_i2c_worker(lwis_dev); + } else { + if (client->lwis_dev->transaction_worker_thread) { + kthread_flush_worker(&client->lwis_dev->transaction_worker); + } + } spin_lock_irqsave(&client->transaction_lock, flags); /* The transaction queue should be empty after canceling all transactions, @@ -725,12 +737,24 @@ static int queue_transaction_locked(struct lwis_client *client, { struct lwis_transaction_event_list *event_list; struct lwis_transaction_info_v2 *info = &transaction->info; + struct lwis_device *lwis_dev = client->lwis_dev; + struct lwis_i2c_bus_manager *i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); if (transaction->queue_immediately) { /* Immediate trigger. */ list_add_tail(&transaction->process_queue_node, &client->transaction_process_queue); - kthread_queue_work(&client->lwis_dev->transaction_worker, - &client->transaction_work); + if (i2c_bus_manager) { + if (!lwis_i2c_bus_manager_enqueue_transfer_request(i2c_bus_manager, + &client->lwis_dev)) { + kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, + &client->i2c_work); + } else { + dev_err(client->lwis_dev->dev, "Cannot queue I2C transfer\n"); + } + } else { + kthread_queue_work(&client->lwis_dev->transaction_worker, + &client->transaction_work); + } } else if (lwis_triggered_by_condition(transaction)) { /* Trigger by trigger conditions. */ add_pending_transaction(client, transaction); @@ -845,6 +869,8 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, struct lwis_transaction *new_instance; int64_t trigger_counter = 0; struct list_head pending_fences; + struct lwis_device *lwis_dev = client->lwis_dev; + struct lwis_i2c_bus_manager *i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); INIT_LIST_HEAD(&pending_fences); @@ -920,8 +946,18 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, /* Schedule deferred transactions */ if (!list_empty(&client->transaction_process_queue)) { - kthread_queue_work(&client->lwis_dev->transaction_worker, - &client->transaction_work); + if (i2c_bus_manager) { + if (!lwis_i2c_bus_manager_enqueue_transfer_request(i2c_bus_manager, + &client->lwis_dev)) { + kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, + &client->i2c_work); + } else { + dev_err(client->lwis_dev->dev, "Cannot queue I2C transfer\n"); + } + } else { + kthread_queue_work(&client->lwis_dev->transaction_worker, + &client->transaction_work); + } } spin_unlock_irqrestore(&client->transaction_lock, flags); @@ -940,6 +976,8 @@ void lwis_transaction_fence_trigger(struct lwis_client *client, struct lwis_fenc struct list_head *it_tran, *it_tran_tmp; struct list_head pending_events; struct list_head pending_fences; + struct lwis_device *lwis_dev = client->lwis_dev; + struct lwis_i2c_bus_manager *i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); if (list_empty(transaction_list)) { return; @@ -987,8 +1025,18 @@ void lwis_transaction_fence_trigger(struct lwis_client *client, struct lwis_fenc /* Schedule deferred transactions */ if (!list_empty(&client->transaction_process_queue)) { - kthread_queue_work(&client->lwis_dev->transaction_worker, - &client->transaction_work); + if (i2c_bus_manager) { + if (!lwis_i2c_bus_manager_enqueue_transfer_request(i2c_bus_manager, + &client->lwis_dev)) { + kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, + &client->i2c_work); + } else { + dev_err(client->lwis_dev->dev, "Cannot queue I2C transfer\n"); + } + } else { + kthread_queue_work(&client->lwis_dev->transaction_worker, + &client->transaction_work); + } } spin_unlock_irqrestore(&client->transaction_lock, flags); diff --git a/lwis_util.c b/lwis_util.c index 0524ffc..5a440d2 100644 --- a/lwis_util.c +++ b/lwis_util.c @@ -162,3 +162,8 @@ int lwis_set_kthread_priority(struct lwis_device *lwis_dev, struct task_struct * return 0; } + +bool lwis_check_device_type(struct lwis_device *lwis_dev, int32_t type) +{ + return ((lwis_dev) && (lwis_dev->type == type)); +}
\ No newline at end of file diff --git a/lwis_util.h b/lwis_util.h index 29dca82..a313c0c 100644 --- a/lwis_util.h +++ b/lwis_util.h @@ -80,4 +80,9 @@ int lwis_create_kthread_workers(struct lwis_device *lwis_dev); */ int lwis_set_kthread_priority(struct lwis_device *lwis_dev, struct task_struct *task, u32 priority); +/* + * lwis_check_device_type: Returns true if the passed lwis_device's type is same as 'type' + */ +bool lwis_check_device_type(struct lwis_device *lwis_dev, int32_t type); + #endif // LWIS_UTIL_H_ |