summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommy Kardach <thomaskardach@google.com>2023-04-27 16:41:54 +0000
committerTreehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com>2023-12-13 16:36:31 +0000
commitf8f9b25ba1a2aba430de0531560c6c21d8f5fdc5 (patch)
treeaef0db150841b19de7eb1ec4f7884efd3901bdb3
parentb64156b9d2e33a005d07f004f3e9967db2aa6a0c (diff)
downloadlwis-f8f9b25ba1a2aba430de0531560c6c21d8f5fdc5.tar.gz
Revert "Optimize I2C Bus manager scheduling"
Revert submission 2462937-I2CBusManager-OptimizedScheduling Reason for revert: Change causes memory leak b/279860649 Reverted changes: /q/submissionid:2462937-I2CBusManager-OptimizedScheduling Bug: 299130975 Change-Id: I8ab99b1b8221f1cade6fd485ca9ce5b3bcc2eeee (cherry picked from commit 7d2d4f7ad26c4ceab65c8e0165bf662aaf179683) Signed-off-by: Meghana Barkalle <mbarkalle@google.com>
-rw-r--r--lwis_device.c7
-rw-r--r--lwis_device_i2c.c1
-rw-r--r--lwis_device_i2c.h1
-rw-r--r--lwis_dt.c34
-rw-r--r--lwis_i2c_bus_manager.c293
-rw-r--r--lwis_i2c_bus_manager.h22
-rw-r--r--lwis_i2c_sched.c67
-rw-r--r--lwis_i2c_sched.h10
-rw-r--r--lwis_periodic_io.c8
-rw-r--r--lwis_transaction.c24
10 files changed, 154 insertions, 313 deletions
diff --git a/lwis_device.c b/lwis_device.c
index 4735f62..60b5dc4 100644
--- a/lwis_device.c
+++ b/lwis_device.c
@@ -160,11 +160,6 @@ static int lwis_open(struct inode *node, struct file *fp)
/* Storing the client handle in fp private_data for easy access */
fp->private_data = lwis_client;
- if (lwis_i2c_bus_manager_connect_client(lwis_client)) {
- dev_err(lwis_dev->dev, "Failed to connect lwis client to I2C bus manager\n");
- return -EINVAL;
- }
-
lwis_client->is_enabled = false;
return 0;
}
@@ -239,8 +234,6 @@ static int lwis_release_client(struct lwis_client *lwis_client)
}
spin_unlock_irqrestore(&lwis_dev->lock, flags);
- lwis_i2c_bus_manager_disconnect_client(lwis_client);
-
kfree(lwis_client);
return 0;
diff --git a/lwis_device_i2c.c b/lwis_device_i2c.c
index 877719f..ad513b8 100644
--- a/lwis_device_i2c.c
+++ b/lwis_device_i2c.c
@@ -7,6 +7,7 @@
* 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-dev: " fmt
#include "lwis_device_i2c.h"
diff --git a/lwis_device_i2c.h b/lwis_device_i2c.h
index 393bd32..03c393f 100644
--- a/lwis_device_i2c.h
+++ b/lwis_device_i2c.h
@@ -36,7 +36,6 @@ struct lwis_i2c_device {
struct mutex *group_i2c_lock;
/* Pointer to the I2C bus manager for this device */
struct lwis_i2c_bus_manager *i2c_bus_manager;
- int device_priority;
};
int lwis_i2c_device_deinit(void);
diff --git a/lwis_dt.c b/lwis_dt.c
index d2e1c3a..d6c4559 100644
--- a/lwis_dt.c
+++ b/lwis_dt.c
@@ -25,7 +25,6 @@
#include "lwis_i2c.h"
#include "lwis_ioreg.h"
#include "lwis_regulator.h"
-#include "lwis_i2c_bus_manager.h"
#define SHARED_STRING "shared-"
#define PULSE_STRING "pulse-"
@@ -1166,33 +1165,6 @@ static int parse_thread_priority(struct lwis_device *lwis_dev)
return 0;
}
-static int parse_i2c_device_priority(struct lwis_i2c_device *i2c_dev)
-{
- struct device_node *dev_node;
- int ret = 0;
-
- dev_node = i2c_dev->base_dev.plat_dev->dev.of_node;
- /* Set i2c device_priority value to default */
- i2c_dev->device_priority = I2C_DEVICE_HIGH_PRIORITY;
-
- ret = of_property_read_u32(dev_node, "i2c-device-priority", &i2c_dev->device_priority);
- /* If no property in device tree, just return to use default */
- if (ret == -EINVAL) {
- return 0;
- }
- if (ret) {
- pr_err("invalid i2c-device-priority value\n");
- return ret;
- }
- if ((i2c_dev->device_priority < I2C_DEVICE_HIGH_PRIORITY) ||
- (i2c_dev->device_priority > I2C_DEVICE_LOW_PRIORITY)) {
- pr_err("invalid i2c-device-priority value %d\n", i2c_dev->device_priority);
- return -EINVAL;
- }
-
- return 0;
-}
-
static int parse_i2c_lock_group_id(struct lwis_i2c_device *i2c_dev)
{
struct device_node *dev_node;
@@ -1392,12 +1364,6 @@ int lwis_i2c_device_parse_dt(struct lwis_i2c_device *i2c_dev)
return ret;
}
- ret = parse_i2c_device_priority(i2c_dev);
- if (ret) {
- dev_err(i2c_dev->base_dev.dev, "Error parsing i2c device priority\n");
- return ret;
- }
-
return 0;
}
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
diff --git a/lwis_i2c_bus_manager.h b/lwis_i2c_bus_manager.h
index 5e236e1..ca40ae8 100644
--- a/lwis_i2c_bus_manager.h
+++ b/lwis_i2c_bus_manager.h
@@ -17,17 +17,6 @@
#include "lwis_periodic_io.h"
#include "lwis_transaction.h"
-/* enum lwis_i2c_device_priority_level:
- * Defines the I2C device priority level
- * in which the requests will be executed
- */
-enum lwis_i2c_device_priority_level {
- I2C_DEVICE_HIGH_PRIORITY = 0,
- I2C_DEVICE_MEDIUM_PRIORITY = 1,
- I2C_DEVICE_LOW_PRIORITY = 2,
- I2C_MAX_PRIORITY_LEVELS = 3
-};
-
// Forward declaration
struct lwis_i2c_device;
@@ -56,7 +45,7 @@ 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_nodes;
+ int number_of_requests;
};
/*
@@ -78,7 +67,7 @@ struct lwis_i2c_bus_manager {
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[I2C_MAX_PRIORITY_LEVELS];
+ 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
@@ -105,14 +94,13 @@ 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);
-int lwis_i2c_bus_manager_connect_client(struct lwis_client *connecting_client);
-
-void lwis_i2c_bus_manager_disconnect_client(struct lwis_client *disconnecting_client);
-
#endif /* LWIS_I2C_BUS_MANAGER_H */
diff --git a/lwis_i2c_sched.c b/lwis_i2c_sched.c
index 1ca802f..3c61fcf 100644
--- a/lwis_i2c_sched.c
+++ b/lwis_i2c_sched.c
@@ -16,9 +16,9 @@
* lwis_i2c_process_request_queue_is_empty:
* Checks if the I2C process request queue is empty
*/
-bool lwis_i2c_process_request_queue_is_empty(struct lwis_i2c_process_queue *process_queue)
+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_nodes == 0))) {
+ if ((!process_queue) || ((process_queue) && (process_queue->number_of_requests == 0))) {
return true;
}
return false;
@@ -30,7 +30,7 @@ bool lwis_i2c_process_request_queue_is_empty(struct lwis_i2c_process_queue *proc
*/
void lwis_i2c_process_request_queue_initialize(struct lwis_i2c_process_queue *process_queue)
{
- process_queue->number_of_nodes = 0;
+ process_queue->number_of_requests = 0;
INIT_LIST_HEAD(&process_queue->head);
}
@@ -54,9 +54,66 @@ void lwis_i2c_process_request_queue_destroy(struct lwis_i2c_process_queue *proce
process_request =
list_entry(request, struct lwis_i2c_process_request, request_node);
list_del(&process_request->request_node);
- process_request->requesting_client = NULL;
+ process_request->requesting_device = NULL;
kfree(process_request);
process_request = NULL;
- --process_queue->number_of_nodes;
+ --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
index 22073af..de3390d 100644
--- a/lwis_i2c_sched.h
+++ b/lwis_i2c_sched.h
@@ -20,14 +20,18 @@ struct lwis_i2c_process_queue;
* 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_client *requesting_client;
+ struct lwis_device **requesting_device;
struct list_head request_node;
};
-bool lwis_i2c_process_request_queue_is_empty(struct lwis_i2c_process_queue *process_queue);
-
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_periodic_io.c b/lwis_periodic_io.c
index ad5ef83..bdd86e5 100644
--- a/lwis_periodic_io.c
+++ b/lwis_periodic_io.c
@@ -37,6 +37,7 @@ static enum hrtimer_restart periodic_io_timer_func(struct hrtimer *timer)
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;
@@ -64,7 +65,12 @@ static enum hrtimer_restart periodic_io_timer_func(struct hrtimer *timer)
}
if (active_periodic_io_present) {
if (i2c_bus_manager) {
- kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work);
+ 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);
diff --git a/lwis_transaction.c b/lwis_transaction.c
index db3c094..983cd5c 100644
--- a/lwis_transaction.c
+++ b/lwis_transaction.c
@@ -744,7 +744,13 @@ static int queue_transaction_locked(struct lwis_client *client,
/* Immediate trigger. */
list_add_tail(&transaction->process_queue_node, &client->transaction_process_queue);
if (i2c_bus_manager) {
- kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work);
+ 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);
@@ -941,7 +947,13 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id,
/* Schedule deferred transactions */
if (!list_empty(&client->transaction_process_queue)) {
if (i2c_bus_manager) {
- kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work);
+ 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);
@@ -1014,7 +1026,13 @@ void lwis_transaction_fence_trigger(struct lwis_client *client, struct lwis_fenc
/* Schedule deferred transactions */
if (!list_empty(&client->transaction_process_queue)) {
if (i2c_bus_manager) {
- kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work);
+ 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);