summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill McVicker <willmcvicker@google.com>2024-04-15 11:14:18 -0700
committerWill McVicker <willmcvicker@google.com>2024-04-15 11:19:24 -0700
commit951a7837ea2391035fe384a81b14748d093926b6 (patch)
tree821286663e7ba94b7dd5db4b6da28b1edb3cbf19
parente5d0b690ab61ca932240b5a77b05214397112a7a (diff)
parente93b073b2f1f5f9a0c479714efceb9ac78c027cd (diff)
downloadlwis-android14-gs-pixel-6.1.tar.gz
Merge aosp/android-gs-raviole-5.10-android14-qpr2 into aosp/android14-gs-pixel-6.1android14-gs-pixel-6.1
* aosp/android-gs-raviole-5.10-android14-qpr2: LWIS: fix race condition LWIS: Fix spinlock flags are overwritten by the nested using LWIS: Add mutex lock to I2C process queue LWIS: Check transaction limit based on flag LWIS: Fix race condition while disabling device LWIS: Transactions optimizations LWIS: Handle device flush for transactions with limit LWIS: Move transaction limit logging LWIS: Update read buffer pointer LWIS: Fix the processing end limit for transactions LWIS: Update the trace information LWIS: Pass irq state to correctly restore transaction lock LWIS: Fix use of transaction pointer after free LWIS: Fix transaction free Add entry processing limit for queued transactions LWIS: Fix memory leak Revert^2 "Optimize I2C Bus manager scheduling" Revert^3 "Revert "LWIS: Implement I2C Bus Manager"" Revert^2 "Revert "LWIS: Implement I2C Bus Manager"" Revert "Optimize I2C Bus manager scheduling" Optimize I2C Bus manager scheduling Revert "Revert "LWIS: Implement I2C Bus Manager"" Revert "LWIS: Implement I2C Bus Manager" LWIS: Implement I2C Bus Manager Top: Use GFP_ATOMIC flag in kmalloc IOEntry: Allow max wait time of 1 second IO Entry: Support Wait and Poll in short interval functions Debug: Fix formatting on dump state logs I2C: Remove redundant parameter in lwis_i2c_io_entry_rw LWIS: Remove fence enable flag LWIS: Remove partial fence enable flag LWIS: Remove bts block name enable flag LWIS: Allow user input GFP flags for different use cases Revert "LWIS: print cleanup transaction read result" LWIS: Use IS_ERR_OR_NULL instead of IS_ERR LWIS: Reset variables when error LWIS: implement the fake injection in Kernel side Transaction: Add debug parameter to skip transaction timestamping Fix formatting. LWIS: test infra refactor: implement the lwis_test device read the interrupt info from dts tree. LWIS: Fix stack size warning LWIS: Add a new command for current LWIS_BTS_BLOCK_NAME_ENABLED on LWIS: Remove partial bts block name enable flag LWIS: test infra refactor: add more functions for lwis-test device. LWIS: do not update BTS for unsupported devices LWIS: Clean up TODO for flash driver Top: Fix lwis_top_event_subscribe() UAF Change-Id: I0844e4e5efd56eb6e97cdc90eabbf1531ae0609e Signed-off-by: Will McVicker <willmcvicker@google.com>
-rw-r--r--Kbuild9
-rw-r--r--lwis_allocator.c10
-rw-r--r--lwis_allocator.h2
-rw-r--r--lwis_clock.c2
-rw-r--r--lwis_cmd.c1433
-rw-r--r--lwis_cmd.h23
-rw-r--r--lwis_commands.h106
-rw-r--r--lwis_debug.c153
-rw-r--r--lwis_debug.h7
-rw-r--r--lwis_device.c72
-rw-r--r--lwis_device.h37
-rw-r--r--lwis_device_dpm.c12
-rw-r--r--lwis_device_dpm.h2
-rw-r--r--lwis_device_i2c.c45
-rw-r--r--lwis_device_i2c.h7
-rw-r--r--lwis_device_ioreg.c7
-rw-r--r--lwis_device_slc.c2
-rw-r--r--lwis_device_test.c134
-rw-r--r--lwis_device_test.h11
-rw-r--r--lwis_device_top.c21
-rw-r--r--lwis_device_top.h1
-rw-r--r--lwis_dt.c132
-rw-r--r--lwis_dt.h7
-rw-r--r--lwis_event.c26
-rw-r--r--lwis_fence.c29
-rw-r--r--lwis_fence.h78
-rw-r--r--lwis_i2c.c56
-rw-r--r--lwis_i2c.h3
-rw-r--r--lwis_i2c_bus_manager.c751
-rw-r--r--lwis_i2c_bus_manager.h118
-rw-r--r--lwis_i2c_sched.c62
-rw-r--r--lwis_i2c_sched.h33
-rw-r--r--lwis_interrupt.c10
-rw-r--r--lwis_interrupt.h5
-rw-r--r--lwis_io_entry.c23
-rw-r--r--lwis_io_entry.h15
-rw-r--r--lwis_ioctl.c1868
-rw-r--r--lwis_ioctl.h14
-rw-r--r--lwis_ioreg.c8
-rw-r--r--lwis_periodic_io.c88
-rw-r--r--lwis_phy.c2
-rw-r--r--lwis_pinctrl.c2
-rw-r--r--lwis_regulator.c2
-rw-r--r--lwis_trace.h13
-rw-r--r--lwis_transaction.c438
-rw-r--r--lwis_transaction.h19
-rw-r--r--lwis_util.c18
-rw-r--r--lwis_util.h8
-rw-r--r--lwis_version.c2
-rw-r--r--platform/anchorage/lwis_platform_anchorage.c55
-rw-r--r--platform/anchorage/lwis_platform_anchorage_dma.c4
-rw-r--r--platform/busan/lwis_platform_busan.c52
52 files changed, 3210 insertions, 2827 deletions
diff --git a/Kbuild b/Kbuild
index ce901ad..37d3f3a 100644
--- a/Kbuild
+++ b/Kbuild
@@ -23,7 +23,9 @@ lwis-objs += lwis_debug.o
lwis-objs += lwis_io_entry.o
lwis-objs += lwis_allocator.o
lwis-objs += lwis_version.o
-lwis-objs += lwis_cmd.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)
@@ -35,11 +37,6 @@ endif
ifeq ($(CONFIG_SOC_GS201), y)
lwis-objs += platform/busan/lwis_platform_busan.o
lwis-objs += platform/busan/lwis_platform_busan_dma.o
-# ccflags-y += -DLWIS_FENCE_ENABLED -DLWIS_BTS_BLOCK_NAME_ENABLED
-endif
-
-ifneq ($(filter -DLWIS_FENCE_ENABLED, $(ccflags-y)),)
-lwis-objs += lwis_fence.o
endif
# Device tree specific file
diff --git a/lwis_allocator.c b/lwis_allocator.c
index 3b64fbd..0e4d55d 100644
--- a/lwis_allocator.c
+++ b/lwis_allocator.c
@@ -237,7 +237,7 @@ void lwis_allocator_release(struct lwis_device *lwis_dev)
mutex_unlock(&lwis_dev->client_lock);
}
-void *lwis_allocator_allocate(struct lwis_device *lwis_dev, size_t size)
+void *lwis_allocator_allocate(struct lwis_device *lwis_dev, size_t size, gfp_t gfp_flags)
{
struct lwis_allocator_block_mgr *block_mgr;
struct lwis_allocator_block_pool *block_pool;
@@ -301,7 +301,7 @@ void *lwis_allocator_allocate(struct lwis_device *lwis_dev, size_t size)
* memory on hand.
*/
if (idx > 19) {
- block = kmalloc(sizeof(struct lwis_allocator_block), GFP_KERNEL);
+ block = kmalloc(sizeof(struct lwis_allocator_block), gfp_flags);
if (block == NULL) {
dev_err(lwis_dev->dev, "Allocate failed\n");
return NULL;
@@ -309,7 +309,7 @@ void *lwis_allocator_allocate(struct lwis_device *lwis_dev, size_t size)
block->type = idx;
block->next = NULL;
block->prev = NULL;
- block->ptr = kvmalloc(size, GFP_KERNEL);
+ block->ptr = kvmalloc(size, gfp_flags);
if (block->ptr == NULL) {
dev_err(lwis_dev->dev, "Allocate failed\n");
kfree(block);
@@ -336,7 +336,7 @@ void *lwis_allocator_allocate(struct lwis_device *lwis_dev, size_t size)
}
/* Allocate new block */
- block = kmalloc(sizeof(struct lwis_allocator_block), GFP_KERNEL);
+ block = kmalloc(sizeof(struct lwis_allocator_block), gfp_flags);
if (block == NULL) {
dev_err(lwis_dev->dev, "Allocate failed\n");
return NULL;
@@ -345,7 +345,7 @@ void *lwis_allocator_allocate(struct lwis_device *lwis_dev, size_t size)
block->next = NULL;
block->prev = NULL;
block_size = 1 << idx;
- block->ptr = kvmalloc(block_size, GFP_KERNEL);
+ block->ptr = kvmalloc(block_size, gfp_flags);
if (block->ptr == NULL) {
dev_err(lwis_dev->dev, "Allocate failed\n");
kfree(block);
diff --git a/lwis_allocator.h b/lwis_allocator.h
index 3809b60..b72f0ba 100644
--- a/lwis_allocator.h
+++ b/lwis_allocator.h
@@ -62,7 +62,7 @@ void lwis_allocator_release(struct lwis_device *lwis_dev);
/*
* lwis_allocator_allocate: Allocate a block from the recycling memory allocator
*/
-void *lwis_allocator_allocate(struct lwis_device *lwis_dev, size_t size);
+void *lwis_allocator_allocate(struct lwis_device *lwis_dev, size_t size, gfp_t gfp_flags);
/*
* lwis_allocator_free: Free a block to the recycling memory allocator
diff --git a/lwis_clock.c b/lwis_clock.c
index 4a9dc18..e03fe9d 100644
--- a/lwis_clock.c
+++ b/lwis_clock.c
@@ -82,7 +82,7 @@ int lwis_clock_get(struct lwis_clock_list *list, char *name, struct device *dev,
/* Make sure clock exists */
clk = devm_clk_get(dev, name);
- if (IS_ERR(clk)) {
+ if (IS_ERR_OR_NULL(clk)) {
pr_err("Clock %s not found\n", name);
return PTR_ERR(clk);
}
diff --git a/lwis_cmd.c b/lwis_cmd.c
deleted file mode 100644
index 9e82632..0000000
--- a/lwis_cmd.c
+++ /dev/null
@@ -1,1433 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Google LWIS Command packets
- *
- * Copyright (c) 2022 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.
- */
-
-#include "lwis_cmd.h"
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#include "lwis_allocator.h"
-#include "lwis_buffer.h"
-#include "lwis_commands.h"
-#include "lwis_device.h"
-#include "lwis_device_dpm.h"
-#include "lwis_device_i2c.h"
-#include "lwis_device_ioreg.h"
-#include "lwis_event.h"
-#include "lwis_fence.h"
-#include "lwis_i2c.h"
-#include "lwis_io_entry.h"
-#include "lwis_ioctl.h"
-#include "lwis_ioreg.h"
-#include "lwis_periodic_io.h"
-#include "lwis_platform.h"
-#include "lwis_transaction.h"
-#include "lwis_util.h"
-
-static int copy_pkt_to_user(struct lwis_device *lwis_dev, void __user *u_msg, void *k_msg,
- size_t size)
-{
- if (copy_to_user(u_msg, k_msg, size)) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes to user\n", size);
- return -EFAULT;
- }
-
- return 0;
-}
-
-static int cmd_echo(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
- struct lwis_cmd_echo __user *u_msg)
-{
- struct lwis_cmd_echo echo_msg;
- char *buffer = NULL;
-
- if (copy_from_user((void *)&echo_msg, (void __user *)u_msg, sizeof(echo_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(echo_msg));
- return -EFAULT;
- }
-
- if (echo_msg.msg.size == 0) {
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- buffer = kmalloc(echo_msg.msg.size + 1, GFP_KERNEL);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Failed to allocate buffer for echo message\n");
- header->ret_code = -ENOMEM;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
- if (copy_from_user(buffer, (void __user *)echo_msg.msg.msg, echo_msg.msg.size)) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes echo message from user\n",
- echo_msg.msg.size);
- kfree(buffer);
- header->ret_code = -EFAULT;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
- buffer[echo_msg.msg.size] = '\0';
-
- if (echo_msg.msg.kernel_log) {
- dev_info(lwis_dev->dev, "LWIS_ECHO: %s\n", buffer);
- }
- kfree(buffer);
-
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_time_query(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
- struct lwis_cmd_time_query __user *u_msg)
-{
- struct lwis_cmd_time_query time_query;
- time_query.timestamp_ns = ktime_to_ns(lwis_get_time());
- time_query.header.cmd_id = header->cmd_id;
- time_query.header.next = header->next;
- time_query.header.ret_code = 0;
-
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&time_query, sizeof(time_query));
-}
-
-static int cmd_get_device_info(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
- struct lwis_cmd_device_info __user *u_msg)
-{
- int i;
- struct lwis_cmd_device_info k_info = { .header.cmd_id = header->cmd_id,
- .header.next = header->next,
- .info.id = lwis_dev->id,
- .info.type = lwis_dev->type,
- .info.num_clks = 0,
- .info.num_regs = 0,
- .info.transaction_worker_thread_pid = -1,
- .info.periodic_io_thread_pid = -1 };
- strscpy(k_info.info.name, lwis_dev->name, LWIS_MAX_NAME_STRING_LEN);
-
- if (lwis_dev->clocks) {
- k_info.info.num_clks = lwis_dev->clocks->count;
- for (i = 0; i < lwis_dev->clocks->count; i++) {
- if (i >= LWIS_MAX_CLOCK_NUM) {
- dev_err(lwis_dev->dev,
- "Clock count larger than LWIS_MAX_CLOCK_NUM\n");
- break;
- }
- strscpy(k_info.info.clks[i].name, lwis_dev->clocks->clk[i].name,
- LWIS_MAX_NAME_STRING_LEN);
- k_info.info.clks[i].clk_index = i;
- k_info.info.clks[i].frequency = 0;
- }
- }
-
- if (lwis_dev->type == DEVICE_TYPE_IOREG) {
- struct lwis_ioreg_device *ioreg_dev;
- ioreg_dev = container_of(lwis_dev, struct lwis_ioreg_device, base_dev);
- if (ioreg_dev->reg_list.count > 0) {
- k_info.info.num_regs = ioreg_dev->reg_list.count;
- for (i = 0; i < ioreg_dev->reg_list.count; i++) {
- if (i >= LWIS_MAX_REG_NUM) {
- dev_err(lwis_dev->dev,
- "Reg count larger than LWIS_MAX_REG_NUM\n");
- break;
- }
- strscpy(k_info.info.regs[i].name, ioreg_dev->reg_list.block[i].name,
- LWIS_MAX_NAME_STRING_LEN);
- k_info.info.regs[i].reg_index = i;
- k_info.info.regs[i].start = ioreg_dev->reg_list.block[i].start;
- k_info.info.regs[i].size = ioreg_dev->reg_list.block[i].size;
- }
- }
- }
-
- if (lwis_dev->transaction_worker_thread) {
- k_info.info.transaction_worker_thread_pid =
- lwis_dev->transaction_worker_thread->pid;
- }
-
- k_info.header.ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_info, sizeof(k_info));
-}
-
-static int cmd_device_enable(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_pkt __user *u_msg)
-{
- int ret = 0;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (lwis_client->is_enabled) {
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- mutex_lock(&lwis_dev->client_lock);
- if (lwis_dev->enabled > 0 && lwis_dev->enabled < INT_MAX) {
- lwis_dev->enabled++;
- lwis_client->is_enabled = true;
- ret = 0;
- goto exit_locked;
- } else if (lwis_dev->enabled == INT_MAX) {
- dev_err(lwis_dev->dev, "Enable counter overflow\n");
- ret = -EINVAL;
- goto exit_locked;
- }
-
- /* Clear event queues to make sure there is no stale event from
- * previous session */
- lwis_client_event_queue_clear(lwis_client);
- lwis_client_error_event_queue_clear(lwis_client);
-
- ret = lwis_dev_power_up_locked(lwis_dev);
- if (ret < 0) {
- dev_err(lwis_dev->dev, "Failed to power up device\n");
- goto exit_locked;
- }
-
- lwis_dev->enabled++;
- lwis_client->is_enabled = true;
- lwis_dev->is_suspended = false;
- dev_info(lwis_dev->dev, "Device enabled\n");
-exit_locked:
- mutex_unlock(&lwis_dev->client_lock);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_device_disable(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_pkt __user *u_msg)
-{
- int ret = 0;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (!lwis_client->is_enabled) {
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- mutex_lock(&lwis_dev->client_lock);
- /* Clear event states for this client */
- lwis_client_event_states_clear(lwis_client);
- mutex_unlock(&lwis_dev->client_lock);
-
- /* Flush all periodic io to complete */
- ret = lwis_periodic_io_client_flush(lwis_client);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to wait for in-process periodic io to complete\n");
- }
-
- /* Flush all pending transactions */
- ret = lwis_transaction_client_flush(lwis_client);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to flush pending transactions\n");
- }
-
- /* Run cleanup transactions. */
- lwis_transaction_client_cleanup(lwis_client);
-
- mutex_lock(&lwis_dev->client_lock);
- if (lwis_dev->enabled > 1) {
- lwis_dev->enabled--;
- lwis_client->is_enabled = false;
- ret = 0;
- goto exit_locked;
- } else if (lwis_dev->enabled <= 0) {
- dev_err(lwis_dev->dev, "Disabling a device that is already disabled\n");
- ret = -EINVAL;
- goto exit_locked;
- }
-
- ret = lwis_dev_power_down_locked(lwis_dev);
- if (ret < 0) {
- dev_err(lwis_dev->dev, "Failed to power down device\n");
- goto exit_locked;
- }
- lwis_device_event_states_clear_locked(lwis_dev);
-
- lwis_dev->enabled--;
- lwis_client->is_enabled = false;
- lwis_dev->is_suspended = false;
- dev_info(lwis_dev->dev, "Device disabled\n");
-exit_locked:
- mutex_unlock(&lwis_dev->client_lock);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int copy_io_entries_from_cmd(struct lwis_device *lwis_dev,
- struct lwis_cmd_io_entries __user *u_msg,
- struct lwis_cmd_io_entries *k_msg,
- struct lwis_io_entry **k_entries)
-{
- struct lwis_io_entry *io_entries;
- uint32_t buf_size;
-
- /* Register io is not supported for the lwis device, return */
- if (!lwis_dev->vops.register_io) {
- dev_err(lwis_dev->dev, "Register IO not supported on this LWIS device\n");
- return -EINVAL;
- }
-
- /* Copy io_entries from userspace */
- if (copy_from_user(k_msg, (void __user *)u_msg, sizeof(*k_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy io_entries header from userspace.\n");
- return -EFAULT;
- }
- buf_size = sizeof(struct lwis_io_entry) * k_msg->io.num_io_entries;
- if (buf_size / sizeof(struct lwis_io_entry) != k_msg->io.num_io_entries) {
- dev_err(lwis_dev->dev, "Failed to copy io_entries due to integer overflow.\n");
- return -EOVERFLOW;
- }
- io_entries = lwis_allocator_allocate(lwis_dev, buf_size);
- if (!io_entries) {
- dev_err(lwis_dev->dev, "Failed to allocate io_entries buffer\n");
- return -ENOMEM;
- }
- if (copy_from_user(io_entries, (void __user *)k_msg->io.io_entries, buf_size)) {
- dev_err(lwis_dev->dev, "Failed to copy io_entries from userspace.\n");
- lwis_allocator_free(lwis_dev, io_entries);
- return -EFAULT;
- }
- *k_entries = io_entries;
-
- return 0;
-}
-
-static int cmd_device_reset(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_io_entries __user *u_msg)
-{
- int ret = 0;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
- struct lwis_cmd_io_entries k_msg;
- struct lwis_io_entry *k_entries = NULL;
- unsigned long flags;
- bool device_enabled = false;
-
- ret = copy_io_entries_from_cmd(lwis_dev, u_msg, &k_msg, &k_entries);
- if (ret) {
- goto soft_reset_exit;
- }
-
- /* Clear event states, event queues and transactions for this client */
- mutex_lock(&lwis_dev->client_lock);
- lwis_client_event_states_clear(lwis_client);
- lwis_client_event_queue_clear(lwis_client);
- lwis_client_error_event_queue_clear(lwis_client);
- device_enabled = lwis_dev->enabled;
- mutex_unlock(&lwis_dev->client_lock);
-
- /* Flush all periodic io to complete */
- ret = lwis_periodic_io_client_flush(lwis_client);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to wait for in-process periodic io to complete\n");
- }
-
- /* Flush all pending transactions */
- ret = lwis_transaction_client_flush(lwis_client);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to flush all pending transactions\n");
- }
-
- /* Perform reset routine defined by the io_entries */
- if (device_enabled) {
- ret = lwis_ioctl_util_synchronous_process_io_entries(
- lwis_dev, k_msg.io.num_io_entries, k_entries, k_msg.io.io_entries);
- } else {
- dev_warn(lwis_dev->dev,
- "Device is not enabled, IoEntries will not be executed in DEVICE_RESET\n");
- }
-
- spin_lock_irqsave(&lwis_dev->lock, flags);
- lwis_device_event_states_clear_locked(lwis_dev);
- spin_unlock_irqrestore(&lwis_dev->lock, flags);
-soft_reset_exit:
- if (k_entries) {
- lwis_allocator_free(lwis_dev, k_entries);
- }
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_device_suspend(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_pkt __user *u_msg)
-{
- int ret = 0;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (!lwis_dev->suspend_sequence) {
- dev_err(lwis_dev->dev, "No suspend sequence defined\n");
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- if (!lwis_client->is_enabled) {
- dev_err(lwis_dev->dev, "Trying to suspend a disabled device\n");
- header->ret_code = -EINVAL;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- if (lwis_dev->is_suspended) {
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- mutex_lock(&lwis_dev->client_lock);
- /* Clear event states for this client */
- lwis_client_event_states_clear(lwis_client);
- mutex_unlock(&lwis_dev->client_lock);
-
- /* Flush all periodic io to complete */
- ret = lwis_periodic_io_client_flush(lwis_client);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to wait for in-process periodic io to complete\n");
- }
-
- /* Flush all pending transactions */
- ret = lwis_transaction_client_flush(lwis_client);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to flush pending transactions\n");
- }
-
- /* Run cleanup transactions. */
- lwis_transaction_client_cleanup(lwis_client);
-
- mutex_lock(&lwis_dev->client_lock);
- ret = lwis_dev_process_power_sequence(lwis_dev, lwis_dev->suspend_sequence,
- /*set_active=*/false, /*skip_error=*/false);
- if (ret) {
- dev_err(lwis_dev->dev, "Error lwis_dev_process_power_sequence (%d)\n", ret);
- goto exit_locked;
- }
-
- lwis_device_event_states_clear_locked(lwis_dev);
-
- lwis_dev->is_suspended = true;
- dev_info(lwis_dev->dev, "Device suspended\n");
-exit_locked:
- mutex_unlock(&lwis_dev->client_lock);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_device_resume(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_pkt __user *u_msg)
-{
- int ret = 0;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (!lwis_dev->resume_sequence) {
- dev_err(lwis_dev->dev, "No resume sequence defined\n");
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- if (!lwis_dev->is_suspended) {
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- mutex_lock(&lwis_dev->client_lock);
- /* Clear event queues to make sure there is no stale event from
- * previous session */
- lwis_client_event_queue_clear(lwis_client);
- lwis_client_error_event_queue_clear(lwis_client);
-
- ret = lwis_dev_process_power_sequence(lwis_dev, lwis_dev->resume_sequence,
- /*set_active=*/true, /*skip_error=*/false);
- if (ret) {
- dev_err(lwis_dev->dev, "Error lwis_dev_process_power_sequence (%d)\n", ret);
- goto exit_locked;
- }
-
- lwis_dev->is_suspended = false;
- dev_info(lwis_dev->dev, "Device resumed\n");
-exit_locked:
- mutex_unlock(&lwis_dev->client_lock);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dump_debug_state(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_pkt __user *u_msg)
-{
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- mutex_lock(&lwis_dev->client_lock);
- /* Dump lwis device crash info */
- lwis_device_crash_info_dump(lwis_dev);
- mutex_unlock(&lwis_dev->client_lock);
-
- header->ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dma_buffer_enroll(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_dma_buffer_enroll __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_dma_buffer_enroll buf_info;
- struct lwis_enrolled_buffer *buffer;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- buffer = kmalloc(sizeof(*buffer), GFP_KERNEL);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Failed to allocate lwis_enrolled_buffer struct\n");
- header->ret_code = -ENOMEM;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- if (copy_from_user((void *)&buf_info, (void __user *)u_msg, sizeof(buf_info))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(buf_info));
- ret = -EFAULT;
- goto error_enroll;
- }
-
- buffer->info.fd = buf_info.info.fd;
- buffer->info.dma_read = buf_info.info.dma_read;
- buffer->info.dma_write = buf_info.info.dma_write;
-
- ret = lwis_buffer_enroll(lwis_client, buffer);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to enroll buffer\n");
- goto error_enroll;
- }
-
- buf_info.info.dma_vaddr = buffer->info.dma_vaddr;
- buf_info.header.cmd_id = header->cmd_id;
- buf_info.header.next = header->next;
- buf_info.header.ret_code = ret;
- ret = copy_pkt_to_user(lwis_dev, u_msg, (void *)&buf_info, sizeof(buf_info));
- if (ret) {
- lwis_buffer_disenroll(lwis_client, buffer);
- goto error_enroll;
- }
-
- return ret;
-
-error_enroll:
- kfree(buffer);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dma_buffer_disenroll(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_dma_buffer_disenroll __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_dma_buffer_disenroll info;
- struct lwis_enrolled_buffer *buffer;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (copy_from_user((void *)&info, (void __user *)u_msg, sizeof(info))) {
- dev_err(lwis_dev->dev, "Failed to copy DMA virtual address from user\n");
- return -EFAULT;
- }
-
- buffer = lwis_client_enrolled_buffer_find(lwis_client, info.info.fd, info.info.dma_vaddr);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Failed to find dma buffer for fd %d vaddr %pad\n",
- info.info.fd, &info.info.dma_vaddr);
- header->ret_code = -ENOENT;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- ret = lwis_buffer_disenroll(lwis_client, buffer);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to disenroll dma buffer for fd %d vaddr %pad\n",
- info.info.fd, &info.info.dma_vaddr);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- kfree(buffer);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dma_buffer_cpu_access(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_dma_buffer_cpu_access __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_dma_buffer_cpu_access op;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (copy_from_user((void *)&op, (void __user *)u_msg, sizeof(op))) {
- dev_err(lwis_dev->dev, "Failed to copy buffer CPU access operation from user\n");
- return -EFAULT;
- }
-
- ret = lwis_buffer_cpu_access(lwis_client, &op.op);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to prepare for cpu access for fd %d\n", op.op.fd);
- }
-
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dma_buffer_alloc(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_dma_buffer_alloc __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_dma_buffer_alloc alloc_info;
- struct lwis_allocated_buffer *buffer;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- buffer = kmalloc(sizeof(*buffer), GFP_KERNEL);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Failed to allocated lwis_allocated_buffer\n");
- return -ENOMEM;
- }
-
- if (copy_from_user((void *)&alloc_info, (void __user *)u_msg, sizeof(alloc_info))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(alloc_info));
- ret = -EFAULT;
- goto error_alloc;
- }
-
- ret = lwis_buffer_alloc(lwis_client, &alloc_info.info, buffer);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to allocate buffer\n");
- goto error_alloc;
- }
-
- alloc_info.header.ret_code = 0;
- ret = copy_pkt_to_user(lwis_dev, u_msg, (void *)&alloc_info, sizeof(alloc_info));
- if (ret) {
- lwis_buffer_free(lwis_client, buffer);
- ret = -EFAULT;
- goto error_alloc;
- }
-
- return ret;
-
-error_alloc:
- kfree(buffer);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dma_buffer_free(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_dma_buffer_free __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_dma_buffer_free info;
- struct lwis_allocated_buffer *buffer;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (copy_from_user((void *)&info, (void __user *)u_msg, sizeof(info))) {
- dev_err(lwis_dev->dev, "Failed to copy file descriptor from user\n");
- return -EFAULT;
- }
-
- buffer = lwis_client_allocated_buffer_find(lwis_client, info.fd);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Cannot find allocated buffer FD %d\n", info.fd);
- header->ret_code = -ENOENT;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- ret = lwis_buffer_free(lwis_client, buffer);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to free buffer FD %d\n", info.fd);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- kfree(buffer);
-
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_reg_io(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
- struct lwis_cmd_io_entries __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_io_entries k_msg;
- struct lwis_io_entry *k_entries = NULL;
-
- ret = copy_io_entries_from_cmd(lwis_dev, u_msg, &k_msg, &k_entries);
- if (ret) {
- goto reg_io_exit;
- }
-
- /* Walk through and execute the entries */
- ret = lwis_ioctl_util_synchronous_process_io_entries(lwis_dev, k_msg.io.num_io_entries,
- k_entries, k_msg.io.io_entries);
-
-reg_io_exit:
- if (k_entries) {
- lwis_allocator_free(lwis_dev, k_entries);
- }
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_event_control_get(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_event_control_get __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_event_control_get control;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (copy_from_user((void *)&control, (void __user *)u_msg, sizeof(control))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(control));
- return -EFAULT;
- }
-
- ret = lwis_client_event_control_get(lwis_client, control.ctl.event_id, &control.ctl);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to get event: %lld (err:%d)\n", control.ctl.event_id,
- ret);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- control.header.ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&control, sizeof(control));
-}
-
-static int cmd_event_control_set(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_event_control_set __user *u_msg)
-{
- struct lwis_cmd_event_control_set k_msg;
- struct lwis_event_control *k_event_controls;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
- int ret = 0;
- int i;
- size_t buf_size;
-
- if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy ioctl message from user\n");
- return -EFAULT;
- }
-
- /* Copy event controls from user buffer. */
- buf_size = sizeof(struct lwis_event_control) * k_msg.list.num_event_controls;
- if (buf_size / sizeof(struct lwis_event_control) != k_msg.list.num_event_controls) {
- dev_err(lwis_dev->dev, "Failed to copy event controls due to integer overflow.\n");
- header->ret_code = -EOVERFLOW;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
- k_event_controls = kmalloc(buf_size, GFP_KERNEL);
- if (!k_event_controls) {
- dev_err(lwis_dev->dev, "Failed to allocate event controls\n");
- header->ret_code = -ENOMEM;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
- if (copy_from_user(k_event_controls, (void __user *)k_msg.list.event_controls, buf_size)) {
- dev_err(lwis_dev->dev, "Failed to copy event controls from user\n");
- ret = -EFAULT;
- goto exit;
- }
-
- for (i = 0; i < k_msg.list.num_event_controls; i++) {
- ret = lwis_client_event_control_set(lwis_client, &k_event_controls[i]);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to apply event control 0x%llx\n",
- k_event_controls[i].event_id);
- goto exit;
- }
- }
-exit:
- kfree(k_event_controls);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_event_dequeue(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_event_dequeue __user *u_msg)
-{
- struct lwis_cmd_event_dequeue info;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
- struct lwis_event_entry *event;
- int ret = 0;
- int err = 0;
- bool is_error_event = false;
-
- if (copy_from_user((void *)&info, (void __user *)u_msg, sizeof(info))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(info));
- return -EFAULT;
- }
-
- mutex_lock(&lwis_dev->client_lock);
- /* Peek at the front element of error event queue first */
- ret = lwis_client_error_event_peek_front(lwis_client, &event);
- if (ret == 0) {
- is_error_event = true;
- } else if (ret != -ENOENT) {
- dev_err(lwis_dev->dev, "Error dequeueing error event: %d\n", ret);
- mutex_unlock(&lwis_dev->client_lock);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- } else {
- /* Nothing at error event queue, continue to check normal
- * event queue */
- ret = lwis_client_event_peek_front(lwis_client, &event);
- if (ret) {
- if (ret != -ENOENT) {
- dev_err(lwis_dev->dev, "Error dequeueing event: %d\n", ret);
- }
- mutex_unlock(&lwis_dev->client_lock);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
- }
-
- /* We need to check if we have an adequate payload buffer */
- if (event->event_info.payload_size > info.info.payload_buffer_size) {
- /* Nope, we don't. Let's inform the user and bail */
- info.info.payload_size = event->event_info.payload_size;
- err = -EAGAIN;
- } else {
- info.info.event_id = event->event_info.event_id;
- info.info.event_counter = event->event_info.event_counter;
- info.info.timestamp_ns = event->event_info.timestamp_ns;
- info.info.payload_size = event->event_info.payload_size;
-
- /* Here we have a payload and the buffer is big enough */
- if (event->event_info.payload_size > 0 && info.info.payload_buffer) {
- /* Copy over the payload buffer to userspace */
- if (copy_to_user((void __user *)info.info.payload_buffer,
- (void *)event->event_info.payload_buffer,
- event->event_info.payload_size)) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes to user\n",
- event->event_info.payload_size);
- mutex_unlock(&lwis_dev->client_lock);
- return -EFAULT;
- }
- }
- }
- /* If we didn't -EAGAIN up above, we can pop and discard the front of
- * the event queue because we're done dealing with it. If we got the
- * -EAGAIN case, we didn't actually dequeue this event and userspace
- * should try again with a bigger payload_buffer.
- */
- if (!err) {
- if (is_error_event) {
- ret = lwis_client_error_event_pop_front(lwis_client, NULL);
- } else {
- ret = lwis_client_event_pop_front(lwis_client, NULL);
- }
- if (ret) {
- dev_err(lwis_dev->dev, "Error dequeueing event: %d\n", ret);
- mutex_unlock(&lwis_dev->client_lock);
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
- }
- mutex_unlock(&lwis_dev->client_lock);
- /* Now let's copy the actual info struct back to user */
- info.header.ret_code = err;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&info, sizeof(info));
-}
-
-static int construct_transaction_from_cmd(struct lwis_client *client,
- struct lwis_cmd_transaction_info __user *u_msg,
- struct lwis_transaction **transaction)
-{
- int ret;
- struct lwis_cmd_transaction_info k_info;
- struct lwis_transaction *k_transaction;
- struct lwis_device *lwis_dev = client->lwis_dev;
-
- k_transaction = kmalloc(sizeof(*k_transaction), GFP_KERNEL);
- if (!k_transaction) {
- dev_err(lwis_dev->dev, "Failed to allocate transaction info\n");
- return -ENOMEM;
- }
-
- if (copy_from_user((void *)&k_info, (void __user *)u_msg, sizeof(k_info))) {
- dev_err(lwis_dev->dev, "Failed to copy transaction info from user\n");
- ret = -EFAULT;
- goto error_free_transaction;
- }
-
- memcpy(&k_transaction->info, &k_info.info, sizeof(k_transaction->info));
-
- ret = lwis_ioctl_util_construct_io_entry(client, k_transaction->info.io_entries,
- k_transaction->info.num_io_entries,
- &k_transaction->info.io_entries);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to prepare lwis io entries for transaction\n");
- goto error_free_transaction;
- }
-
- k_transaction->resp = NULL;
- k_transaction->is_weak_transaction = false;
- INIT_LIST_HEAD(&k_transaction->event_list_node);
- INIT_LIST_HEAD(&k_transaction->process_queue_node);
- INIT_LIST_HEAD(&k_transaction->completion_fence_list);
-
- *transaction = k_transaction;
- return 0;
-
-error_free_transaction:
- kfree(k_transaction);
- return ret;
-}
-
-static int cmd_transaction_submit(struct lwis_client *client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_transaction_info __user *u_msg)
-{
- struct lwis_transaction *k_transaction = NULL;
- struct lwis_cmd_transaction_info k_transaction_info;
- struct lwis_device *lwis_dev = client->lwis_dev;
- int ret = 0;
- unsigned long flags;
-
- if (lwis_dev->type == DEVICE_TYPE_SLC || lwis_dev->type == DEVICE_TYPE_DPM) {
- dev_err(lwis_dev->dev, "not supported device type: %d\n", lwis_dev->type);
- ret = -EINVAL;
- goto err_exit;
- }
-
- ret = construct_transaction_from_cmd(client, u_msg, &k_transaction);
- if (ret) {
- goto err_exit;
- }
-
- ret = lwis_initialize_transaction_fences(client, k_transaction);
- if (ret) {
- lwis_transaction_free(lwis_dev, k_transaction);
- goto err_exit;
- }
-
- spin_lock_irqsave(&client->transaction_lock, flags);
- ret = lwis_transaction_submit_locked(client, k_transaction);
- k_transaction_info.info = k_transaction->info;
- spin_unlock_irqrestore(&client->transaction_lock, flags);
- if (ret) {
- k_transaction_info.info.id = LWIS_ID_INVALID;
- lwis_transaction_free(lwis_dev, k_transaction);
- }
-
- k_transaction_info.header.cmd_id = header->cmd_id;
- k_transaction_info.header.next = header->next;
- k_transaction_info.header.ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_transaction_info,
- sizeof(k_transaction_info));
-
-err_exit:
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_transaction_cancel(struct lwis_client *client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_transaction_cancel __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_transaction_cancel k_msg;
- struct lwis_device *lwis_dev = client->lwis_dev;
-
- if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy transaction ID from user\n");
- return -EFAULT;
- }
-
- ret = lwis_transaction_cancel(client, k_msg.id);
- if (ret) {
- dev_info_ratelimited(
- lwis_dev->dev,
- "Transaction id 0x%llx does not exist or is already done, not available for cancel(%d)\n",
- k_msg.id, ret);
- }
-
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_transaction_replace(struct lwis_client *client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_transaction_info __user *u_msg)
-{
- struct lwis_transaction *k_transaction = NULL;
- struct lwis_cmd_transaction_info k_transaction_info;
- struct lwis_device *lwis_dev = client->lwis_dev;
- int ret = 0;
- unsigned long flags;
-
- ret = construct_transaction_from_cmd(client, u_msg, &k_transaction);
- if (ret) {
- goto err_exit;
- }
-
- ret = lwis_initialize_transaction_fences(client, k_transaction);
- if (ret) {
- lwis_transaction_free(lwis_dev, k_transaction);
- goto err_exit;
- }
-
- spin_lock_irqsave(&client->transaction_lock, flags);
- ret = lwis_transaction_replace_locked(client, k_transaction);
- k_transaction_info.info = k_transaction->info;
- spin_unlock_irqrestore(&client->transaction_lock, flags);
- if (ret) {
- k_transaction_info.info.id = LWIS_ID_INVALID;
- lwis_transaction_free(lwis_dev, k_transaction);
- }
-
- k_transaction_info.header.cmd_id = header->cmd_id;
- k_transaction_info.header.next = header->next;
- k_transaction_info.header.ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_transaction_info,
- sizeof(k_transaction_info));
-
-err_exit:
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int construct_periodic_io_from_cmd(struct lwis_client *client,
- struct lwis_cmd_periodic_io_info __user *u_msg,
- struct lwis_periodic_io **periodic_io)
-{
- int ret = 0;
- struct lwis_periodic_io *k_periodic_io;
- struct lwis_cmd_periodic_io_info k_info;
- struct lwis_device *lwis_dev = client->lwis_dev;
-
- k_periodic_io = kmalloc(sizeof(struct lwis_periodic_io), GFP_KERNEL);
- if (!k_periodic_io) {
- dev_err(lwis_dev->dev, "Failed to allocate periodic io\n");
- return -ENOMEM;
- }
-
- if (copy_from_user((void *)&k_info, (void __user *)u_msg, sizeof(k_info))) {
- dev_err(lwis_dev->dev, "Failed to copy periodic io info from user\n");
- ret = -EFAULT;
- goto error_free_periodic_io;
- }
-
- memcpy(&k_periodic_io->info, &k_info.info, sizeof(k_periodic_io->info));
-
- ret = lwis_ioctl_util_construct_io_entry(client, k_periodic_io->info.io_entries,
- k_periodic_io->info.num_io_entries,
- &k_periodic_io->info.io_entries);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to prepare lwis io entries for periodic io\n");
- goto error_free_periodic_io;
- }
-
- k_periodic_io->resp = NULL;
- k_periodic_io->periodic_io_list = NULL;
-
- *periodic_io = k_periodic_io;
- return 0;
-
-error_free_periodic_io:
- kfree(k_periodic_io);
- return ret;
-}
-
-static int cmd_periodic_io_submit(struct lwis_client *client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_periodic_io_info __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_periodic_io_info k_periodic_io_info;
- struct lwis_periodic_io *k_periodic_io = NULL;
- struct lwis_device *lwis_dev = client->lwis_dev;
-
- ret = construct_periodic_io_from_cmd(client, u_msg, &k_periodic_io);
- if (ret) {
- goto err_exit;
- }
-
- ret = lwis_periodic_io_submit(client, k_periodic_io);
- k_periodic_io_info.info = k_periodic_io->info;
- if (ret) {
- k_periodic_io_info.info.id = LWIS_ID_INVALID;
- lwis_periodic_io_free(lwis_dev, k_periodic_io);
- goto err_exit;
- }
-
- k_periodic_io_info.header.cmd_id = header->cmd_id;
- k_periodic_io_info.header.next = header->next;
- k_periodic_io_info.header.ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_periodic_io_info,
- sizeof(k_periodic_io_info));
-
-err_exit:
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_periodic_io_cancel(struct lwis_client *client, struct lwis_cmd_pkt *header,
- struct lwis_cmd_periodic_io_cancel __user *u_msg)
-{
- int ret = 0;
- struct lwis_cmd_periodic_io_cancel k_msg;
- struct lwis_device *lwis_dev = client->lwis_dev;
-
- if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy periodic io ID from user\n");
- return -EFAULT;
- }
-
- ret = lwis_periodic_io_cancel(client, k_msg.id);
- if (ret) {
- dev_err_ratelimited(lwis_dev->dev, "Failed to clear periodic io id 0x%llx\n",
- k_msg.id);
- }
-
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dpm_clk_update(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
- struct lwis_cmd_dpm_clk_update __user *u_msg)
-{
- int ret;
- struct lwis_cmd_dpm_clk_update k_msg;
- struct lwis_clk_setting *clk_settings;
- size_t buf_size;
-
- if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy ioctl message from user\n");
- return -EFAULT;
- }
-
- buf_size = sizeof(struct lwis_clk_setting) * k_msg.settings.num_settings;
- if (buf_size / sizeof(struct lwis_clk_setting) != k_msg.settings.num_settings) {
- dev_err(lwis_dev->dev, "Failed to copy clk settings due to integer overflow.\n");
- ret = -EOVERFLOW;
- goto exit;
- }
- clk_settings = kmalloc(buf_size, GFP_KERNEL);
- if (!clk_settings) {
- dev_err(lwis_dev->dev, "Failed to allocate clock settings\n");
- ret = -ENOMEM;
- goto exit;
- }
-
- if (copy_from_user(clk_settings, (void __user *)k_msg.settings.settings, buf_size)) {
- dev_err(lwis_dev->dev, "Failed to copy clk settings from user\n");
- kfree(clk_settings);
- ret = -EFAULT;
- goto exit;
- }
-
- ret = lwis_dpm_update_clock(lwis_dev, clk_settings, k_msg.settings.num_settings);
- kfree(clk_settings);
-exit:
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dpm_qos_update(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
- struct lwis_cmd_dpm_qos_update __user *u_msg)
-{
- struct lwis_cmd_dpm_qos_update k_msg;
- struct lwis_qos_setting *k_qos_settings;
- int ret = 0;
- int i;
- size_t buf_size;
-
- if (lwis_dev->type != DEVICE_TYPE_DPM) {
- dev_err(lwis_dev->dev, "not supported device type: %d\n", lwis_dev->type);
- ret = -EINVAL;
- goto exit;
- }
-
- if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy ioctl message from user\n");
- return -EFAULT;
- }
-
- // Copy qos settings from user buffer.
- buf_size = sizeof(struct lwis_qos_setting) * k_msg.reqs.num_settings;
- if (buf_size / sizeof(struct lwis_qos_setting) != k_msg.reqs.num_settings) {
- dev_err(lwis_dev->dev, "Failed to copy qos settings due to integer overflow.\n");
- ret = -EOVERFLOW;
- goto exit;
- }
- k_qos_settings = kmalloc(buf_size, GFP_KERNEL);
- if (!k_qos_settings) {
- dev_err(lwis_dev->dev, "Failed to allocate qos settings\n");
- ret = -ENOMEM;
- goto exit;
- }
- if (copy_from_user(k_qos_settings, (void __user *)k_msg.reqs.qos_settings, buf_size)) {
- dev_err(lwis_dev->dev, "Failed to copy clk settings from user\n");
- kfree(k_qos_settings);
- ret = -EFAULT;
- goto exit;
- }
-
- for (i = 0; i < k_msg.reqs.num_settings; i++) {
- ret = lwis_dpm_update_qos(lwis_dev, &k_qos_settings[i]);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to apply qos setting, ret: %d\n", ret);
- kfree(k_qos_settings);
- goto exit;
- }
- }
- kfree(k_qos_settings);
-exit:
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-static int cmd_dpm_get_clock(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
- struct lwis_cmd_dpm_clk_get __user *u_msg)
-{
- struct lwis_cmd_dpm_clk_get current_setting;
- struct lwis_device *target_device;
- int ret = 0;
-
- if (lwis_dev->type != DEVICE_TYPE_DPM) {
- dev_err(lwis_dev->dev, "not supported device type: %d\n", lwis_dev->type);
- ret = -EINVAL;
- goto err_exit;
- }
-
- if (copy_from_user((void *)&current_setting, (void __user *)u_msg,
- sizeof(current_setting))) {
- dev_err(lwis_dev->dev, "failed to copy from user\n");
- return -EFAULT;
- }
-
- target_device = lwis_find_dev_by_id(current_setting.setting.device_id);
- if (!target_device) {
- dev_err(lwis_dev->dev, "could not find lwis device by id %d\n",
- current_setting.setting.device_id);
- ret = -ENODEV;
- goto err_exit;
- }
-
- if (target_device->enabled == 0 && target_device->type != DEVICE_TYPE_DPM) {
- dev_warn(target_device->dev, "%s disabled, can't get clk\n", target_device->name);
- ret = -EPERM;
- goto err_exit;
- }
-
- current_setting.setting.frequency_hz = (int64_t)lwis_dpm_read_clock(target_device);
- current_setting.header.ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&current_setting, sizeof(current_setting));
-
-err_exit:
- header->ret_code = ret;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
-}
-
-#ifdef LWIS_FENCE_ENABLED
-static int cmd_fence_create(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
- struct lwis_cmd_fence_create __user *u_msg)
-{
- int32_t fd_or_err;
- struct lwis_cmd_fence_create fence_create;
-
- if (copy_from_user((void *)&fence_create, (void __user *)u_msg, sizeof(fence_create))) {
- dev_err(lwis_dev->dev, "failed to copy from user\n");
- return -EFAULT;
- }
-
- fd_or_err = lwis_fence_create(lwis_dev);
- if (fd_or_err < 0) {
- header->ret_code = fd_or_err;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
- }
-
- fence_create.fd = fd_or_err;
- fence_create.header.ret_code = 0;
- return copy_pkt_to_user(lwis_dev, u_msg, (void *)&fence_create, sizeof(fence_create));
-}
-#endif
-
-int lwis_ioctl_handle_cmd_pkt(struct lwis_client *lwis_client, struct lwis_cmd_pkt __user *user_msg)
-{
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
- struct lwis_cmd_pkt header;
- int ret = 0;
-
- while (user_msg) {
- /* Copy cmd packet header from userspace */
- if (copy_from_user(&header, (void __user *)user_msg, sizeof(header))) {
- dev_err(lwis_dev->dev,
- "Failed to copy cmd packet header from userspace.\n");
- return -EFAULT;
- }
-
- switch (header.cmd_id) {
- case LWIS_CMD_ID_ECHO:
- ret = cmd_echo(lwis_dev, &header, (struct lwis_cmd_echo __user *)user_msg);
- break;
- case LWIS_CMD_ID_TIME_QUERY:
- ret = cmd_time_query(lwis_dev, &header,
- (struct lwis_cmd_time_query __user *)user_msg);
- break;
- case LWIS_CMD_ID_GET_DEVICE_INFO:
- mutex_lock(&lwis_client->lock);
- ret = cmd_get_device_info(lwis_dev, &header,
- (struct lwis_cmd_device_info __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DEVICE_ENABLE:
- mutex_lock(&lwis_client->lock);
- ret = cmd_device_enable(lwis_client, &header,
- (struct lwis_cmd_pkt __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DEVICE_DISABLE:
- mutex_lock(&lwis_client->lock);
- ret = cmd_device_disable(lwis_client, &header,
- (struct lwis_cmd_pkt __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DEVICE_RESET:
- mutex_lock(&lwis_client->lock);
- ret = cmd_device_reset(lwis_client, &header,
- (struct lwis_cmd_io_entries __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DEVICE_SUSPEND:
- mutex_lock(&lwis_client->lock);
- ret = cmd_device_suspend(lwis_client, &header,
- (struct lwis_cmd_pkt __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DEVICE_RESUME:
- mutex_lock(&lwis_client->lock);
- ret = cmd_device_resume(lwis_client, &header,
- (struct lwis_cmd_pkt __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DUMP_DEBUG_STATE:
- ret = cmd_dump_debug_state(lwis_client, &header,
- (struct lwis_cmd_pkt __user *)user_msg);
- break;
- case LWIS_CMD_ID_DMA_BUFFER_ENROLL:
- mutex_lock(&lwis_client->lock);
- ret = cmd_dma_buffer_enroll(
- lwis_client, &header,
- (struct lwis_cmd_dma_buffer_enroll __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DMA_BUFFER_DISENROLL:
- mutex_lock(&lwis_client->lock);
- ret = cmd_dma_buffer_disenroll(
- lwis_client, &header,
- (struct lwis_cmd_dma_buffer_disenroll __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DMA_BUFFER_CPU_ACCESS:
- mutex_lock(&lwis_client->lock);
- ret = cmd_dma_buffer_cpu_access(
- lwis_client, &header,
- (struct lwis_cmd_dma_buffer_cpu_access __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DMA_BUFFER_ALLOC:
- mutex_lock(&lwis_client->lock);
- ret = cmd_dma_buffer_alloc(
- lwis_client, &header,
- (struct lwis_cmd_dma_buffer_alloc __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DMA_BUFFER_FREE:
- mutex_lock(&lwis_client->lock);
- ret = cmd_dma_buffer_free(
- lwis_client, &header,
- (struct lwis_cmd_dma_buffer_free __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_REG_IO:
- mutex_lock(&lwis_client->lock);
- ret = cmd_reg_io(lwis_dev, &header,
- (struct lwis_cmd_io_entries __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_EVENT_CONTROL_GET:
- mutex_lock(&lwis_client->lock);
- ret = cmd_event_control_get(
- lwis_client, &header,
- (struct lwis_cmd_event_control_get __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_EVENT_CONTROL_SET:
- mutex_lock(&lwis_client->lock);
- ret = cmd_event_control_set(
- lwis_client, &header,
- (struct lwis_cmd_event_control_set __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_EVENT_DEQUEUE:
- ret = cmd_event_dequeue(lwis_client, &header,
- (struct lwis_cmd_event_dequeue __user *)user_msg);
- break;
- case LWIS_CMD_ID_TRANSACTION_SUBMIT:
- mutex_lock(&lwis_client->lock);
- ret = cmd_transaction_submit(
- lwis_client, &header,
- (struct lwis_cmd_transaction_info __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_TRANSACTION_CANCEL:
- mutex_lock(&lwis_client->lock);
- ret = cmd_transaction_cancel(
- lwis_client, &header,
- (struct lwis_cmd_transaction_cancel __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_TRANSACTION_REPLACE:
- mutex_lock(&lwis_client->lock);
- ret = cmd_transaction_replace(
- lwis_client, &header,
- (struct lwis_cmd_transaction_info __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_PERIODIC_IO_SUBMIT:
- mutex_lock(&lwis_client->lock);
- ret = cmd_periodic_io_submit(
- lwis_client, &header,
- (struct lwis_cmd_periodic_io_info __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_PERIODIC_IO_CANCEL:
- mutex_lock(&lwis_client->lock);
- ret = cmd_periodic_io_cancel(
- lwis_client, &header,
- (struct lwis_cmd_periodic_io_cancel __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DPM_CLK_UPDATE:
- mutex_lock(&lwis_client->lock);
- ret = cmd_dpm_clk_update(lwis_dev, &header,
- (struct lwis_cmd_dpm_clk_update __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DPM_QOS_UPDATE:
- mutex_lock(&lwis_client->lock);
- ret = cmd_dpm_qos_update(lwis_dev, &header,
- (struct lwis_cmd_dpm_qos_update __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
- case LWIS_CMD_ID_DPM_GET_CLOCK:
- mutex_lock(&lwis_client->lock);
- ret = cmd_dpm_get_clock(lwis_dev, &header,
- (struct lwis_cmd_dpm_clk_get __user *)user_msg);
- mutex_unlock(&lwis_client->lock);
- break;
-#ifdef LWIS_FENCE_ENABLED
- case LWIS_CMD_ID_FENCE_CREATE:
- ret = cmd_fence_create(lwis_dev, &header,
- (struct lwis_cmd_fence_create __user *)user_msg);
- break;
-#endif
- default:
- dev_err_ratelimited(lwis_dev->dev, "Unknown command id\n");
- header.ret_code = -EINVAL;
- ret = copy_pkt_to_user(lwis_dev, user_msg, (void *)&header, sizeof(header));
- }
- if (ret) {
- return ret;
- }
- user_msg = header.next;
- }
-
- return ret;
-}
diff --git a/lwis_cmd.h b/lwis_cmd.h
deleted file mode 100644
index c9a6858..0000000
--- a/lwis_cmd.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Google LWIS Command packets
- *
- * Copyright (c) 2022 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_CMD_H_
-#define LWIS_CMD_H_
-
-#include "lwis_device.h"
-
-/*
- * lwis_ioctl_handle_cmd_pkt: Handle command packets from IOCTL
- */
-int lwis_ioctl_handle_cmd_pkt(struct lwis_client *lwis_client,
- struct lwis_cmd_pkt __user *user_msg);
-
-#endif /* LWIS_CMD_H_ */ \ No newline at end of file
diff --git a/lwis_commands.h b/lwis_commands.h
index b0c5300..97b115d 100644
--- a/lwis_commands.h
+++ b/lwis_commands.h
@@ -159,7 +159,9 @@ enum lwis_io_entry_types {
LWIS_IO_ENTRY_WRITE_BATCH,
LWIS_IO_ENTRY_MODIFY,
LWIS_IO_ENTRY_POLL,
- LWIS_IO_ENTRY_READ_ASSERT
+ LWIS_IO_ENTRY_READ_ASSERT,
+ LWIS_IO_ENTRY_POLL_SHORT,
+ LWIS_IO_ENTRY_WAIT
};
// For io_entry read and write types.
@@ -201,6 +203,7 @@ struct lwis_io_entry {
struct lwis_io_entry_rw_batch rw_batch;
struct lwis_io_entry_modify mod;
struct lwis_io_entry_read_assert read_assert;
+ uint64_t wait_us;
};
};
@@ -304,6 +307,7 @@ struct lwis_transaction_trigger_node {
};
enum lwis_transaction_trigger_node_operator {
+ LWIS_TRIGGER_NODE_OPERATOR_INVALID = -1,
LWIS_TRIGGER_NODE_OPERATOR_NONE,
LWIS_TRIGGER_NODE_OPERATOR_AND,
LWIS_TRIGGER_NODE_OPERATOR_OR,
@@ -324,14 +328,33 @@ struct lwis_transaction_trigger_condition {
#define LWIS_ID_INVALID (-1LL)
#define LWIS_EVENT_COUNTER_ON_NEXT_OCCURRENCE (-1LL)
#define LWIS_EVENT_COUNTER_EVERY_TIME (-2LL)
+
struct lwis_transaction_info {
// Input
int64_t trigger_event_id;
int64_t trigger_event_counter;
-#ifdef LWIS_FENCE_ENABLED
+ size_t num_io_entries;
+ struct lwis_io_entry *io_entries;
+ bool run_in_event_context;
+ // Use reserved to keep the original interface
+ bool reserved;
+ int64_t emit_success_event_id;
+ int64_t emit_error_event_id;
+ bool is_level_triggered;
+ // Output
+ int64_t id;
+ // Only will be set if trigger_event_id is specified.
+ // Otherwise, the value is -1.
+ int64_t current_trigger_event_counter;
+ int64_t submission_timestamp_ns;
+};
+
+struct lwis_transaction_info_v2 {
+ // Input
+ int64_t trigger_event_id;
+ int64_t trigger_event_counter;
struct lwis_transaction_trigger_condition trigger_condition;
int32_t completion_fence_fd;
-#endif
size_t num_io_entries;
struct lwis_io_entry *io_entries;
bool run_in_event_context;
@@ -411,10 +434,25 @@ struct lwis_qos_setting {
int64_t peak_bw;
// RT BW (total peak)
int64_t rt_bw;
-#ifdef LWIS_BTS_BLOCK_NAME_ENABLED
+};
+
+struct lwis_qos_setting_v2 {
+ // Frequency in hz.
+ int64_t frequency_hz;
+ // Device id for this vote.
+ int32_t device_id;
+ // Target clock family.
+ int32_t clock_family;
+ // read BW
+ int64_t read_bw;
+ // write BW
+ int64_t write_bw;
+ // peak BW
+ int64_t peak_bw;
+ // RT BW (total peak)
+ int64_t rt_bw;
// Bts client name
char bts_block_name[LWIS_MAX_NAME_STRING_LEN];
-#endif
};
struct lwis_dpm_qos_requirements {
@@ -424,6 +462,13 @@ struct lwis_dpm_qos_requirements {
size_t num_settings;
};
+struct lwis_dpm_qos_requirements_v2 {
+ // qos entities from user.
+ struct lwis_qos_setting_v2 *qos_settings;
+ // number of qos_settings.
+ size_t num_settings;
+};
+
enum lwis_cmd_id {
LWIS_CMD_ID_ECHO = 0x100,
LWIS_CMD_ID_TIME_QUERY = 0x200,
@@ -449,17 +494,22 @@ enum lwis_cmd_id {
LWIS_CMD_ID_EVENT_DEQUEUE = 0x40200,
LWIS_CMD_ID_TRANSACTION_SUBMIT = 0x50000,
+ LWIS_CMD_ID_TRANSACTION_SUBMIT_V2,
LWIS_CMD_ID_TRANSACTION_CANCEL = 0x50100,
LWIS_CMD_ID_TRANSACTION_REPLACE = 0x50200,
+ LWIS_CMD_ID_TRANSACTION_REPLACE_V2,
LWIS_CMD_ID_PERIODIC_IO_SUBMIT = 0x60000,
LWIS_CMD_ID_PERIODIC_IO_CANCEL = 0x60100,
LWIS_CMD_ID_DPM_CLK_UPDATE = 0x70000,
LWIS_CMD_ID_DPM_QOS_UPDATE = 0x70100,
+ LWIS_CMD_ID_DPM_QOS_UPDATE_V2,
LWIS_CMD_ID_DPM_GET_CLOCK = 0x70200,
- LWIS_CMD_ID_FENCE_CREATE = 0x80000
+ LWIS_CMD_ID_FENCE_CREATE = 0x80000,
+
+ LWIS_CMD_ID_EVENT_INJECTION = 0x90000
};
struct lwis_cmd_pkt {
@@ -533,6 +583,11 @@ struct lwis_cmd_transaction_info {
struct lwis_transaction_info info;
};
+struct lwis_cmd_transaction_info_v2 {
+ struct lwis_cmd_pkt header;
+ struct lwis_transaction_info_v2 info;
+};
+
struct lwis_cmd_transaction_cancel {
struct lwis_cmd_pkt header;
int64_t id;
@@ -558,55 +613,26 @@ struct lwis_cmd_dpm_qos_update {
struct lwis_dpm_qos_requirements reqs;
};
+struct lwis_cmd_dpm_qos_update_v2 {
+ struct lwis_cmd_pkt header;
+ struct lwis_dpm_qos_requirements_v2 reqs;
+};
+
struct lwis_cmd_dpm_clk_get {
struct lwis_cmd_pkt header;
struct lwis_qos_setting setting;
};
-#ifdef LWIS_FENCE_ENABLED
struct lwis_cmd_fence_create {
struct lwis_cmd_pkt header;
int32_t fd;
};
-#endif
/*
* IOCTL Commands
*/
#define LWIS_IOC_TYPE 'L'
-
-#define LWIS_GET_DEVICE_INFO _IOWR(LWIS_IOC_TYPE, 1, struct lwis_device_info)
-#define LWIS_BUFFER_ENROLL _IOWR(LWIS_IOC_TYPE, 2, struct lwis_buffer_info)
-#define LWIS_BUFFER_DISENROLL _IOWR(LWIS_IOC_TYPE, 3, struct lwis_enrolled_buffer_info)
-#define LWIS_BUFFER_CPU_ACCESS _IOWR(LWIS_IOC_TYPE, 4, struct lwis_buffer_cpu_access_op)
-#define LWIS_DEVICE_ENABLE _IO(LWIS_IOC_TYPE, 6)
-#define LWIS_DEVICE_DISABLE _IO(LWIS_IOC_TYPE, 7)
-#define LWIS_BUFFER_ALLOC _IOWR(LWIS_IOC_TYPE, 8, struct lwis_alloc_buffer_info)
-#define LWIS_BUFFER_FREE _IOWR(LWIS_IOC_TYPE, 9, int32_t)
-#define LWIS_TIME_QUERY _IOWR(LWIS_IOC_TYPE, 10, int64_t)
-#define LWIS_REG_IO _IOWR(LWIS_IOC_TYPE, 11, struct lwis_io_entries)
-#define LWIS_ECHO _IOWR(LWIS_IOC_TYPE, 12, struct lwis_echo)
-#define LWIS_DEVICE_RESET _IOWR(LWIS_IOC_TYPE, 13, struct lwis_io_entries)
-#define LWIS_DUMP_DEBUG_STATE _IO(LWIS_IOC_TYPE, 14)
-
-#define LWIS_EVENT_CONTROL_GET _IOWR(LWIS_IOC_TYPE, 20, struct lwis_event_control)
-#define LWIS_EVENT_CONTROL_SET _IOW(LWIS_IOC_TYPE, 21, struct lwis_event_control_list)
-#define LWIS_EVENT_DEQUEUE _IOWR(LWIS_IOC_TYPE, 22, struct lwis_event_info)
-
-#define LWIS_TRANSACTION_SUBMIT _IOWR(LWIS_IOC_TYPE, 30, struct lwis_transaction_info)
-#define LWIS_TRANSACTION_CANCEL _IOWR(LWIS_IOC_TYPE, 31, int64_t)
-#define LWIS_TRANSACTION_REPLACE _IOWR(LWIS_IOC_TYPE, 32, struct lwis_transaction_info)
-
-#define LWIS_PERIODIC_IO_SUBMIT _IOWR(LWIS_IOC_TYPE, 40, struct lwis_periodic_io_info)
-#define LWIS_PERIODIC_IO_CANCEL _IOWR(LWIS_IOC_TYPE, 41, int64_t)
-
-#define LWIS_DPM_CLK_UPDATE _IOW(LWIS_IOC_TYPE, 50, struct lwis_dpm_clk_settings)
-#define LWIS_DPM_QOS_UPDATE _IOW(LWIS_IOC_TYPE, 51, struct lwis_dpm_qos_requirements)
-#define LWIS_DPM_GET_CLOCK _IOW(LWIS_IOC_TYPE, 52, struct lwis_qos_setting)
-
-#define LWIS_FENCE_CREATE _IOWR(LWIS_IOC_TYPE, 60, int32_t)
-
#define LWIS_CMD_PACKET _IOWR(LWIS_IOC_TYPE, 100, struct lwis_cmd_pkt)
/*
diff --git a/lwis_debug.c b/lwis_debug.c
index 5813476..59d83de 100644
--- a/lwis_debug.c
+++ b/lwis_debug.c
@@ -23,7 +23,7 @@
#define PRINT_BUFFER_SIZE 128
/* Printing the log buffer line by line as printk does not work well with large chunks of data */
-static void print_to_log(char *buffer)
+static void print_to_log(struct lwis_device *lwis_dev, char *buffer)
{
int size;
char tmpbuf[PRINT_BUFFER_SIZE + 1];
@@ -36,7 +36,7 @@ static void print_to_log(char *buffer)
}
memcpy(tmpbuf, start, size);
tmpbuf[size] = '\0';
- pr_info("%s", tmpbuf);
+ dev_info(lwis_dev->dev, "%s", tmpbuf);
start = end + 1;
end = strchr(start, '\n');
}
@@ -99,10 +99,17 @@ static void list_transactions(struct lwis_client *client, char *k_buf, size_t k_
trans_hist->info.emit_success_event_id,
trans_hist->info.emit_error_event_id);
strlcat(k_buf, tmp_buf, k_buf_size);
- scnprintf(tmp_buf, sizeof(tmp_buf),
- " Num Entries: %zu Processed @ %lld for %lldns\n",
- trans_hist->info.num_io_entries, trans_hist->process_timestamp,
- trans_hist->process_duration_ns);
+ /* Process timestamp not recorded */
+ if (trans_hist->process_timestamp == -1) {
+ scnprintf(tmp_buf, sizeof(tmp_buf), " Num Entries: %zu\n",
+ trans_hist->info.num_io_entries);
+ } else {
+ scnprintf(tmp_buf, sizeof(tmp_buf),
+ " Num Entries: %zu Processed @ %lld for %lldns\n",
+ trans_hist->info.num_io_entries,
+ trans_hist->process_timestamp,
+ trans_hist->process_duration_ns);
+ }
strlcat(k_buf, tmp_buf, k_buf_size);
}
hist_idx++;
@@ -153,6 +160,13 @@ static void list_enrolled_buffers(struct lwis_client *client, char *k_buf, size_
hash_for_each (client->enrolled_buffers, i, enrollment_list, node) {
list_for_each (it_enrollment, &enrollment_list->list) {
buffer = list_entry(it_enrollment, struct lwis_enrolled_buffer, list_node);
+ if (IS_ERR_VALUE(buffer->info.dma_vaddr)) {
+ scnprintf(tmp_buf, sizeof(tmp_buf),
+ "Enrolled buffers: dma_vaddr %pad is invalid\n",
+ &buffer->info.dma_vaddr);
+ strlcat(k_buf, tmp_buf, k_buf_size);
+ continue;
+ }
end_dma_vaddr = buffer->info.dma_vaddr + (buffer->dma_buf->size - 1);
scnprintf(tmp_buf, sizeof(tmp_buf),
"[%2d] FD: %d Mode: %s%s Addr:[%pad ~ %pad] Size: %zu\n", idx++,
@@ -193,18 +207,10 @@ static int generate_event_states_info(struct lwis_device *lwis_dev, char *buffer
return -EINVAL;
}
+ scnprintf(buffer, buffer_size, "=== LWIS EVENT STATES INFO: %s ===\n", lwis_dev->name);
if (lwis_event_dump_cnt >= 0 && lwis_event_dump_cnt <= EVENT_DEBUG_HISTORY_SIZE) {
- scnprintf(tmp_buf, sizeof(tmp_buf), "=== LWIS DUMP LAST %d Received Events ===\n",
- lwis_event_dump_cnt);
- strlcat(buffer, tmp_buf, buffer_size);
traverse_last_events_size = lwis_event_dump_cnt;
- } else if (lwis_event_dump_cnt > EVENT_DEBUG_HISTORY_SIZE) {
- pr_err("lwis_event_dump_cnt (%d) exceed EVENT_DEBUG_HISTORY_SIZE (%d) \n",
- lwis_event_dump_cnt, EVENT_DEBUG_HISTORY_SIZE);
- return -EINVAL;
} else {
- scnprintf(buffer, buffer_size, "=== LWIS EVENT STATES INFO: %s ===\n",
- lwis_dev->name);
traverse_last_events_size = EVENT_DEBUG_HISTORY_SIZE;
}
@@ -213,9 +219,9 @@ static int generate_event_states_info(struct lwis_device *lwis_dev, char *buffer
strlcat(buffer, " No events being monitored\n", buffer_size);
goto exit;
}
- strlcat(buffer, "Enabled Device Events:\n", buffer_size);
+ strlcat(buffer, "Event Counts:\n", buffer_size);
hash_for_each (lwis_dev->event_states, i, state, node) {
- if (state->enable_counter > 0) {
+ if (state->event_counter > 0) {
scnprintf(tmp_buf, sizeof(tmp_buf), "[%2d] ID: 0x%llx Counter: 0x%llx\n",
idx++, state->event_id, state->event_counter);
strlcat(buffer, tmp_buf, buffer_size);
@@ -223,19 +229,15 @@ static int generate_event_states_info(struct lwis_device *lwis_dev, char *buffer
}
}
if (!enabled_event_present) {
- strlcat(buffer, "No enabled events\n", buffer_size);
- }
- if (lwis_event_dump_cnt < 0) {
- strlcat(buffer, "Last Events:\n", buffer_size);
+ strlcat(buffer, " No enabled events\n", buffer_size);
}
+ strlcat(buffer, "Last Events:\n", buffer_size);
idx = lwis_dev->debug_info.cur_event_hist_idx;
for (i = 0; i < traverse_last_events_size; ++i) {
- if (lwis_event_dump_cnt >= 0) {
- if (idx == 0) {
- idx = EVENT_DEBUG_HISTORY_SIZE;
- }
- idx--;
+ idx--;
+ if (idx < 0) {
+ idx = EVENT_DEBUG_HISTORY_SIZE - 1;
}
state = &lwis_dev->debug_info.event_hist[idx].state;
/* Skip uninitialized entries */
@@ -246,12 +248,6 @@ static int generate_event_states_info(struct lwis_device *lwis_dev, char *buffer
lwis_dev->debug_info.event_hist[idx].timestamp);
strlcat(buffer, tmp_buf, buffer_size);
}
- if (lwis_event_dump_cnt < 0) {
- idx++;
- if (idx >= EVENT_DEBUG_HISTORY_SIZE) {
- idx = 0;
- }
- }
}
exit:
@@ -307,20 +303,21 @@ static int generate_buffer_info(struct lwis_device *lwis_dev, char *buffer, size
return 0;
}
- spin_lock_irqsave(&lwis_dev->lock, flags);
list_for_each_entry (client, &lwis_dev->clients, node) {
scnprintf(tmp_buf, sizeof(tmp_buf), "Client %d:\n", idx);
strlcat(buffer, tmp_buf, buffer_size);
+ spin_lock_irqsave(&client->buffer_lock, flags);
list_allocated_buffers(client, buffer, buffer_size);
list_enrolled_buffers(client, buffer, buffer_size);
+ spin_unlock_irqrestore(&client->buffer_lock, flags);
idx++;
}
- spin_unlock_irqrestore(&lwis_dev->lock, flags);
return 0;
}
-static int generate_register_io_history(struct lwis_device *lwis_dev, char *buffer, size_t buffer_size)
+static int generate_register_io_history(struct lwis_device *lwis_dev, char *buffer,
+ size_t buffer_size)
{
/* Temporary buffer to be concatenated to the main buffer. */
char tmp_buf[128] = {};
@@ -340,40 +337,45 @@ static int generate_register_io_history(struct lwis_device *lwis_dev, char *buff
/* Skip uninitialized entries */
if (reg_io->start_timestamp != 0) {
if (reg_io->io_entry.type == LWIS_IO_ENTRY_READ) {
- scnprintf(tmp_buf, sizeof(tmp_buf),
- "READ: bid %d, offset %llu, val %llu, access_size %lu, start_timestamp %llu\n",
- reg_io->io_entry.rw.bid, reg_io->io_entry.rw.offset,
- reg_io->io_entry.rw.val, reg_io->access_size,
- reg_io->start_timestamp);
+ scnprintf(
+ tmp_buf, sizeof(tmp_buf),
+ "READ: bid %d, offset %llu, val %llu, access_size %lu, start_timestamp %llu\n",
+ reg_io->io_entry.rw.bid, reg_io->io_entry.rw.offset,
+ reg_io->io_entry.rw.val, reg_io->access_size,
+ reg_io->start_timestamp);
strlcat(buffer, tmp_buf, buffer_size);
} else if (reg_io->io_entry.type == LWIS_IO_ENTRY_READ_BATCH) {
- scnprintf(tmp_buf, sizeof(tmp_buf),
- "READ_BATCH: bid %d, offset %llu, size_in_bytes %lu, access_size %lu, start_timestamp %llu\n",
- reg_io->io_entry.rw_batch.bid,
- reg_io->io_entry.rw_batch.offset,
- reg_io->io_entry.rw_batch.size_in_bytes,
- reg_io->access_size, reg_io->start_timestamp);
+ scnprintf(
+ tmp_buf, sizeof(tmp_buf),
+ "READ_BATCH: bid %d, offset %llu, size_in_bytes %lu, access_size %lu, start_timestamp %llu\n",
+ reg_io->io_entry.rw_batch.bid,
+ reg_io->io_entry.rw_batch.offset,
+ reg_io->io_entry.rw_batch.size_in_bytes,
+ reg_io->access_size, reg_io->start_timestamp);
strlcat(buffer, tmp_buf, buffer_size);
} else if (reg_io->io_entry.type == LWIS_IO_ENTRY_WRITE) {
- scnprintf(tmp_buf, sizeof(tmp_buf),
- "WRITE: bid %d, offset %llu, val %llu, access_size %lu, start_timestamp %llu\n",
- reg_io->io_entry.rw.bid, reg_io->io_entry.rw.offset,
- reg_io->io_entry.rw.val, reg_io->access_size,
- reg_io->start_timestamp);
+ scnprintf(
+ tmp_buf, sizeof(tmp_buf),
+ "WRITE: bid %d, offset %llu, val %llu, access_size %lu, start_timestamp %llu\n",
+ reg_io->io_entry.rw.bid, reg_io->io_entry.rw.offset,
+ reg_io->io_entry.rw.val, reg_io->access_size,
+ reg_io->start_timestamp);
strlcat(buffer, tmp_buf, buffer_size);
} else if (reg_io->io_entry.type == LWIS_IO_ENTRY_WRITE_BATCH) {
- scnprintf(tmp_buf, sizeof(tmp_buf),
- "WRITE_BATCH: bid %d, offset %llu, size_in_bytes %lu, access_size %lu, start_timestamp %llu\n",
- reg_io->io_entry.rw_batch.bid,
- reg_io->io_entry.rw_batch.offset,
- reg_io->io_entry.rw_batch.size_in_bytes,
- reg_io->access_size, reg_io->start_timestamp);
+ scnprintf(
+ tmp_buf, sizeof(tmp_buf),
+ "WRITE_BATCH: bid %d, offset %llu, size_in_bytes %lu, access_size %lu, start_timestamp %llu\n",
+ reg_io->io_entry.rw_batch.bid,
+ reg_io->io_entry.rw_batch.offset,
+ reg_io->io_entry.rw_batch.size_in_bytes,
+ reg_io->access_size, reg_io->start_timestamp);
strlcat(buffer, tmp_buf, buffer_size);
} else if (reg_io->io_entry.type == LWIS_IO_ENTRY_MODIFY) {
- scnprintf(tmp_buf, sizeof(tmp_buf),
- "MODIFY: bid %d, offset %llu, access_size %lu, start_timestamp %llu\n",
- reg_io->io_entry.mod.bid, reg_io->io_entry.mod.offset,
- reg_io->access_size, reg_io->start_timestamp);
+ scnprintf(
+ tmp_buf, sizeof(tmp_buf),
+ "MODIFY: bid %d, offset %llu, access_size %lu, start_timestamp %llu\n",
+ reg_io->io_entry.mod.bid, reg_io->io_entry.mod.offset,
+ reg_io->access_size, reg_io->start_timestamp);
strlcat(buffer, tmp_buf, buffer_size);
}
}
@@ -402,7 +404,7 @@ int lwis_debug_print_register_io_history(struct lwis_device *lwis_dev)
dev_err(lwis_dev->dev, "Failed to generate register io history");
goto exit;
}
- print_to_log(buffer);
+ print_to_log(lwis_dev, buffer);
exit:
kfree(buffer);
@@ -421,7 +423,7 @@ int lwis_debug_print_device_info(struct lwis_device *lwis_dev)
dev_err(lwis_dev->dev, "Failed to generate device info");
return ret;
}
- print_to_log(buffer);
+ print_to_log(lwis_dev, buffer);
return 0;
}
@@ -441,7 +443,7 @@ int lwis_debug_print_event_states_info(struct lwis_device *lwis_dev, int lwis_ev
dev_err(lwis_dev->dev, "Failed to generate event states info");
goto exit;
}
- print_to_log(buffer);
+ print_to_log(lwis_dev, buffer);
exit:
kfree(buffer);
return ret;
@@ -463,7 +465,7 @@ int lwis_debug_print_transaction_info(struct lwis_device *lwis_dev)
dev_err(lwis_dev->dev, "Failed to generate transaction info");
goto exit;
}
- print_to_log(buffer);
+ print_to_log(lwis_dev, buffer);
exit:
kfree(buffer);
return ret;
@@ -485,12 +487,27 @@ int lwis_debug_print_buffer_info(struct lwis_device *lwis_dev)
dev_err(lwis_dev->dev, "Failed to generate buffer info");
goto exit;
}
- print_to_log(buffer);
+ print_to_log(lwis_dev, buffer);
exit:
kfree(buffer);
return ret;
}
+void lwis_debug_crash_info_dump(struct lwis_device *lwis_dev)
+{
+ const int event_dump_count = 5;
+
+ /* State dump is only meaningful for I2C and IOREG devices */
+ if (lwis_dev->type != DEVICE_TYPE_I2C && lwis_dev->type != DEVICE_TYPE_IOREG) {
+ return;
+ }
+
+ dev_info(lwis_dev->dev, "LWIS Device (%s) Crash Info Dump:\n", lwis_dev->name);
+
+ /* Dump event states and last 5 received events */
+ lwis_debug_print_event_states_info(lwis_dev, event_dump_count);
+}
+
/* DebugFS specific functions */
#ifdef CONFIG_DEBUG_FS
@@ -688,8 +705,8 @@ int lwis_device_debugfs_setup(struct lwis_device *lwis_dev, struct dentry *dbg_r
dbg_buffer_file = NULL;
}
- dbg_reg_io_file =
- debugfs_create_file("io_history", 0444, dbg_dir, lwis_dev, &register_io_history_fops);
+ dbg_reg_io_file = debugfs_create_file("io_history", 0444, dbg_dir, lwis_dev,
+ &register_io_history_fops);
if (IS_ERR_OR_NULL(dbg_reg_io_file)) {
dev_warn(lwis_dev->dev, "Failed to create DebugFS io_history - %ld",
PTR_ERR(dbg_reg_io_file));
diff --git a/lwis_debug.h b/lwis_debug.h
index 85996a3..c2604ba 100644
--- a/lwis_debug.h
+++ b/lwis_debug.h
@@ -17,6 +17,13 @@ int lwis_debug_print_transaction_info(struct lwis_device *lwis_dev);
int lwis_debug_print_register_io_history(struct lwis_device *lwis_dev);
int lwis_debug_print_buffer_info(struct lwis_device *lwis_dev);
+/*
+ * lwis_debug_crash_info_dump:
+ * Use the customized function handle to print information from each device registered in LWIS
+ * when usersapce crash.
+ */
+void lwis_debug_crash_info_dump(struct lwis_device *lwis_dev);
+
/* DebugFS specific functions */
int lwis_device_debugfs_setup(struct lwis_device *lwis_dev, struct dentry *dbg_root);
int lwis_device_debugfs_cleanup(struct lwis_device *lwis_dev); \ No newline at end of file
diff --git a/lwis_device.c b/lwis_device.c
index e28c1aa..309584d 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
*/
@@ -114,6 +122,8 @@ static int lwis_open(struct inode *node, struct file *fp)
mutex_init(&lwis_client->lock);
spin_lock_init(&lwis_client->periodic_io_lock);
spin_lock_init(&lwis_client->event_lock);
+ spin_lock_init(&lwis_client->flush_lock);
+ spin_lock_init(&lwis_client->buffer_lock);
/* Empty hash table for client event states */
hash_init(lwis_client->event_states);
@@ -135,6 +145,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);
@@ -151,6 +162,15 @@ 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;
+ spin_lock_irqsave(&lwis_client->flush_lock, flags);
+ lwis_client->flush_state = NOT_FLUSHING;
+ spin_unlock_irqrestore(&lwis_client->flush_lock, flags);
+
+ 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;
}
@@ -210,6 +230,7 @@ static int lwis_release_client(struct lwis_client *lwis_client)
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
int rc = 0;
unsigned long flags;
+
rc = lwis_cleanup_client(lwis_client);
if (rc) {
return rc;
@@ -225,7 +246,10 @@ 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;
}
@@ -250,9 +274,9 @@ static int lwis_release(struct inode *node, struct file *fp)
mutex_lock(&lwis_dev->client_lock);
/* Release power if client closed without power down called */
if (is_client_enabled && lwis_dev->enabled > 0) {
- lwis_device_crash_info_dump(lwis_dev);
lwis_dev->enabled--;
if (lwis_dev->enabled == 0) {
+ lwis_debug_crash_info_dump(lwis_dev);
dev_info(lwis_dev->dev, "No more client, power down\n");
rc = lwis_dev_power_down_locked(lwis_dev);
lwis_dev->is_suspended = false;
@@ -260,15 +284,13 @@ static int lwis_release(struct inode *node, struct file *fp)
}
if (lwis_dev->enabled == 0) {
-#ifdef LWIS_BTS_BLOCK_NAME_ENABLED
for (i = 0; i < lwis_dev->bts_block_num; i++) {
- lwis_platform_update_bts(lwis_dev, i, /*bw_peak=*/0,
- /*bw_read=*/0, /*bw_write=*/0, /*bw_rt=*/0);
+ if (lwis_dev->bts_indexes[i] != BTS_UNSUPPORTED) {
+ lwis_platform_update_bts(lwis_dev, i, /*bw_peak=*/0,
+ /*bw_read=*/0, /*bw_write=*/0,
+ /*bw_rt=*/0);
+ }
}
-#else
- lwis_platform_update_bts(lwis_dev, 0, /*bw_peak=*/0,
- /*bw_read=*/0, /*bw_write=*/0, /*bw_rt=*/0);
-#endif
/* remove voted qos */
lwis_platform_remove_qos(lwis_dev);
/* Release device event states if no more client is using */
@@ -619,7 +641,7 @@ int lwis_dev_process_power_sequence(struct lwis_device *lwis_dev,
gpios_info = lwis_gpios_get_info_by_name(lwis_dev->gpios_list,
list->seq_info[i].name);
- if (IS_ERR(gpios_info)) {
+ if (IS_ERR_OR_NULL(gpios_info)) {
dev_err(lwis_dev->dev, "Get %s gpios info failed\n",
list->seq_info[i].name);
ret = PTR_ERR(gpios_info);
@@ -695,7 +717,7 @@ int lwis_dev_process_power_sequence(struct lwis_device *lwis_dev,
gpios_info_it = lwis_gpios_get_info_by_name(
lwis_dev_it->gpios_list,
list->seq_info[i].name);
- if (IS_ERR(gpios_info_it)) {
+ if (IS_ERR_OR_NULL(gpios_info_it)) {
continue;
}
if (gpios_info_it->id == gpios_info->id &&
@@ -754,7 +776,7 @@ int lwis_dev_process_power_sequence(struct lwis_device *lwis_dev,
if (set_active) {
lwis_dev->mclk_ctrl = devm_pinctrl_get(&lwis_dev->plat_dev->dev);
- if (IS_ERR(lwis_dev->mclk_ctrl)) {
+ if (IS_ERR_OR_NULL(lwis_dev->mclk_ctrl)) {
dev_err(lwis_dev->dev, "Failed to get mclk\n");
ret = PTR_ERR(lwis_dev->mclk_ctrl);
lwis_dev->mclk_ctrl = NULL;
@@ -925,7 +947,7 @@ static int lwis_dev_power_up_by_default(struct lwis_device *lwis_dev)
bool activate_mclk = true;
lwis_dev->mclk_ctrl = devm_pinctrl_get(&lwis_dev->plat_dev->dev);
- if (IS_ERR(lwis_dev->mclk_ctrl)) {
+ if (IS_ERR_OR_NULL(lwis_dev->mclk_ctrl)) {
dev_err(lwis_dev->dev, "Failed to get mclk\n");
ret = PTR_ERR(lwis_dev->mclk_ctrl);
lwis_dev->mclk_ctrl = NULL;
@@ -1430,19 +1452,6 @@ void lwis_device_info_dump(const char *name, void (*func)(struct lwis_device *))
mutex_unlock(&core.lock);
}
-void lwis_device_crash_info_dump(struct lwis_device *lwis_dev)
-{
- int dump_cnt = 5;
- int64_t timestamp;
-
- pr_info("LWIS Device (%s) Crash Info Dump:\n", lwis_dev->name);
-
- /* Dump Current kernel timestamp && Last 5 Received Event*/
- timestamp = ktime_to_ns(lwis_get_time());
- dev_info(lwis_dev->dev, " AT %lld Dump Last %d Received Events:\n\n", timestamp, dump_cnt);
- lwis_debug_print_event_states_info(lwis_dev, /*lwis_event_dump_cnt=*/dump_cnt);
-}
-
void lwis_save_register_io_info(struct lwis_device *lwis_dev, struct lwis_io_entry *io_entry,
size_t access_size)
{
@@ -1472,7 +1481,7 @@ int lwis_base_probe(struct lwis_device *lwis_dev, struct platform_device *plat_d
if (ret >= 0) {
lwis_dev->id = ret;
} else {
- pr_err("Unable to allocate minor ID (%d)\n", ret);
+ dev_err(&plat_dev->dev, "Unable to allocate minor ID (%d)\n", ret);
return ret;
}
@@ -1518,7 +1527,7 @@ int lwis_base_probe(struct lwis_device *lwis_dev, struct platform_device *plat_d
/* Upon success initialization, create device for this instance */
lwis_dev->dev = device_create(core.dev_class, NULL, MKDEV(core.device_major, lwis_dev->id),
lwis_dev, LWIS_DEVICE_NAME "-%s", lwis_dev->name);
- if (IS_ERR(lwis_dev->dev)) {
+ if (IS_ERR_OR_NULL(lwis_dev->dev)) {
pr_err("Failed to create device\n");
ret = PTR_ERR(lwis_dev->dev);
goto error_init;
@@ -1530,7 +1539,6 @@ int lwis_base_probe(struct lwis_device *lwis_dev, struct platform_device *plat_d
lwis_platform_probe(lwis_dev);
lwis_device_debugfs_setup(lwis_dev, core.dbg_root);
- memset(&lwis_dev->debug_info, 0, sizeof(lwis_dev->debug_info));
timer_setup(&lwis_dev->heartbeat_timer, event_heartbeat_timer, 0);
@@ -1604,8 +1612,12 @@ 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(lwis_dev->dev)) {
+ if (!IS_ERR_OR_NULL(lwis_dev->dev)) {
device_destroy(core.dev_class,
MKDEV(core.device_major, lwis_dev->id));
}
@@ -1651,7 +1663,7 @@ static int __init lwis_register_base_device(void)
/* Create a device class*/
core.dev_class = class_create(THIS_MODULE, LWIS_CLASS_NAME);
- if (IS_ERR(core.dev_class)) {
+ if (IS_ERR_OR_NULL(core.dev_class)) {
pr_err("Failed to create device class\n");
ret = PTR_ERR(core.dev_class);
goto error_class_create;
diff --git a/lwis_device.h b/lwis_device.h
index 62cb199..c70b98d 100644
--- a/lwis_device.h
+++ b/lwis_device.h
@@ -47,6 +47,19 @@
#define BTS_UNSUPPORTED -1
#define MAX_UNIFIED_POWER_DEVICE 8
+/* enum lwis_client_flush_state
+ * Client flush states indicate if the client has been issued a
+ * flush on the transaction worker threads.
+ * Client will move to FLUSHING state only when a direct call to
+ * lwis_transaction_client_flush has been made.
+ * Once the flush is complete, the client will transition back
+ * to NOT_FLUSHING state.
+ */
+enum lwis_client_flush_state {
+ NOT_FLUSHING = 0,
+ FLUSHING
+};
+
/* Forward declaration for lwis_device. This is needed for the declaration for
lwis_device_subclass_operations data struct. */
struct lwis_device;
@@ -254,17 +267,12 @@ struct lwis_device {
/* clock family this device belongs to */
int clock_family;
-#ifdef LWIS_BTS_BLOCK_NAME_ENABLED
/* number of BTS blocks */
int bts_block_num;
/* BTS block names*/
const char *bts_block_names[MAX_BTS_BLOCK_NUM];
/* indexes to bandwidth traffic shaper */
int bts_indexes[MAX_BTS_BLOCK_NUM];
-#else
- /* index to bandwidth traffic shaper */
- int bts_index;
-#endif
/* BTS scenario name */
const char *bts_scenario_name;
/* BTS scenario index */
@@ -299,6 +307,8 @@ struct lwis_device {
/* Worker thread */
struct kthread_worker transaction_worker;
struct task_struct *transaction_worker_thread;
+ /* Limit on number of transactions to be processed at a time */
+ int transaction_process_limit;
};
/*
@@ -350,6 +360,14 @@ 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;
+ /* Indicates if the client has been issued a flush worker call */
+ enum lwis_client_flush_state flush_state;
+ /* Lock to guard client's flush state changes */
+ spinlock_t flush_lock;
+ /* Lock to guard client's buffer changes */
+ spinlock_t buffer_lock;
};
/*
@@ -420,18 +438,11 @@ void lwis_dev_power_seq_list_print(struct lwis_device_power_sequence_list *list)
void lwis_device_info_dump(const char *name, void (*func)(struct lwis_device *));
/*
- * lwis_device_crash_info_dump:
- * Use the customized function handle to print information from each device registered in LWIS
- * when usersapce crash.
- */
-void lwis_device_crash_info_dump(struct lwis_device *lwis_dev);
-
-/*
* lwis_save_register_io_info: Saves the register io info in a history buffer
* for better debugability.
*/
void lwis_save_register_io_info(struct lwis_device *lwis_dev, struct lwis_io_entry *io_entry,
- size_t access_size);
+ size_t access_size);
/*
* lwis_process_worker_queue:
diff --git a/lwis_device_dpm.c b/lwis_device_dpm.c
index 8f9b418..deaaca3 100644
--- a/lwis_device_dpm.c
+++ b/lwis_device_dpm.c
@@ -38,9 +38,8 @@ static struct lwis_event_subscribe_operations dpm_subscribe_ops = {
.release = NULL,
};
-#ifdef LWIS_BTS_BLOCK_NAME_ENABLED
static int find_bts_block(struct lwis_device *lwis_dev, struct lwis_device *target_dev,
- struct lwis_qos_setting *qos_setting)
+ struct lwis_qos_setting_v2 *qos_setting)
{
int i;
@@ -64,12 +63,11 @@ static int find_bts_block(struct lwis_device *lwis_dev, struct lwis_device *targ
return -EINVAL;
}
}
-#endif
/*
* lwis_dpm_update_qos: update qos requirement for lwis device.
*/
-int lwis_dpm_update_qos(struct lwis_device *lwis_dev, struct lwis_qos_setting *qos_setting)
+int lwis_dpm_update_qos(struct lwis_device *lwis_dev, struct lwis_qos_setting_v2 *qos_setting)
{
int ret = 0, bts_block = -1;
int64_t peak_bw = 0;
@@ -106,14 +104,10 @@ int lwis_dpm_update_qos(struct lwis_device *lwis_dev, struct lwis_qos_setting *q
qos_setting->clock_family);
}
} else {
-#ifdef LWIS_BTS_BLOCK_NAME_ENABLED
bts_block = find_bts_block(lwis_dev, target_dev, qos_setting);
if (bts_block < 0) {
return bts_block;
}
-#else
- bts_block = 0;
-#endif
read_bw = qos_setting->read_bw;
write_bw = qos_setting->write_bw;
@@ -173,7 +167,7 @@ int lwis_dpm_update_clock(struct lwis_device *lwis_dev, struct lwis_clk_setting
goto out;
}
- if (IS_ERR(lwis_dev->clocks->clk[clk_index].clk)) {
+ if (IS_ERR_OR_NULL(lwis_dev->clocks->clk[clk_index].clk)) {
dev_err(lwis_dev->dev, "%s clk is invalid\n", lwis_dev->name);
ret = -EINVAL;
goto out;
diff --git a/lwis_device_dpm.h b/lwis_device_dpm.h
index 077c056..0aeba1f 100644
--- a/lwis_device_dpm.h
+++ b/lwis_device_dpm.h
@@ -32,7 +32,7 @@ int lwis_dpm_update_clock(struct lwis_device *lwis_dev, struct lwis_clk_setting
/*
* lwis_dpm_update_qos: update qos requirement from dpm client.
*/
-int lwis_dpm_update_qos(struct lwis_device *lwis_dev, struct lwis_qos_setting *qos_setting);
+int lwis_dpm_update_qos(struct lwis_device *lwis_dev, struct lwis_qos_setting_v2 *qos_setting);
/*
* lwis_dpm_read_clock: read current IP core clock for given lwis device.
diff --git a/lwis_device_i2c.c b/lwis_device_i2c.c
index 231db39..877719f 100644
--- a/lwis_device_i2c.c
+++ b/lwis_device_i2c.c
@@ -7,7 +7,6 @@
* 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"
@@ -76,8 +75,7 @@ static int lwis_i2c_device_enable(struct lwis_device *lwis_dev)
LWIS_ATRACE_FUNC_BEGIN(lwis_dev, "lwis_i2c_device_enable");
#if IS_ENABLED(CONFIG_INPUT_STMVL53L1)
if (is_shared_i2c_with_stmvl53l1(i2c_dev->state_pinctrl))
- ret = shared_i2c_set_state(&i2c_dev->client->dev,
- i2c_dev->state_pinctrl,
+ ret = shared_i2c_set_state(&i2c_dev->client->dev, i2c_dev->state_pinctrl,
I2C_ON_STRING);
else
ret = lwis_i2c_set_state(i2c_dev, I2C_ON_STRING);
@@ -111,13 +109,11 @@ static int lwis_i2c_device_disable(struct lwis_device *lwis_dev)
if (is_shared_i2c_with_stmvl53l1(i2c_dev->state_pinctrl)) {
/* Disable the shared i2c bus */
mutex_lock(i2c_dev->group_i2c_lock);
- ret = shared_i2c_set_state(&i2c_dev->client->dev,
- i2c_dev->state_pinctrl,
+ ret = shared_i2c_set_state(&i2c_dev->client->dev, i2c_dev->state_pinctrl,
I2C_OFF_STRING);
mutex_unlock(i2c_dev->group_i2c_lock);
if (ret) {
- dev_err(lwis_dev->dev, "Error disabling i2c bus (%d)\n",
- ret);
+ dev_err(lwis_dev->dev, "Error disabling i2c bus (%d)\n", ret);
}
return ret;
}
@@ -151,7 +147,7 @@ static int lwis_i2c_register_io(struct lwis_device *lwis_dev, struct lwis_io_ent
}
lwis_save_register_io_info(lwis_dev, entry, access_size);
- return lwis_i2c_io_entry_rw(i2c_dev, entry, lwis_dev);
+ return lwis_i2c_io_entry_rw(i2c_dev, entry);
}
static int lwis_i2c_addr_matcher(struct device *dev, void *data)
@@ -216,7 +212,7 @@ static int lwis_i2c_device_setup(struct lwis_i2c_device *i2c_dev)
pinctrl's are defined */
/* TODO: Need to figure out why this is parent's parent */
pinctrl = devm_pinctrl_get(dev->parent->parent);
- if (IS_ERR(pinctrl)) {
+ if (IS_ERR_OR_NULL(pinctrl)) {
dev_err(i2c_dev->base_dev.dev, "Cannot instantiate pinctrl instance (%lu)\n",
PTR_ERR(pinctrl));
i2c_dev->state_pinctrl = NULL;
@@ -225,11 +221,11 @@ static int lwis_i2c_device_setup(struct lwis_i2c_device *i2c_dev)
/* Verify that on_i2c or off_i2c strings are present */
i2c_dev->pinctrl_default_state_only = false;
- if (IS_ERR(pinctrl_lookup_state(pinctrl, I2C_OFF_STRING)) ||
- IS_ERR(pinctrl_lookup_state(pinctrl, I2C_ON_STRING))) {
+ if (IS_ERR_OR_NULL(pinctrl_lookup_state(pinctrl, I2C_OFF_STRING)) ||
+ IS_ERR_OR_NULL(pinctrl_lookup_state(pinctrl, I2C_ON_STRING))) {
state = pinctrl_lookup_state(pinctrl, I2C_DEFAULT_STATE_STRING);
/* Default option also missing, return error */
- if (IS_ERR(state)) {
+ if (IS_ERR_OR_NULL(state)) {
dev_err(i2c_dev->base_dev.dev,
"Pinctrl states {%s, %s, %s} not found (%lu)\n", I2C_OFF_STRING,
I2C_ON_STRING, I2C_DEFAULT_STATE_STRING, PTR_ERR(state));
@@ -277,27 +273,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;
@@ -313,7 +296,7 @@ static int lwis_i2c_device_suspend(struct device *dev)
struct lwis_device *lwis_dev = dev_get_drvdata(dev);
if (lwis_dev->pm_hibernation == 0) {
- /* TODO(b/265688764): Cleaning up system deep sleep for flash driver. */
+ /* Allow the device to enter PM hibernation, e.g., flash driver. */
return 0;
}
@@ -379,6 +362,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);
@@ -393,6 +378,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 1ae8294..393bd32 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,9 @@ 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 device_priority;
};
int lwis_i2c_device_deinit(void);
@@ -43,8 +47,7 @@ int lwis_i2c_device_deinit(void);
* two APIs in stmvl53l1 driver to well handle the enabling and disabling.
*/
extern bool is_shared_i2c_with_stmvl53l1(struct pinctrl *pinctrl);
-extern int shared_i2c_set_state(struct device *dev, struct pinctrl *pinctrl,
- const char *state_str);
+extern int shared_i2c_set_state(struct device *dev, struct pinctrl *pinctrl, const char *state_str);
#endif
#endif /* LWIS_DEVICE_I2C_H_ */
diff --git a/lwis_device_ioreg.c b/lwis_device_ioreg.c
index ed98410..36d2d98 100644
--- a/lwis_device_ioreg.c
+++ b/lwis_device_ioreg.c
@@ -138,12 +138,11 @@ static int lwis_ioreg_device_probe(struct platform_device *plat_dev)
if (ioreg_dev->base_dev.transaction_thread_priority != 0) {
ret = lwis_set_kthread_priority(&ioreg_dev->base_dev,
- ioreg_dev->base_dev.transaction_worker_thread,
- ioreg_dev->base_dev.transaction_thread_priority);
+ ioreg_dev->base_dev.transaction_worker_thread,
+ ioreg_dev->base_dev.transaction_thread_priority);
if (ret) {
dev_err(ioreg_dev->base_dev.dev,
- "Failed to set LWIS IOREG transaction kthread priority (%d)",
- ret);
+ "Failed to set LWIS IOREG transaction kthread priority (%d)", ret);
lwis_base_unprobe(&ioreg_dev->base_dev);
goto error_probe;
}
diff --git a/lwis_device_slc.c b/lwis_device_slc.c
index 5b0dd53..5debf65 100644
--- a/lwis_device_slc.c
+++ b/lwis_device_slc.c
@@ -93,7 +93,7 @@ static int lwis_slc_enable(struct lwis_device *lwis_dev)
/* Initialize SLC partitions and get a handle */
slc_dev->partition_handle = pt_client_register(node, NULL, NULL);
- if (IS_ERR(slc_dev->partition_handle)) {
+ if (IS_ERR_OR_NULL(slc_dev->partition_handle)) {
ret = PTR_ERR(slc_dev->partition_handle);
dev_err(lwis_dev->dev, "Failed to register PT client (%d)\n", ret);
slc_dev->partition_handle = NULL;
diff --git a/lwis_device_test.c b/lwis_device_test.c
index 75fd4dc..31b9ce5 100644
--- a/lwis_device_test.c
+++ b/lwis_device_test.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include "lwis_commands.h"
+#include "lwis_device.h"
#include "lwis_init.h"
#include "lwis_platform.h"
@@ -26,11 +27,16 @@
#define LWIS_DRIVER_NAME "lwis-test"
+static int lwis_test_device_enable(struct lwis_device *lwis_dev);
+static int lwis_test_device_disable(struct lwis_device *lwis_dev);
+static int lwis_test_register_io(struct lwis_device *lwis_dev, struct lwis_io_entry *entry,
+ int access_size);
+
static struct lwis_device_subclass_operations test_vops = {
- .register_io = NULL,
+ .register_io = lwis_test_register_io,
.register_io_barrier = NULL,
- .device_enable = NULL,
- .device_disable = NULL,
+ .device_enable = lwis_test_device_enable,
+ .device_disable = lwis_test_device_disable,
.event_enable = NULL,
.event_flags_updated = NULL,
.close = NULL,
@@ -43,6 +49,108 @@ static struct lwis_event_subscribe_operations test_subscribe_ops = {
.release = NULL,
};
+static int lwis_test_device_enable(struct lwis_device *lwis_dev)
+{
+ return 0;
+}
+
+static int lwis_test_device_disable(struct lwis_device *lwis_dev)
+{
+ return 0;
+}
+
+static int lwis_test_register_io(struct lwis_device *lwis_dev, struct lwis_io_entry *entry,
+ int access_size)
+{
+ struct lwis_test_device *test_dev =
+ container_of(lwis_dev, struct lwis_test_device, base_dev);
+ struct lwis_io_entry_rw_batch *rw_batch;
+ int i;
+ uint64_t reg_value;
+
+ if (!entry) {
+ dev_err(test_dev->base_dev.dev, "IO entry is NULL.\n");
+ return -EINVAL;
+ }
+
+ lwis_save_register_io_info(lwis_dev, entry, access_size);
+
+ if (entry->type == LWIS_IO_ENTRY_READ) {
+ if (entry->rw.offset >= SCRATCH_TEST_DEV_MEMORY_SIZE) {
+ dev_err(test_dev->base_dev.dev, "Offset (%llu) must be < %d\n",
+ entry->rw.offset, SCRATCH_TEST_DEV_MEMORY_SIZE);
+ return -EINVAL;
+ }
+ entry->rw.val = test_dev->scratch_mem[entry->rw.offset];
+ } else if (entry->type == LWIS_IO_ENTRY_READ_BATCH) {
+ rw_batch = &entry->rw_batch;
+ if (rw_batch->offset >= SCRATCH_TEST_DEV_MEMORY_SIZE ||
+ SCRATCH_TEST_DEV_MEMORY_SIZE - rw_batch->offset < rw_batch->size_in_bytes) {
+ dev_err(test_dev->base_dev.dev,
+ "Read range[offset(%llu) + size_in_bytes(%zu)] exceeds scratch memory (%d)\n",
+ rw_batch->offset, rw_batch->size_in_bytes,
+ SCRATCH_TEST_DEV_MEMORY_SIZE);
+ return -EINVAL;
+ }
+ for (i = 0; i < rw_batch->size_in_bytes; ++i) {
+ rw_batch->buf[i] = test_dev->scratch_mem[rw_batch->offset + i];
+ }
+ } else if (entry->type == LWIS_IO_ENTRY_WRITE) {
+ if (entry->rw.offset >= SCRATCH_TEST_DEV_MEMORY_SIZE) {
+ dev_err(test_dev->base_dev.dev, "Offset (%llu) must be < %d\n",
+ entry->rw.offset, SCRATCH_TEST_DEV_MEMORY_SIZE);
+ return -EINVAL;
+ }
+ test_dev->scratch_mem[entry->rw.offset] = entry->rw.val;
+ } else if (entry->type == LWIS_IO_ENTRY_WRITE_BATCH) {
+ rw_batch = &entry->rw_batch;
+ if (rw_batch->offset >= SCRATCH_TEST_DEV_MEMORY_SIZE ||
+ SCRATCH_TEST_DEV_MEMORY_SIZE - rw_batch->offset < rw_batch->size_in_bytes) {
+ dev_err(test_dev->base_dev.dev,
+ "Write range[offset(%llu) + size_in_bytes(%zu)] exceeds scratch memory (%d)\n",
+ rw_batch->offset, rw_batch->size_in_bytes,
+ SCRATCH_TEST_DEV_MEMORY_SIZE);
+ return -EINVAL;
+ }
+ for (i = 0; i < rw_batch->size_in_bytes; ++i) {
+ test_dev->scratch_mem[rw_batch->offset + i] = rw_batch->buf[i];
+ }
+ } else if (entry->type == LWIS_IO_ENTRY_MODIFY) {
+ if (entry->mod.offset >= SCRATCH_TEST_DEV_MEMORY_SIZE) {
+ dev_err(test_dev->base_dev.dev, "Offset (%llu) must be < %d\n",
+ entry->mod.offset, SCRATCH_TEST_DEV_MEMORY_SIZE);
+ return -EINVAL;
+ }
+ reg_value = test_dev->scratch_mem[entry->mod.offset];
+ reg_value &= ~entry->mod.val_mask;
+ reg_value |= entry->mod.val_mask & entry->mod.val;
+ test_dev->scratch_mem[entry->rw.offset] = reg_value;
+ } else {
+ dev_err(test_dev->base_dev.dev, "Invalid IO entry type: %d\n", entry->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lwis_test_device_setup(struct lwis_test_device *test_dev)
+{
+ int ret = 0;
+
+#ifdef CONFIG_OF
+ /* Parse device tree for device configurations */
+ ret = lwis_test_device_parse_dt(test_dev);
+ if (ret) {
+ dev_err(test_dev->base_dev.dev, "Failed to parse device tree\n");
+ }
+#else
+ /* Non-device-tree init: Save for future implementation */
+ ret = -ENOSYS;
+#endif
+
+ return ret;
+}
+
static int lwis_test_device_probe(struct platform_device *plat_dev)
{
int ret = 0;
@@ -52,7 +160,7 @@ static int lwis_test_device_probe(struct platform_device *plat_dev)
/* Allocate test device specific data construct */
test_dev = devm_kzalloc(dev, sizeof(struct lwis_test_device), GFP_KERNEL);
if (!test_dev) {
- dev_err(dev, "Failed to allocate test device structure\n");
+ dev_err(dev, "Failed to allocate TEST device structure\n");
return -ENOMEM;
}
@@ -63,9 +171,23 @@ static int lwis_test_device_probe(struct platform_device *plat_dev)
/* Call the base device probe function */
ret = lwis_base_probe(&test_dev->base_dev, plat_dev);
if (ret) {
- dev_err(dev, "Error in lwis base probe, ret: %d\n", ret);
+ dev_err(dev, "TEST device: Error in lwis base probe: %d\n", ret);
+ goto error_probe;
+ }
+
+ /* Call TEST device specific setup function */
+ ret = lwis_test_device_setup(test_dev);
+ if (ret) {
+ dev_err(test_dev->base_dev.dev, "Error in TEST device initialization\n");
+ lwis_base_unprobe(&test_dev->base_dev);
+ goto error_probe;
}
+ dev_info(test_dev->base_dev.dev, "TEST Device Probe: Success\n");
+
+ return 0;
+
+error_probe:
return ret;
}
@@ -111,6 +233,8 @@ int __init lwis_test_device_init(void)
{
int ret = 0;
+ pr_info("TEST device initialization\n");
+
ret = platform_driver_register(&lwis_driver);
if (ret)
pr_err("platform_driver_register failed: %d\n", ret);
diff --git a/lwis_device_test.h b/lwis_device_test.h
index c3236e2..93b19b7 100644
--- a/lwis_device_test.h
+++ b/lwis_device_test.h
@@ -15,14 +15,23 @@
#include "lwis_commands.h"
#include "lwis_device.h"
+#define SCRATCH_TEST_DEV_MEMORY_SIZE 32
+#define TEST_DEVICE_IRQ_CNT 1
+#define TEST_DEVICE_FAKE_INJECTION_IRQ 999
+
/*
* struct lwis_test_device
* The device majorly control/handle requests from test clients.
*/
struct lwis_test_device {
struct lwis_device base_dev;
+ /*
+ * For testing purposes, scratch memory is used as register space in
+ * test device.
+ */
+ uint8_t scratch_mem[SCRATCH_TEST_DEV_MEMORY_SIZE];
};
int lwis_test_device_deinit(void);
-#endif /* LWIS_DEVICE_TEST_H_ */ \ No newline at end of file
+#endif /* LWIS_DEVICE_TEST_H_ */
diff --git a/lwis_device_top.c b/lwis_device_top.c
index 449936b..3cd17a7 100644
--- a/lwis_device_top.c
+++ b/lwis_device_top.c
@@ -115,7 +115,7 @@ static struct lwis_event_subscriber_list *event_subscriber_list_create(struct lw
struct lwis_top_device *lwis_top_dev =
container_of(lwis_dev, struct lwis_top_device, base_dev);
struct lwis_event_subscriber_list *event_subscriber_list =
- kmalloc(sizeof(struct lwis_event_subscriber_list), GFP_KERNEL);
+ kmalloc(sizeof(struct lwis_event_subscriber_list), GFP_ATOMIC);
if (!event_subscriber_list) {
dev_err(lwis_dev->dev, "Can't allocate event subscriber list\n");
return NULL;
@@ -218,13 +218,14 @@ static int lwis_top_event_subscribe(struct lwis_device *lwis_dev, int64_t trigge
return -EINVAL;
}
+ spin_lock_irqsave(&lwis_top_dev->base_dev.lock, flags);
event_subscriber_list = event_subscriber_list_find_or_create(lwis_dev, trigger_event_id);
if (!event_subscriber_list) {
+ spin_unlock_irqrestore(&lwis_top_dev->base_dev.lock, flags);
dev_err(lwis_dev->dev, "Can't find/create event subscriber list\n");
return -EINVAL;
}
- spin_lock_irqsave(&lwis_top_dev->base_dev.lock, flags);
list_for_each (it_event_subscriber, &event_subscriber_list->list) {
old_subscription = list_entry(it_event_subscriber, struct lwis_event_subscribe_info,
list_node);
@@ -511,20 +512,18 @@ static int lwis_top_device_probe(struct platform_device *plat_dev)
lwis_top_event_subscribe_init(top_dev);
kthread_init_worker(&top_dev->subscribe_worker);
- top_dev->subscribe_worker_thread = kthread_run(kthread_worker_fn,
- &top_dev->subscribe_worker, LWIS_SUBSCRIBER_THREAD_NAME);
- if (IS_ERR(top_dev->subscribe_worker_thread)) {
+ top_dev->subscribe_worker_thread = kthread_run(
+ kthread_worker_fn, &top_dev->subscribe_worker, LWIS_SUBSCRIBER_THREAD_NAME);
+ if (IS_ERR_OR_NULL(top_dev->subscribe_worker_thread)) {
dev_err(top_dev->base_dev.dev, "subscribe kthread_run failed\n");
goto error_probe;
}
- ret = lwis_set_kthread_priority(&top_dev->base_dev,
- top_dev->subscribe_worker_thread,
- SUBSCRIBE_THREAD_PRIORITY);
+ ret = lwis_set_kthread_priority(&top_dev->base_dev, top_dev->subscribe_worker_thread,
+ SUBSCRIBE_THREAD_PRIORITY);
if (ret) {
dev_err(top_dev->base_dev.dev,
- "Failed to set LWIS top subscription kthread priority (%d)",
- ret);
+ "Failed to set LWIS top subscription kthread priority (%d)", ret);
lwis_base_unprobe(&top_dev->base_dev);
goto error_probe;
}
@@ -602,4 +601,4 @@ int lwis_top_device_deinit(void)
{
platform_driver_unregister(&lwis_driver);
return 0;
-} \ No newline at end of file
+}
diff --git a/lwis_device_top.h b/lwis_device_top.h
index e2ef4ea..8d0a324 100644
--- a/lwis_device_top.h
+++ b/lwis_device_top.h
@@ -32,7 +32,6 @@ struct lwis_top_device {
struct kthread_work subscribe_work;
struct list_head emitted_event_list_work;
-
/* Subscription thread */
struct kthread_worker subscribe_worker;
struct task_struct *subscribe_worker_thread;
diff --git a/lwis_dt.c b/lwis_dt.c
index d9f9e07..c844ce9 100644
--- a/lwis_dt.c
+++ b/lwis_dt.c
@@ -25,6 +25,7 @@
#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-"
@@ -50,7 +51,7 @@ static int parse_gpios(struct lwis_device *lwis_dev, char *name, bool *is_presen
}
list = lwis_gpio_list_get(dev, name);
- if (IS_ERR(list)) {
+ if (IS_ERR_OR_NULL(list)) {
pr_err("Error parsing GPIO list %s (%ld)\n", name, PTR_ERR(list));
return PTR_ERR(list);
}
@@ -103,14 +104,14 @@ static int parse_irq_gpios(struct lwis_device *lwis_dev)
}
gpios = lwis_gpio_list_get(dev, "irq");
- if (IS_ERR(gpios)) {
+ if (IS_ERR_OR_NULL(gpios)) {
pr_err("Error parsing irq GPIO list (%ld)\n", PTR_ERR(gpios));
return PTR_ERR(gpios);
}
lwis_dev->irq_gpios_info.gpios = gpios;
lwis_dev->irq_gpios_info.irq_list = lwis_interrupt_list_alloc(lwis_dev, gpios->ndescs);
- if (IS_ERR(lwis_dev->irq_gpios_info.irq_list)) {
+ if (IS_ERR_OR_NULL(lwis_dev->irq_gpios_info.irq_list)) {
ret = -ENOMEM;
lwis_dev->irq_gpios_info.irq_list = NULL;
pr_err("Failed to allocate irq list\n");
@@ -141,8 +142,8 @@ static int parse_irq_gpios(struct lwis_device *lwis_dev)
goto error_parse_irq_gpios;
}
- type_count = of_property_read_variable_u32_array(
- dev_node, "irq-gpios-types", irq_gpios_types, type_count, type_count);
+ type_count = of_property_read_variable_u32_array(dev_node, "irq-gpios-types",
+ irq_gpios_types, type_count, type_count);
if (type_count != count) {
pr_err("Error getting irq-gpios-types: %d\n", type_count);
@@ -252,9 +253,11 @@ static int parse_regulators(struct lwis_device *lwis_dev)
of_property_count_elems_of_size(dev_node, "regulator-voltages", sizeof(u32));
lwis_dev->regulators = lwis_regulator_list_alloc(count);
- if (IS_ERR(lwis_dev->regulators)) {
+ if (IS_ERR_OR_NULL(lwis_dev->regulators)) {
pr_err("Cannot allocate regulator list\n");
- return PTR_ERR(lwis_dev->regulators);
+ ret = PTR_ERR(lwis_dev->regulators);
+ lwis_dev->regulators = NULL;
+ return ret;
}
/* Parse regulator list and acquire the regulator pointers */
@@ -310,9 +313,11 @@ static int parse_clocks(struct lwis_device *lwis_dev)
}
lwis_dev->clocks = lwis_clock_list_alloc(count);
- if (IS_ERR(lwis_dev->clocks)) {
+ if (IS_ERR_OR_NULL(lwis_dev->clocks)) {
pr_err("Cannot allocate clocks list\n");
- return PTR_ERR(lwis_dev->clocks);
+ ret = PTR_ERR(lwis_dev->clocks);
+ lwis_dev->clocks = NULL;
+ return ret;
}
/* Parse and acquire clock pointers and frequencies, if applicable */
@@ -333,7 +338,6 @@ static int parse_clocks(struct lwis_device *lwis_dev)
ret = of_property_read_u32(dev_node, "clock-family", &clock_family);
lwis_dev->clock_family = (ret == 0) ? clock_family : CLOCK_FAMILY_INVALID;
-#ifdef LWIS_BTS_BLOCK_NAME_ENABLED
/* Parse the BTS block names */
bts_count = of_property_count_strings(dev_node, "bts-block-names");
if (bts_count > 0) {
@@ -351,7 +355,6 @@ static int parse_clocks(struct lwis_device *lwis_dev)
for (i = 0; i < MAX_BTS_BLOCK_NUM; ++i) {
lwis_dev->bts_indexes[i] = BTS_UNSUPPORTED;
}
-#endif
#ifdef LWIS_DT_DEBUG
pr_info("%s: clock family %d", lwis_dev->name, lwis_dev->clock_family);
@@ -391,13 +394,13 @@ static int parse_pinctrls(struct lwis_device *lwis_dev, char *expected_state)
/* Set up pinctrl */
pc = devm_pinctrl_get(dev);
- if (IS_ERR(pc)) {
+ if (IS_ERR_OR_NULL(pc)) {
pr_err("Cannot allocate pinctrl\n");
return PTR_ERR(pc);
}
pinctrl_state = pinctrl_lookup_state(pc, expected_state);
- if (IS_ERR(pinctrl_state)) {
+ if (IS_ERR_OR_NULL(pinctrl_state)) {
pr_err("Cannot find pinctrl state %s\n", expected_state);
devm_pinctrl_put(pc);
return PTR_ERR(pinctrl_state);
@@ -637,7 +640,12 @@ static int parse_interrupts(struct lwis_device *lwis_dev)
plat_dev = lwis_dev->plat_dev;
dev_node = plat_dev->dev.of_node;
- count = platform_irq_count(plat_dev);
+ /* Test device type DEVICE_TYPE_TEST used for test, platform independent. */
+ if (lwis_dev->type == DEVICE_TYPE_TEST) {
+ count = TEST_DEVICE_IRQ_CNT;
+ } else {
+ count = platform_irq_count(plat_dev);
+ }
/* No interrupts found, just return */
if (count <= 0) {
@@ -646,9 +654,15 @@ static int parse_interrupts(struct lwis_device *lwis_dev)
}
lwis_dev->irqs = lwis_interrupt_list_alloc(lwis_dev, count);
- if (IS_ERR(lwis_dev->irqs)) {
- pr_err("Failed to allocate IRQ list\n");
- return PTR_ERR(lwis_dev->irqs);
+ if (IS_ERR_OR_NULL(lwis_dev->irqs)) {
+ if (lwis_dev->type == DEVICE_TYPE_TEST) {
+ pr_err("Failed to allocate injection\n");
+ } else {
+ pr_err("Failed to allocate IRQ list\n");
+ }
+ ret = PTR_ERR(lwis_dev->irqs);
+ lwis_dev->irqs = NULL;
+ return ret;
}
for (i = 0; i < count; ++i) {
@@ -740,7 +754,7 @@ static int parse_interrupts(struct lwis_device *lwis_dev)
ret = of_property_read_string(event_info, "irq-type", &irq_type_str);
if (ret && ret != -EINVAL) {
pr_err("Error getting irq-type from dt: %d\n", ret);
- return ret;
+ goto error_event_infos;
} else if (ret && ret == -EINVAL) {
/* The property does not exist, which means regular*/
irq_type = REGULAR_INTERRUPT;
@@ -751,9 +765,11 @@ static int parse_interrupts(struct lwis_device *lwis_dev)
irq_type = AGGREGATE_INTERRUPT;
} else if (strcmp(irq_type_str, "leaf") == 0) {
irq_type = LEAF_INTERRUPT;
+ } else if (strcmp(irq_type_str, "injection") == 0) {
+ irq_type = FAKEEVENT_INTERRUPT;
} else {
pr_err("Invalid irq-type from dt: %s\n", irq_type_str);
- return ret;
+ goto error_event_infos;
}
}
@@ -769,6 +785,12 @@ static int parse_interrupts(struct lwis_device *lwis_dev)
pr_err("Cannot set irq %s\n", name);
goto error_event_infos;
}
+ } else if (irq_type == FAKEEVENT_INTERRUPT) {
+ /*
+ * Hardcode the fake injection irq number to
+ * TEST_DEVICE_FAKE_INJECTION_IRQ
+ */
+ lwis_dev->irqs->irq[i].irq = TEST_DEVICE_FAKE_INJECTION_IRQ;
}
/* Parse event info */
@@ -827,9 +849,11 @@ static int parse_phys(struct lwis_device *lwis_dev)
}
lwis_dev->phys = lwis_phy_list_alloc(count);
- if (IS_ERR(lwis_dev->phys)) {
+ if (IS_ERR_OR_NULL(lwis_dev->phys)) {
pr_err("Failed to allocate PHY list\n");
- return PTR_ERR(lwis_dev->phys);
+ ret = PTR_ERR(lwis_dev->phys);
+ lwis_dev->phys = NULL;
+ return ret;
}
for (i = 0; i < count; ++i) {
@@ -858,7 +882,6 @@ error_parse_phy:
static void parse_bitwidths(struct lwis_device *lwis_dev)
{
- int __maybe_unused ret;
struct device *dev;
struct device_node *dev_node;
u32 addr_bitwidth = 32;
@@ -927,9 +950,11 @@ static int parse_power_seqs(struct lwis_device *lwis_dev, const char *seq_name,
}
*list = lwis_dev_power_seq_list_alloc(power_seq_count);
- if (IS_ERR(*list)) {
+ if (IS_ERR_OR_NULL(*list)) {
pr_err("Failed to allocate power sequence list\n");
- return PTR_ERR(*list);
+ ret = PTR_ERR(*list);
+ *list = NULL;
+ return ret;
}
for (i = 0; i < power_seq_count; ++i) {
@@ -966,7 +991,7 @@ static int parse_power_seqs(struct lwis_device *lwis_dev, const char *seq_name,
if (type_gpio_count > 0 && lwis_dev->gpios_list == NULL) {
lwis_dev->gpios_list = lwis_gpios_list_alloc(type_gpio_count);
- if (IS_ERR(lwis_dev->gpios_list)) {
+ if (IS_ERR_OR_NULL(lwis_dev->gpios_list)) {
pr_err("Failed to allocate gpios list\n");
ret = PTR_ERR(lwis_dev->gpios_list);
goto error_parse_power_seqs;
@@ -987,7 +1012,7 @@ static int parse_power_seqs(struct lwis_device *lwis_dev, const char *seq_name,
seq_item_name = (*list)->seq_info[i].name;
dev = &lwis_dev->plat_dev->dev;
descs = lwis_gpio_list_get(dev, seq_item_name);
- if (IS_ERR(descs)) {
+ if (IS_ERR_OR_NULL(descs)) {
pr_err("Error parsing GPIO list %s (%ld)\n", seq_item_name,
PTR_ERR(descs));
ret = PTR_ERR(descs);
@@ -1021,7 +1046,7 @@ static int parse_power_seqs(struct lwis_device *lwis_dev, const char *seq_name,
if (type_regulator_count > 0 && lwis_dev->regulators == NULL) {
lwis_dev->regulators = lwis_regulator_list_alloc(type_regulator_count);
- if (IS_ERR(lwis_dev->regulators)) {
+ if (IS_ERR_OR_NULL(lwis_dev->regulators)) {
pr_err("Failed to allocate regulator list\n");
ret = PTR_ERR(lwis_dev->regulators);
goto error_parse_power_seqs;
@@ -1141,6 +1166,33 @@ 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;
@@ -1167,6 +1219,19 @@ static int parse_i2c_lock_group_id(struct lwis_i2c_device *i2c_dev)
return 0;
}
+static int parse_transaction_process_limit(struct lwis_device *lwis_dev)
+{
+ struct device_node *dev_node;
+
+ lwis_dev->transaction_process_limit = 0;
+ dev_node = lwis_dev->plat_dev->dev.of_node;
+
+ of_property_read_u32(dev_node, "transaction-process-limit",
+ &lwis_dev->transaction_process_limit);
+
+ return 0;
+}
+
int lwis_base_parse_dt(struct lwis_device *lwis_dev)
{
struct device *dev;
@@ -1297,6 +1362,7 @@ int lwis_base_parse_dt(struct lwis_device *lwis_dev)
parse_access_mode(lwis_dev);
parse_thread_priority(lwis_dev);
parse_bitwidths(lwis_dev);
+ parse_transaction_process_limit(lwis_dev);
lwis_dev->bts_scenario_name = NULL;
of_property_read_string(dev_node, "bts-scenario", &lwis_dev->bts_scenario_name);
@@ -1340,6 +1406,12 @@ 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;
}
@@ -1391,3 +1463,9 @@ int lwis_top_device_parse_dt(struct lwis_top_device *top_dev)
/* To be implemented */
return 0;
}
+
+int lwis_test_device_parse_dt(struct lwis_test_device *test_dev)
+{
+ /* To be implemented */
+ return 0;
+}
diff --git a/lwis_dt.h b/lwis_dt.h
index 8d3195f..06ab764 100644
--- a/lwis_dt.h
+++ b/lwis_dt.h
@@ -16,6 +16,7 @@
#include "lwis_device.h"
#include "lwis_device_i2c.h"
#include "lwis_device_ioreg.h"
+#include "lwis_device_test.h"
#include "lwis_device_top.h"
/*
@@ -42,4 +43,10 @@ int lwis_ioreg_device_parse_dt(struct lwis_ioreg_device *ioreg_dev);
*/
int lwis_top_device_parse_dt(struct lwis_top_device *top_dev);
+/*
+ * lwis_test_device_parse_dt: Parse device configurations specifically for
+ * TEST devices.
+ */
+int lwis_test_device_parse_dt(struct lwis_test_device *test_dev);
+
#endif /* LWIS_DT_H_ */
diff --git a/lwis_event.c b/lwis_event.c
index baccbd9..971d70a 100644
--- a/lwis_event.c
+++ b/lwis_event.c
@@ -24,13 +24,13 @@
/* Exposes the device id embedded in the event id */
#define EVENT_OWNER_DEVICE_ID(x) ((x >> LWIS_EVENT_ID_EVENT_CODE_LEN) & 0xFFFF)
-#define lwis_dev_err_ratelimited(dev, fmt, ...) \
- { \
- static int64_t timestamp = 0; \
- if (ktime_to_ns(lwis_get_time()) - timestamp > 200000000LL) { \
- dev_err(dev, fmt, ##__VA_ARGS__); \
- timestamp = ktime_to_ns(lwis_get_time()); \
- } \
+#define lwis_dev_err_ratelimited(dev, fmt, ...) \
+ { \
+ static int64_t timestamp = 0; \
+ if (ktime_to_ns(lwis_get_time()) - timestamp > 200000000LL) { \
+ dev_err(dev, fmt, ##__VA_ARGS__); \
+ timestamp = ktime_to_ns(lwis_get_time()); \
+ } \
}
/*
@@ -989,15 +989,15 @@ int lwis_pending_events_emit(struct lwis_device *lwis_dev, struct list_head *pen
while (!list_empty(pending_events)) {
event = list_first_entry(pending_events, struct lwis_event_entry, node);
- emit_result = lwis_device_event_emit_impl(lwis_dev, event->event_info.event_id,
- event->event_info.payload_buffer,
- event->event_info.payload_size,
- pending_events);
+ emit_result =
+ lwis_device_event_emit_impl(lwis_dev, event->event_info.event_id,
+ event->event_info.payload_buffer,
+ event->event_info.payload_size, pending_events);
if (emit_result) {
return_val = emit_result;
dev_warn_ratelimited(lwis_dev->dev,
- "lwis_device_pending_event_emit error on ID 0x%llx\n",
- event->event_info.event_id);
+ "lwis_device_pending_event_emit error on ID 0x%llx\n",
+ event->event_info.event_id);
}
list_del(&event->node);
kfree(event);
diff --git a/lwis_fence.c b/lwis_fence.c
index dec3e2c..184dab7 100644
--- a/lwis_fence.c
+++ b/lwis_fence.c
@@ -20,10 +20,9 @@
#define HASH_CLIENT(x) hash_ptr(x, LWIS_CLIENTS_HASH_BITS)
-#ifdef LWIS_FENCE_ENABLED
bool lwis_fence_debug;
module_param(lwis_fence_debug, bool, 0644);
-#endif
+
static int lwis_fence_release(struct inode *node, struct file *fp);
static ssize_t lwis_fence_get_status(struct file *fp, char __user *user_buffer, size_t len,
loff_t *offset);
@@ -62,12 +61,10 @@ static int lwis_fence_release(struct inode *node, struct file *fp)
struct list_head *it_tran, *it_tran_tmp;
int i;
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(lwis_fence->lwis_top_dev->dev, "Releasing lwis_fence fd-%d",
lwis_fence->fd);
}
-#endif
if (lwis_fence->status == LWIS_FENCE_STATUS_NOT_SIGNALED) {
dev_err(lwis_fence->lwis_top_dev->dev,
@@ -241,11 +238,9 @@ int lwis_fence_create(struct lwis_device *lwis_dev)
new_fence->status = LWIS_FENCE_STATUS_NOT_SIGNALED;
spin_lock_init(&new_fence->lock);
init_waitqueue_head(&new_fence->status_wait_queue);
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(lwis_dev->dev, "lwis_fence created new LWIS fence fd: %d", new_fence->fd);
}
-#endif
return fd_or_err;
}
@@ -327,21 +322,19 @@ static int lwis_trigger_fence_add_transaction(int fence_fd, struct lwis_client *
transaction->trigger_fence_fps[transaction->num_trigger_fences++] = fp;
tx_list = transaction_list_find_or_create(lwis_fence, client);
list_add(&pending_transaction_id->list_node, &tx_list->list);
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(client->lwis_dev->dev,
"lwis_fence transaction id %llu added to its trigger fence fd %d ",
transaction->info.id, lwis_fence->fd);
}
-#endif
} else {
kfree(pending_transaction_id);
-#ifdef LWIS_FENCE_DBG
- dev_info(
- client->lwis_dev->dev,
- "lwis_fence fd-%d not added to transaction id %llu, fence already signaled with error code %d \n",
- fence_fd, transaction->info.id, lwis_fence->status);
-#endif
+ if (lwis_fence_debug) {
+ dev_info(
+ client->lwis_dev->dev,
+ "lwis_fence fd-%d not added to transaction id %llu, fence already signaled with error code %d \n",
+ fence_fd, transaction->info.id, lwis_fence->status);
+ }
if (!transaction->info.is_level_triggered) {
/* If level triggering is disabled, return an error. */
fput(fp);
@@ -375,7 +368,7 @@ bool lwis_event_triggered_condition_ready(struct lwis_transaction *transaction,
{
int32_t operator_type;
size_t all_signaled;
- struct lwis_transaction_info *info = &transaction->info;
+ struct lwis_transaction_info_v2 *info = &transaction->info;
int i;
struct lwis_fence *lwis_fence;
bool is_node_signaled = false;
@@ -482,7 +475,7 @@ bool lwis_fence_triggered_condition_ready(struct lwis_transaction *transaction,
int lwis_parse_trigger_condition(struct lwis_client *client, struct lwis_transaction *transaction)
{
- struct lwis_transaction_info *info;
+ struct lwis_transaction_info_v2 *info;
struct lwis_device *lwis_dev;
int i, ret;
@@ -540,7 +533,7 @@ int ioctl_lwis_fence_create(struct lwis_device *lwis_dev, int32_t __user *msg)
int lwis_initialize_transaction_fences(struct lwis_client *client,
struct lwis_transaction *transaction)
{
- struct lwis_transaction_info *info = &transaction->info;
+ struct lwis_transaction_info_v2 *info = &transaction->info;
struct lwis_device *lwis_dev = client->lwis_dev;
int i;
int fd_or_err;
@@ -609,13 +602,11 @@ int lwis_add_completion_fence(struct lwis_client *client, struct lwis_transactio
return -ENOMEM;
}
list_add(&fence_pending_signal->node, &transaction->completion_fence_list);
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(client->lwis_dev->dev,
"lwis_fence transaction id %llu add completion fence fd %d ",
transaction->info.id, lwis_fence->fd);
}
-#endif
return 0;
}
diff --git a/lwis_fence.h b/lwis_fence.h
index 71b17e5..a79fc06 100644
--- a/lwis_fence.h
+++ b/lwis_fence.h
@@ -18,9 +18,8 @@
#define LWIS_CLIENTS_HASH_BITS 8
-#ifdef LWIS_FENCE_ENABLED
extern bool lwis_fence_debug;
-#endif
+
struct lwis_fence {
int fd;
int status;
@@ -58,8 +57,6 @@ int ioctl_lwis_fence_create(struct lwis_device *lwis_dev, int32_t __user *msg);
*/
struct lwis_device *lwis_fence_get(int fd);
-#ifdef LWIS_FENCE_ENABLED
-
/* Creates all fences that do not currently exist */
int lwis_initialize_transaction_fences(struct lwis_client *client,
struct lwis_transaction *transaction);
@@ -70,16 +67,13 @@ bool lwis_event_triggered_condition_ready(struct lwis_transaction *transaction,
struct lwis_transaction *weak_transaction,
int64_t event_id, int64_t event_counter);
-
-bool lwis_fence_triggered_condition_ready(struct lwis_transaction *transaction,
- int fence_status);
+bool lwis_fence_triggered_condition_ready(struct lwis_transaction *transaction, int fence_status);
/*
* lwis_parse_trigger_condition: Add the transaction to the associated trigger
* fence and event lists.
*/
-int lwis_parse_trigger_condition(struct lwis_client *client,
- struct lwis_transaction *transaction);
+int lwis_parse_trigger_condition(struct lwis_client *client, struct lwis_transaction *transaction);
/*
* lwis_fence_signal: Signals the lwis_fence with the provided error code.
@@ -107,71 +101,5 @@ void lwis_fences_pending_signal_emit(struct lwis_device *lwis_device,
void lwis_pending_fences_move_all(struct lwis_device *lwis_device,
struct lwis_transaction *transaction,
struct list_head *pending_fences, int error_code);
-#else
-
-static inline int lwis_initialize_transaction_fences(struct lwis_client *client,
- struct lwis_transaction *transaction)
-{
- return 0;
-}
-
-static inline
-bool lwis_triggered_by_condition(struct lwis_transaction *transaction)
-{
- return false;
-}
-
-static inline
-bool lwis_event_triggered_condition_ready(struct lwis_transaction *transaction,
- struct lwis_transaction *weak_transaction,
- int64_t event_id, int64_t event_counter)
-{
- return false;
-}
-
-static inline
-bool lwis_fence_triggered_condition_ready(struct lwis_transaction *transaction,
- int fence_status)
-{
- return false;
-}
-
-static inline
-int lwis_parse_trigger_condition(struct lwis_client *client, struct
- lwis_transaction *transaction)
-{
- return 0;
-}
-
-static inline int lwis_fence_signal(struct lwis_fence *lwis_fence, int status)
-{
- return 0;
-}
-
-static inline int lwis_add_completion_fence(struct lwis_client *client,
- struct lwis_transaction *transaction)
-{
- return 0;
-}
-
-static inline struct lwis_fence_pending_signal *
-lwis_fence_pending_signal_create(struct lwis_fence *fence)
-{
- return NULL;
-}
-
-static inline void lwis_fences_pending_signal_emit(struct lwis_device *lwis_device,
- struct list_head *pending_fences)
-{
- return;
-}
-
-static inline void lwis_pending_fences_move_all(struct lwis_device *lwis_device,
- struct lwis_transaction *transaction,
- struct list_head *pending_fences, int error_code)
-{
- return;
-}
-#endif
#endif /* LWIS_IOCTL_H_ */
diff --git a/lwis_i2c.c b/lwis_i2c.c
index aa8fb87..4ba20f4 100644
--- a/lwis_i2c.c
+++ b/lwis_i2c.c
@@ -73,10 +73,13 @@ static int perform_read_transfer(struct i2c_client *client, struct i2c_msg *msg,
const int num_msg = 2;
+ char trace_name[LWIS_MAX_NAME_STRING_LEN];
+ scnprintf(trace_name, LWIS_MAX_NAME_STRING_LEN, "i2c_read_%s", lwis_dev->name);
+
value_to_buf(offset, wbuf, offset_size_bytes);
- LWIS_ATRACE_FUNC_BEGIN(lwis_dev, "i2c_read");
+ LWIS_ATRACE_FUNC_BEGIN(lwis_dev, trace_name);
ret = i2c_transfer(client->adapter, msg, num_msg);
- LWIS_ATRACE_FUNC_END(lwis_dev, "i2c_read");
+ LWIS_ATRACE_FUNC_END(lwis_dev, trace_name);
return (ret == num_msg) ? 0 : ret;
}
@@ -89,11 +92,14 @@ static int perform_write_transfer(struct i2c_client *client, struct i2c_msg *msg
const int num_msg = 1;
+ char trace_name[LWIS_MAX_NAME_STRING_LEN];
+ scnprintf(trace_name, LWIS_MAX_NAME_STRING_LEN, "i2c_write_%s", lwis_dev->name);
+
value_to_buf(offset, buf, offset_size_bytes);
value_to_buf(value, buf + offset_size_bytes, value_size_bytes);
- LWIS_ATRACE_FUNC_BEGIN(lwis_dev, "i2c_write");
+ LWIS_ATRACE_FUNC_BEGIN(lwis_dev, trace_name);
ret = i2c_transfer(client->adapter, msg, num_msg);
- LWIS_ATRACE_FUNC_END(lwis_dev, "i2c_write");
+ LWIS_ATRACE_FUNC_END(lwis_dev, trace_name);
return (ret == num_msg) ? 0 : ret;
}
@@ -107,12 +113,15 @@ static int perform_write_batch_transfer(struct i2c_client *client, struct i2c_ms
const int num_msg = 1;
+ char trace_name[LWIS_MAX_NAME_STRING_LEN];
+ scnprintf(trace_name, LWIS_MAX_NAME_STRING_LEN, "i2c_write_batch_%s", lwis_dev->name);
+
value_to_buf(offset, buf, offset_size_bytes);
memcpy(buf + offset_size_bytes, value_buf, value_size_bytes);
- LWIS_ATRACE_FUNC_BEGIN(lwis_dev, "i2c_write_batch");
+ LWIS_ATRACE_FUNC_BEGIN(lwis_dev, trace_name);
ret = i2c_transfer(client->adapter, msg, num_msg);
- LWIS_ATRACE_FUNC_END(lwis_dev, "i2c_write_batch");
+ LWIS_ATRACE_FUNC_END(lwis_dev, trace_name);
return (ret == num_msg) ? 0 : ret;
}
@@ -130,7 +139,7 @@ int lwis_i2c_set_state(struct lwis_i2c_device *i2c, const char *state_str)
state_to_set = i2c->pinctrl_default_state_only ? "default" : state_str;
state = pinctrl_lookup_state(i2c->state_pinctrl, state_to_set);
- if (IS_ERR(state)) {
+ if (IS_ERR_OR_NULL(state)) {
dev_err(i2c->base_dev.dev, "State %s not found (%ld)\n", state_str, PTR_ERR(state));
return PTR_ERR(state);
}
@@ -144,8 +153,7 @@ int lwis_i2c_set_state(struct lwis_i2c_device *i2c, const char *state_str)
return 0;
}
-static int i2c_read(struct lwis_i2c_device *i2c, uint64_t offset, uint64_t *value,
- struct lwis_device *lwis_dev)
+static int i2c_read(struct lwis_i2c_device *i2c, uint64_t offset, uint64_t *value)
{
int ret = 0;
u8 *wbuf;
@@ -200,7 +208,7 @@ static int i2c_read(struct lwis_i2c_device *i2c, uint64_t offset, uint64_t *valu
msg[1].len = value_bytes;
msg[1].buf = rbuf;
- ret = perform_read_transfer(client, msg, offset, offset_bytes, lwis_dev);
+ ret = perform_read_transfer(client, msg, offset, offset_bytes, &i2c->base_dev);
if (ret) {
dev_err(i2c->base_dev.dev, "I2C Read failed: Offset 0x%llx (%d)\n", offset, ret);
@@ -217,8 +225,7 @@ error_rbuf_alloc:
return ret;
}
-static int i2c_write(struct lwis_i2c_device *i2c, uint64_t offset, uint64_t value,
- struct lwis_device *lwis_dev)
+static int i2c_write(struct lwis_i2c_device *i2c, uint64_t offset, uint64_t value)
{
int ret;
u8 *buf;
@@ -268,7 +275,7 @@ static int i2c_write(struct lwis_i2c_device *i2c, uint64_t offset, uint64_t valu
msg.len = msg_bytes;
ret = perform_write_transfer(client, &msg, offset, offset_bytes, value_bytes, value,
- lwis_dev);
+ &i2c->base_dev);
if (ret) {
dev_err(i2c->base_dev.dev, "I2C Write failed: Offset 0x%llx Value 0x%llx (%d)\n",
@@ -281,7 +288,7 @@ static int i2c_write(struct lwis_i2c_device *i2c, uint64_t offset, uint64_t valu
}
static int i2c_read_batch(struct lwis_i2c_device *i2c, uint64_t start_offset, uint8_t *read_buf,
- int read_buf_size, struct lwis_device *lwis_dev)
+ int read_buf_size)
{
int ret = 0;
uint8_t *wbuf;
@@ -319,7 +326,7 @@ static int i2c_read_batch(struct lwis_i2c_device *i2c, uint64_t start_offset, ui
msg[1].len = read_buf_size;
msg[1].buf = read_buf;
- ret = perform_read_transfer(client, msg, start_offset, offset_bytes, lwis_dev);
+ ret = perform_read_transfer(client, msg, start_offset, offset_bytes, &i2c->base_dev);
if (ret) {
dev_err(i2c->base_dev.dev, "I2C Read Batch failed: Start Offset 0x%llx (%d)\n",
@@ -331,7 +338,7 @@ static int i2c_read_batch(struct lwis_i2c_device *i2c, uint64_t start_offset, ui
}
static int i2c_write_batch(struct lwis_i2c_device *i2c, uint64_t start_offset, uint8_t *write_buf,
- int write_buf_size, struct lwis_device *lwis_dev)
+ int write_buf_size)
{
int ret;
uint8_t *buf;
@@ -372,7 +379,7 @@ static int i2c_write_batch(struct lwis_i2c_device *i2c, uint64_t start_offset, u
msg.len = msg_bytes;
ret = perform_write_batch_transfer(client, &msg, start_offset, offset_bytes, write_buf_size,
- write_buf, lwis_dev);
+ write_buf, &i2c->base_dev);
if (ret) {
dev_err(i2c->base_dev.dev, "I2C Write Batch failed: Start Offset 0x%llx (%d)\n",
@@ -384,8 +391,7 @@ static int i2c_write_batch(struct lwis_i2c_device *i2c, uint64_t start_offset, u
return ret;
}
-int lwis_i2c_io_entry_rw(struct lwis_i2c_device *i2c, struct lwis_io_entry *entry,
- struct lwis_device *lwis_dev)
+int lwis_i2c_io_entry_rw(struct lwis_i2c_device *i2c, struct lwis_io_entry *entry)
{
int ret;
uint64_t reg_value;
@@ -396,27 +402,27 @@ int lwis_i2c_io_entry_rw(struct lwis_i2c_device *i2c, struct lwis_io_entry *entr
}
if (entry->type == LWIS_IO_ENTRY_READ) {
- return i2c_read(i2c, entry->rw.offset, &entry->rw.val, lwis_dev);
+ return i2c_read(i2c, entry->rw.offset, &entry->rw.val);
}
if (entry->type == LWIS_IO_ENTRY_WRITE) {
- return i2c_write(i2c, entry->rw.offset, entry->rw.val, lwis_dev);
+ return i2c_write(i2c, entry->rw.offset, entry->rw.val);
}
if (entry->type == LWIS_IO_ENTRY_MODIFY) {
- ret = i2c_read(i2c, entry->mod.offset, &reg_value, lwis_dev);
+ ret = i2c_read(i2c, entry->mod.offset, &reg_value);
if (ret) {
return ret;
}
reg_value &= ~entry->mod.val_mask;
reg_value |= entry->mod.val_mask & entry->mod.val;
- return i2c_write(i2c, entry->mod.offset, reg_value, lwis_dev);
+ return i2c_write(i2c, entry->mod.offset, reg_value);
}
if (entry->type == LWIS_IO_ENTRY_READ_BATCH) {
return i2c_read_batch(i2c, entry->rw_batch.offset, entry->rw_batch.buf,
- entry->rw_batch.size_in_bytes, lwis_dev);
+ entry->rw_batch.size_in_bytes);
}
if (entry->type == LWIS_IO_ENTRY_WRITE_BATCH) {
return i2c_write_batch(i2c, entry->rw_batch.offset, entry->rw_batch.buf,
- entry->rw_batch.size_in_bytes, lwis_dev);
+ entry->rw_batch.size_in_bytes);
}
dev_err(i2c->base_dev.dev, "Invalid IO entry type: %d\n", entry->type);
return -EINVAL;
diff --git a/lwis_i2c.h b/lwis_i2c.h
index e21ec73..d2e7e19 100644
--- a/lwis_i2c.h
+++ b/lwis_i2c.h
@@ -33,7 +33,6 @@ int lwis_i2c_set_state(struct lwis_i2c_device *i2c, const char *state_str);
* lwis_i2c_io_entry_rw: Read/Write from i2c bus via io_entry request.
* The readback values will be stored in the entry.
*/
-int lwis_i2c_io_entry_rw(struct lwis_i2c_device *i2c, struct lwis_io_entry *entry,
- struct lwis_device *lwis_dev);
+int lwis_i2c_io_entry_rw(struct lwis_i2c_device *i2c, struct lwis_io_entry *entry);
#endif /* LWIS_I2C_H_ */
diff --git a/lwis_i2c_bus_manager.c b/lwis_i2c_bus_manager.c
new file mode 100644
index 0000000..c030f27
--- /dev/null
+++ b/lwis_i2c_bus_manager.c
@@ -0,0 +1,751 @@
+/*
+ * 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) {
+ 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)
+{
+ 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) {
+ 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;
+}
+
+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) {
+ 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);
+ 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) {
+ 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);
+ 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);
+} \ 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..b278124
--- /dev/null
+++ b/lwis_i2c_bus_manager.h
@@ -0,0 +1,118 @@
+
+/*
+ * 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"
+
+/* 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;
+
+/* 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_nodes;
+};
+
+/*
+ * 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 */
+ struct mutex 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[I2C_MAX_PRIORITY_LEVELS];
+ /* 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);
+
+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
new file mode 100644
index 0000000..1ca802f
--- /dev/null
+++ b/lwis_i2c_sched.c
@@ -0,0 +1,62 @@
+/*
+ * 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
+*/
+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))) {
+ 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_nodes = 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_client = NULL;
+ kfree(process_request);
+ process_request = NULL;
+ --process_queue->number_of_nodes;
+ }
+} \ No newline at end of file
diff --git a/lwis_i2c_sched.h b/lwis_i2c_sched.h
new file mode 100644
index 0000000..22073af
--- /dev/null
+++ b/lwis_i2c_sched.h
@@ -0,0 +1,33 @@
+/*
+ * 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_client *requesting_client;
+ 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);
+
+#endif /* LWIS_I2C_SCHED_H_ */ \ No newline at end of file
diff --git a/lwis_interrupt.c b/lwis_interrupt.c
index cb424d4..62dd3ad 100644
--- a/lwis_interrupt.c
+++ b/lwis_interrupt.c
@@ -416,6 +416,16 @@ error:
return IRQ_HANDLED;
}
+int lwis_fake_event_inject(void *data)
+{
+ struct lwis_interrupt *irq = (struct lwis_interrupt *)data;
+ uint64_t source_value = 0x00000020ll, overflow_value = 0;
+
+ lwis_interrupt_emit_events(irq, source_value, overflow_value);
+
+ return irq->irq;
+}
+
static int lwis_interrupt_handle_aggregation(struct lwis_interrupt *irq, uint64_t source_value)
{
struct lwis_interrupt_leaf_node *leaf;
diff --git a/lwis_interrupt.h b/lwis_interrupt.h
index 345fa3f..d6106bc 100644
--- a/lwis_interrupt.h
+++ b/lwis_interrupt.h
@@ -24,7 +24,8 @@ enum lwis_interrupt_types {
REGULAR_INTERRUPT,
AGGREGATE_INTERRUPT,
LEAF_INTERRUPT,
- GPIO_HW_INTERRUPT
+ GPIO_HW_INTERRUPT,
+ FAKEEVENT_INTERRUPT
};
/*
@@ -185,4 +186,6 @@ int lwis_interrupt_event_enable(struct lwis_interrupt_list *list, int64_t event_
*/
void lwis_interrupt_print(struct lwis_interrupt_list *list);
+int lwis_fake_event_inject(void *data);
+
#endif /* LWIS_INTERRUPT_H_ */
diff --git a/lwis_io_entry.c b/lwis_io_entry.c
index df50f96..7df9e9b 100644
--- a/lwis_io_entry.c
+++ b/lwis_io_entry.c
@@ -16,7 +16,7 @@
#include "lwis_io_entry.h"
#include "lwis_util.h"
-int lwis_io_entry_poll(struct lwis_device *lwis_dev, struct lwis_io_entry *entry)
+int lwis_io_entry_poll(struct lwis_device *lwis_dev, struct lwis_io_entry *entry, bool is_short)
{
uint64_t val, start;
uint64_t timeout_ms = entry->read_assert.timeout_ms;
@@ -40,8 +40,13 @@ int lwis_io_entry_poll(struct lwis_device *lwis_dev, struct lwis_io_entry *entry
entry->read_assert.bid, entry->read_assert.offset);
return -ETIMEDOUT;
}
- /* Sleep for 1ms */
- usleep_range(1000, 1000);
+ if (is_short) {
+ /* Sleep for 10us */
+ usleep_range(10, 10);
+ } else {
+ /* Sleep for 1ms */
+ usleep_range(1000, 1000);
+ }
}
return ret;
}
@@ -64,3 +69,15 @@ int lwis_io_entry_read_assert(struct lwis_device *lwis_dev, struct lwis_io_entry
}
return -EINVAL;
}
+
+int lwis_io_entry_wait(struct lwis_device *lwis_dev, struct lwis_io_entry *entry)
+{
+ // Check if the sleep time is within the range.
+ if (entry->wait_us >= MIN_WAIT_TIME && entry->wait_us <= MAX_WAIT_TIME) {
+ usleep_range(entry->wait_us, entry->wait_us);
+ return 0;
+ }
+ dev_warn(lwis_dev->dev, "Sleep time should be within %dus ~ %dus\n", MIN_WAIT_TIME,
+ MAX_WAIT_TIME);
+ return -EINVAL;
+}
diff --git a/lwis_io_entry.h b/lwis_io_entry.h
index ae1b2cc..d8bd81a 100644
--- a/lwis_io_entry.h
+++ b/lwis_io_entry.h
@@ -14,11 +14,16 @@
#include "lwis_commands.h"
#include "lwis_device.h"
+/* Minimum value of sleep time in us */
+#define MIN_WAIT_TIME 10
+/* Maximum value of sleep time in us */
+#define MAX_WAIT_TIME 1000000
+
/*
* lwis_io_entry_poll:
* Polls a register for a specified time or until it reaches the expected value.
*/
-int lwis_io_entry_poll(struct lwis_device *lwis_dev, struct lwis_io_entry *entry);
+int lwis_io_entry_poll(struct lwis_device *lwis_dev, struct lwis_io_entry *entry, bool is_short);
/*
* lwis_io_entry_read_assert:
@@ -26,4 +31,10 @@ int lwis_io_entry_poll(struct lwis_device *lwis_dev, struct lwis_io_entry *entry
*/
int lwis_io_entry_read_assert(struct lwis_device *lwis_dev, struct lwis_io_entry *entry);
-#endif /* LWIS_IO_ENTRY_H_ */ \ No newline at end of file
+/*
+ * lwis_io_entry_wait:
+ * Waits for a settling time to meet the devices to function properly.
+ */
+int lwis_io_entry_wait(struct lwis_device *lwis_dev, struct lwis_io_entry *entry);
+
+#endif /* LWIS_IO_ENTRY_H_ */
diff --git a/lwis_ioctl.c b/lwis_ioctl.c
index 1441388..b2fbe1f 100644
--- a/lwis_ioctl.c
+++ b/lwis_ioctl.c
@@ -19,12 +19,13 @@
#include "lwis_allocator.h"
#include "lwis_buffer.h"
-#include "lwis_cmd.h"
#include "lwis_commands.h"
+#include "lwis_debug.h"
#include "lwis_device.h"
#include "lwis_device_dpm.h"
#include "lwis_device_i2c.h"
#include "lwis_device_ioreg.h"
+#include "lwis_device_test.h"
#include "lwis_event.h"
#include "lwis_fence.h"
#include "lwis_i2c.h"
@@ -35,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)
@@ -51,106 +53,6 @@ static void lwis_ioctl_pr_err(struct lwis_device *lwis_dev, unsigned int ioctl_t
strscpy(type_name, STRINGIFY(LWIS_CMD_PACKET), sizeof(type_name));
exp_size = IOCTL_ARG_SIZE(LWIS_CMD_PACKET);
break;
- case IOCTL_TO_ENUM(LWIS_GET_DEVICE_INFO):
- strscpy(type_name, STRINGIFY(LWIS_GET_DEVICE_INFO), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_GET_DEVICE_INFO);
- break;
- case IOCTL_TO_ENUM(LWIS_BUFFER_ALLOC):
- strscpy(type_name, STRINGIFY(LWIS_BUFFER_ALLOC), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_BUFFER_ALLOC);
- break;
- case IOCTL_TO_ENUM(LWIS_BUFFER_FREE):
- strscpy(type_name, STRINGIFY(LWIS_BUFFER_FREE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_BUFFER_FREE);
- break;
- case IOCTL_TO_ENUM(LWIS_BUFFER_ENROLL):
- strscpy(type_name, STRINGIFY(LWIS_BUFFER_ENROLL), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_BUFFER_ENROLL);
- break;
- case IOCTL_TO_ENUM(LWIS_BUFFER_DISENROLL):
- strscpy(type_name, STRINGIFY(LWIS_BUFFER_DISENROLL), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_BUFFER_DISENROLL);
- break;
- case IOCTL_TO_ENUM(LWIS_BUFFER_CPU_ACCESS):
- strscpy(type_name, STRINGIFY(LWIS_BUFFER_CPU_ACCESS), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_BUFFER_CPU_ACCESS);
- break;
- case IOCTL_TO_ENUM(LWIS_REG_IO):
- strscpy(type_name, STRINGIFY(LWIS_REG_IO), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_REG_IO);
- break;
- case IOCTL_TO_ENUM(LWIS_DEVICE_ENABLE):
- strscpy(type_name, STRINGIFY(LWIS_DEVICE_ENABLE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_DEVICE_ENABLE);
- break;
- case IOCTL_TO_ENUM(LWIS_DEVICE_DISABLE):
- strscpy(type_name, STRINGIFY(LWIS_DEVICE_DISABLE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_DEVICE_DISABLE);
- break;
- case IOCTL_TO_ENUM(LWIS_DEVICE_RESET):
- strscpy(type_name, STRINGIFY(LWIS_DEVICE_RESET), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_DEVICE_RESET);
- break;
- case IOCTL_TO_ENUM(LWIS_DUMP_DEBUG_STATE):
- strscpy(type_name, STRINGIFY(LWIS_DUMP_DEBUG_STATE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_DUMP_DEBUG_STATE);
- break;
- case IOCTL_TO_ENUM(LWIS_EVENT_CONTROL_GET):
- strscpy(type_name, STRINGIFY(LWIS_EVENT_CONTROL_GET), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_EVENT_CONTROL_GET);
- break;
- case IOCTL_TO_ENUM(LWIS_EVENT_CONTROL_SET):
- strscpy(type_name, STRINGIFY(LWIS_EVENT_CONTROL_SET), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_EVENT_CONTROL_SET);
- break;
- case IOCTL_TO_ENUM(LWIS_EVENT_DEQUEUE):
- strscpy(type_name, STRINGIFY(LWIS_EVENT_DEQUEUE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_EVENT_DEQUEUE);
- break;
- case IOCTL_TO_ENUM(LWIS_TIME_QUERY):
- strscpy(type_name, STRINGIFY(LWIS_TIME_QUERY), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_TIME_QUERY);
- break;
- case IOCTL_TO_ENUM(LWIS_TRANSACTION_SUBMIT):
- strscpy(type_name, STRINGIFY(LWIS_TRANSACTION_SUBMIT), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_TRANSACTION_SUBMIT);
- break;
- case IOCTL_TO_ENUM(LWIS_TRANSACTION_CANCEL):
- strscpy(type_name, STRINGIFY(LWIS_TRANSACTION_CANCEL), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_TRANSACTION_CANCEL);
- break;
- case IOCTL_TO_ENUM(LWIS_TRANSACTION_REPLACE):
- strscpy(type_name, STRINGIFY(LWIS_TRANSACTION_REPLACE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_TRANSACTION_REPLACE);
- break;
- case IOCTL_TO_ENUM(LWIS_DPM_CLK_UPDATE):
- strscpy(type_name, STRINGIFY(LWIS_DPM_CLK_UPDATE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_DPM_CLK_UPDATE);
- break;
- case IOCTL_TO_ENUM(LWIS_ECHO):
- strscpy(type_name, STRINGIFY(LWIS_ECHO), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_ECHO);
- break;
- case IOCTL_TO_ENUM(LWIS_DPM_QOS_UPDATE):
- strscpy(type_name, STRINGIFY(LWIS_DPM_QOS_UPDATE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_DPM_QOS_UPDATE);
- break;
- case IOCTL_TO_ENUM(LWIS_DPM_GET_CLOCK):
- strscpy(type_name, STRINGIFY(LWIS_DPM_GET_CLOCK), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_DPM_GET_CLOCK);
- break;
- case IOCTL_TO_ENUM(LWIS_PERIODIC_IO_SUBMIT):
- strscpy(type_name, STRINGIFY(LWIS_PERIODIC_IO_SUBMIT), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_PERIODIC_IO_SUBMIT);
- break;
- case IOCTL_TO_ENUM(LWIS_PERIODIC_IO_CANCEL):
- strscpy(type_name, STRINGIFY(LWIS_PERIODIC_IO_CANCEL), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_PERIODIC_IO_CANCEL);
- break;
- case IOCTL_TO_ENUM(LWIS_FENCE_CREATE):
- strscpy(type_name, STRINGIFY(LWIS_FENCE_CREATE), sizeof(type_name));
- exp_size = IOCTL_ARG_SIZE(LWIS_FENCE_CREATE);
- break;
default:
strscpy(type_name, "UNDEFINED", sizeof(type_name));
exp_size = 0;
@@ -168,64 +70,6 @@ static void lwis_ioctl_pr_err(struct lwis_device *lwis_dev, unsigned int ioctl_t
}
}
-static int ioctl_get_device_info(struct lwis_device *lwis_dev, struct lwis_device_info *msg)
-{
- int i;
- struct lwis_device_info k_info = { .id = lwis_dev->id,
- .type = lwis_dev->type,
- .num_clks = 0,
- .num_regs = 0,
- .transaction_worker_thread_pid = -1,
- .periodic_io_thread_pid = -1 };
- strscpy(k_info.name, lwis_dev->name, LWIS_MAX_NAME_STRING_LEN);
-
- if (lwis_dev->clocks) {
- k_info.num_clks = lwis_dev->clocks->count;
- for (i = 0; i < lwis_dev->clocks->count; i++) {
- if (i >= LWIS_MAX_CLOCK_NUM) {
- dev_err(lwis_dev->dev,
- "Clock count larger than LWIS_MAX_CLOCK_NUM\n");
- break;
- }
- strscpy(k_info.clks[i].name, lwis_dev->clocks->clk[i].name,
- LWIS_MAX_NAME_STRING_LEN);
- k_info.clks[i].clk_index = i;
- k_info.clks[i].frequency = 0;
- }
- }
-
- if (lwis_dev->type == DEVICE_TYPE_IOREG) {
- struct lwis_ioreg_device *ioreg_dev;
- ioreg_dev = container_of(lwis_dev, struct lwis_ioreg_device, base_dev);
- if (ioreg_dev->reg_list.count > 0) {
- k_info.num_regs = ioreg_dev->reg_list.count;
- for (i = 0; i < ioreg_dev->reg_list.count; i++) {
- if (i >= LWIS_MAX_REG_NUM) {
- dev_err(lwis_dev->dev,
- "Reg count larger than LWIS_MAX_REG_NUM\n");
- break;
- }
- strscpy(k_info.regs[i].name, ioreg_dev->reg_list.block[i].name,
- LWIS_MAX_NAME_STRING_LEN);
- k_info.regs[i].reg_index = i;
- k_info.regs[i].start = ioreg_dev->reg_list.block[i].start;
- k_info.regs[i].size = ioreg_dev->reg_list.block[i].size;
- }
- }
- }
-
- if (lwis_dev->transaction_worker_thread) {
- k_info.transaction_worker_thread_pid = lwis_dev->transaction_worker_thread->pid;
- }
-
- if (copy_to_user((void __user *)msg, &k_info, sizeof(k_info))) {
- dev_err(lwis_dev->dev, "Failed to copy device info to userspace\n");
- return -EFAULT;
- }
-
- return 0;
-}
-
static int register_read(struct lwis_device *lwis_dev, struct lwis_io_entry *read_entry,
struct lwis_io_entry *user_msg)
{
@@ -238,8 +82,8 @@ static int register_read(struct lwis_device *lwis_dev, struct lwis_io_entry *rea
/* Save the userspace buffer address */
user_buf = read_entry->rw_batch.buf;
/* Allocate read buffer */
- read_entry->rw_batch.buf =
- lwis_allocator_allocate(lwis_dev, read_entry->rw_batch.size_in_bytes);
+ read_entry->rw_batch.buf = lwis_allocator_allocate(
+ lwis_dev, read_entry->rw_batch.size_in_bytes, GFP_KERNEL);
if (!read_entry->rw_batch.buf) {
dev_err_ratelimited(lwis_dev->dev,
"Failed to allocate register read buffer\n");
@@ -294,8 +138,8 @@ static int register_write(struct lwis_device *lwis_dev, struct lwis_io_entry *wr
/* Save the userspace buffer address */
user_buf = write_entry->rw_batch.buf;
/* Allocate write buffer and copy contents from userspace */
- write_entry->rw_batch.buf =
- lwis_allocator_allocate(lwis_dev, write_entry->rw_batch.size_in_bytes);
+ write_entry->rw_batch.buf = lwis_allocator_allocate(
+ lwis_dev, write_entry->rw_batch.size_in_bytes, GFP_KERNEL);
if (!write_entry->rw_batch.buf) {
dev_err_ratelimited(lwis_dev->dev,
"Failed to allocate register write buffer\n");
@@ -340,51 +184,13 @@ static int register_modify(struct lwis_device *lwis_dev, struct lwis_io_entry *m
return ret;
}
-static int copy_io_entries(struct lwis_device *lwis_dev, struct lwis_io_entries *user_msg,
- struct lwis_io_entries *k_msg, struct lwis_io_entry **k_entries)
-{
- int ret = 0;
- struct lwis_io_entry *io_entries;
- uint32_t buf_size;
-
- /* Register io is not supported for the lwis device, return */
- if (!lwis_dev->vops.register_io) {
- dev_err(lwis_dev->dev, "Register IO not supported on this LWIS device\n");
- return -EINVAL;
- }
-
- /* Copy io_entries from userspace */
- if (copy_from_user(k_msg, (void __user *)user_msg, sizeof(*k_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy io_entries header from userspace.\n");
- return -EFAULT;
- }
- buf_size = sizeof(struct lwis_io_entry) * k_msg->num_io_entries;
- if (buf_size / sizeof(struct lwis_io_entry) != k_msg->num_io_entries) {
- dev_err(lwis_dev->dev, "Failed to copy io_entries due to integer overflow.\n");
- return -EOVERFLOW;
- }
- io_entries = lwis_allocator_allocate(lwis_dev, buf_size);
- if (!io_entries) {
- dev_err(lwis_dev->dev, "Failed to allocate io_entries buffer\n");
- return -ENOMEM;
- }
- if (copy_from_user(io_entries, (void __user *)k_msg->io_entries, buf_size)) {
- ret = -EFAULT;
- lwis_allocator_free(lwis_dev, io_entries);
- dev_err(lwis_dev->dev, "Failed to copy io_entries from userspace.\n");
- return ret;
- }
- *k_entries = io_entries;
-
- return 0;
-}
-
-int lwis_ioctl_util_synchronous_process_io_entries(struct lwis_device *lwis_dev, int num_io_entries,
- struct lwis_io_entry *io_entries,
- struct lwis_io_entry *user_msg)
+static int synchronous_process_io_entries(struct lwis_device *lwis_dev, int num_io_entries,
+ struct lwis_io_entry *io_entries,
+ struct lwis_io_entry *user_msg)
{
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) {
@@ -406,7 +212,13 @@ int lwis_ioctl_util_synchronous_process_io_entries(struct lwis_device *lwis_dev,
ret = register_write(lwis_dev, &io_entries[i]);
break;
case LWIS_IO_ENTRY_POLL:
- ret = lwis_io_entry_poll(lwis_dev, &io_entries[i]);
+ ret = lwis_io_entry_poll(lwis_dev, &io_entries[i], /*is_short=*/false);
+ break;
+ case LWIS_IO_ENTRY_POLL_SHORT:
+ ret = lwis_io_entry_poll(lwis_dev, &io_entries[i], /*is_short=*/true);
+ break;
+ case LWIS_IO_ENTRY_WAIT:
+ ret = lwis_io_entry_wait(lwis_dev, &io_entries[i]);
break;
case LWIS_IO_ENTRY_READ_ASSERT:
ret = lwis_io_entry_read_assert(lwis_dev, &io_entries[i]);
@@ -428,211 +240,226 @@ exit:
/*use_read_barrier=*/true,
/*use_write_barrier=*/false);
}
+ lwis_i2c_bus_manager_unlock_i2c_bus(lwis_dev);
return ret;
}
-static int ioctl_reg_io(struct lwis_device *lwis_dev, struct lwis_io_entries *user_msg)
+static int construct_io_entry(struct lwis_client *client, struct lwis_io_entry *user_entries,
+ size_t num_io_entries, struct lwis_io_entry **io_entries)
{
+ int i;
int ret = 0;
- struct lwis_io_entries k_msg;
- struct lwis_io_entry *k_entries = NULL;
-
- ret = copy_io_entries(lwis_dev, user_msg, &k_msg, &k_entries);
- if (ret) {
- goto reg_io_exit;
- }
-
- /* Walk through and execute the entries */
- ret = lwis_ioctl_util_synchronous_process_io_entries(lwis_dev, k_msg.num_io_entries,
- k_entries, k_msg.io_entries);
+ int last_buf_alloc_idx = -1;
+ size_t entry_size;
+ struct lwis_io_entry *k_entries;
+ uint8_t *user_buf;
+ uint8_t *k_buf;
+ struct lwis_device *lwis_dev = client->lwis_dev;
-reg_io_exit:
- if (k_entries) {
- lwis_allocator_free(lwis_dev, k_entries);
+ entry_size = num_io_entries * sizeof(struct lwis_io_entry);
+ if (entry_size / sizeof(struct lwis_io_entry) != num_io_entries) {
+ dev_err(lwis_dev->dev, "Failed to prepare io entries due to integer overflow\n");
+ return -EOVERFLOW;
}
- return ret;
-}
-
-static int ioctl_buffer_alloc(struct lwis_client *lwis_client,
- struct lwis_alloc_buffer_info __user *msg)
-{
- unsigned long ret = 0;
- struct lwis_alloc_buffer_info alloc_info;
- struct lwis_allocated_buffer *buffer;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- buffer = kmalloc(sizeof(*buffer), GFP_KERNEL);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Failed to allocated lwis_allocated_buffer\n");
+ k_entries = lwis_allocator_allocate(lwis_dev, entry_size, GFP_KERNEL);
+ if (!k_entries) {
+ dev_err(lwis_dev->dev, "Failed to allocate io entries\n");
return -ENOMEM;
}
- if (copy_from_user((void *)&alloc_info, (void __user *)msg, sizeof(alloc_info))) {
+ if (copy_from_user((void *)k_entries, (void __user *)user_entries, entry_size)) {
ret = -EFAULT;
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(alloc_info));
- goto error_alloc;
- }
-
- ret = lwis_buffer_alloc(lwis_client, &alloc_info, buffer);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to allocate buffer\n");
- goto error_alloc;
+ dev_err(lwis_dev->dev, "Failed to copy io entries from user\n");
+ goto error_free_entries;
}
- if (copy_to_user((void __user *)msg, (void *)&alloc_info, sizeof(alloc_info))) {
- ret = -EFAULT;
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes to user\n", sizeof(alloc_info));
- lwis_buffer_free(lwis_client, buffer);
- goto error_alloc;
+ /*
+ * For batch writes, need to allocate kernel buffers to deep copy the
+ * write values. Don't need to do this for batch reads because memory
+ * will be allocated in the form of lwis_io_result in io processing.
+ */
+ for (i = 0; i < num_io_entries; ++i) {
+ if (k_entries[i].type == LWIS_IO_ENTRY_WRITE_BATCH) {
+ user_buf = k_entries[i].rw_batch.buf;
+ k_buf = lwis_allocator_allocate(
+ lwis_dev, k_entries[i].rw_batch.size_in_bytes, GFP_KERNEL);
+ if (!k_buf) {
+ dev_err_ratelimited(lwis_dev->dev,
+ "Failed to allocate io write buffer\n");
+ ret = -ENOMEM;
+ goto error_free_buf;
+ }
+ last_buf_alloc_idx = i;
+ k_entries[i].rw_batch.buf = k_buf;
+ if (copy_from_user(k_buf, (void __user *)user_buf,
+ k_entries[i].rw_batch.size_in_bytes)) {
+ ret = -EFAULT;
+ dev_err_ratelimited(
+ lwis_dev->dev,
+ "Failed to copy io write buffer from userspace\n");
+ goto error_free_buf;
+ }
+ }
}
+ *io_entries = k_entries;
return 0;
-error_alloc:
- kfree(buffer);
+error_free_buf:
+ for (i = 0; i <= last_buf_alloc_idx; ++i) {
+ if (k_entries[i].type == LWIS_IO_ENTRY_WRITE_BATCH) {
+ lwis_allocator_free(lwis_dev, k_entries[i].rw_batch.buf);
+ k_entries[i].rw_batch.buf = NULL;
+ }
+ }
+error_free_entries:
+ lwis_allocator_free(lwis_dev, k_entries);
+ *io_entries = NULL;
return ret;
}
-static int ioctl_buffer_free(struct lwis_client *lwis_client, int __user *msg)
+static int copy_pkt_to_user(struct lwis_device *lwis_dev, void __user *u_msg, void *k_msg,
+ size_t size)
{
- int ret = 0;
- int fd;
- struct lwis_allocated_buffer *buffer;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (copy_from_user((void *)&fd, (void __user *)msg, sizeof(fd))) {
- dev_err(lwis_dev->dev, "Failed to copy file descriptor from user\n");
+ if (copy_to_user(u_msg, k_msg, size)) {
+ dev_err(lwis_dev->dev, "Failed to copy %zu bytes to user\n", size);
return -EFAULT;
}
- buffer = lwis_client_allocated_buffer_find(lwis_client, fd);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Cannot find allocated buffer FD %d\n", fd);
- return -ENOENT;
- }
-
- ret = lwis_buffer_free(lwis_client, buffer);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to free buffer FD %d\n", fd);
- return ret;
- }
-
- kfree(buffer);
-
return 0;
}
-static int ioctl_buffer_enroll(struct lwis_client *lwis_client, struct lwis_buffer_info __user *msg)
+static int cmd_echo(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_echo __user *u_msg)
{
- unsigned long ret = 0;
- struct lwis_enrolled_buffer *buffer;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+ struct lwis_cmd_echo echo_msg;
+ char *buffer = NULL;
- buffer = kmalloc(sizeof(struct lwis_enrolled_buffer), GFP_KERNEL);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Failed to allocate lwis_enrolled_buffer struct\n");
- return -ENOMEM;
+ if (copy_from_user((void *)&echo_msg, (void __user *)u_msg, sizeof(echo_msg))) {
+ dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(echo_msg));
+ return -EFAULT;
}
- if (copy_from_user((void *)&buffer->info, (void __user *)msg, sizeof(buffer->info))) {
- ret = -EFAULT;
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n",
- sizeof(buffer->info));
- goto error_enroll;
+ if (echo_msg.msg.size == 0) {
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
- ret = lwis_buffer_enroll(lwis_client, buffer);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to enroll buffer\n");
- goto error_enroll;
+ buffer = kmalloc(echo_msg.msg.size + 1, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(lwis_dev->dev, "Failed to allocate buffer for echo message\n");
+ header->ret_code = -ENOMEM;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-
- if (copy_to_user((void __user *)msg, (void *)&buffer->info, sizeof(buffer->info))) {
- ret = -EFAULT;
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes to user\n", sizeof(buffer->info));
- lwis_buffer_disenroll(lwis_client, buffer);
- goto error_enroll;
+ if (copy_from_user(buffer, (void __user *)echo_msg.msg.msg, echo_msg.msg.size)) {
+ dev_err(lwis_dev->dev, "Failed to copy %zu bytes echo message from user\n",
+ echo_msg.msg.size);
+ kfree(buffer);
+ header->ret_code = -EFAULT;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
+ buffer[echo_msg.msg.size] = '\0';
- return 0;
-
-error_enroll:
+ if (echo_msg.msg.kernel_log) {
+ dev_info(lwis_dev->dev, "LWIS_ECHO: %s\n", buffer);
+ }
kfree(buffer);
- return ret;
+
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_buffer_disenroll(struct lwis_client *lwis_client,
- struct lwis_enrolled_buffer_info __user *msg)
+static int cmd_time_query(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_time_query __user *u_msg)
{
- unsigned long ret = 0;
- struct lwis_enrolled_buffer_info info;
- struct lwis_enrolled_buffer *buffer;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
-
- if (copy_from_user((void *)&info, (void __user *)msg, sizeof(info))) {
- dev_err(lwis_dev->dev, "Failed to copy DMA virtual address from user\n");
- return -EFAULT;
- }
-
- buffer = lwis_client_enrolled_buffer_find(lwis_client, info.fd, info.dma_vaddr);
-
- if (!buffer) {
- dev_err(lwis_dev->dev, "Failed to find dma buffer for fd %d vaddr %pad\n", info.fd,
- &info.dma_vaddr);
- return -ENOENT;
- }
+ struct lwis_cmd_time_query time_query;
+ time_query.timestamp_ns = ktime_to_ns(lwis_get_time());
+ time_query.header.cmd_id = header->cmd_id;
+ time_query.header.next = header->next;
+ time_query.header.ret_code = 0;
- ret = lwis_buffer_disenroll(lwis_client, buffer);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to disenroll dma buffer for fd %d vaddr %pad\n",
- info.fd, &info.dma_vaddr);
- return ret;
- }
-
- kfree(buffer);
-
- return 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&time_query, sizeof(time_query));
}
-static int ioctl_buffer_cpu_access(struct lwis_client *lwis_client,
- struct lwis_buffer_cpu_access_op __user *msg)
+static int cmd_get_device_info(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_device_info __user *u_msg)
{
- int ret = 0;
- struct lwis_buffer_cpu_access_op op;
- struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+ int i;
+ struct lwis_cmd_device_info k_info = { .header.cmd_id = header->cmd_id,
+ .header.next = header->next,
+ .info.id = lwis_dev->id,
+ .info.type = lwis_dev->type,
+ .info.num_clks = 0,
+ .info.num_regs = 0,
+ .info.transaction_worker_thread_pid = -1,
+ .info.periodic_io_thread_pid = -1 };
+ strscpy(k_info.info.name, lwis_dev->name, LWIS_MAX_NAME_STRING_LEN);
- if (copy_from_user((void *)&op, (void __user *)msg, sizeof(op))) {
- dev_err(lwis_dev->dev, "Failed to copy buffer CPU access operation from user\n");
- return -EFAULT;
+ if (lwis_dev->clocks) {
+ k_info.info.num_clks = lwis_dev->clocks->count;
+ for (i = 0; i < lwis_dev->clocks->count; i++) {
+ if (i >= LWIS_MAX_CLOCK_NUM) {
+ dev_err(lwis_dev->dev,
+ "Clock count larger than LWIS_MAX_CLOCK_NUM\n");
+ break;
+ }
+ strscpy(k_info.info.clks[i].name, lwis_dev->clocks->clk[i].name,
+ LWIS_MAX_NAME_STRING_LEN);
+ k_info.info.clks[i].clk_index = i;
+ k_info.info.clks[i].frequency = 0;
+ }
}
- ret = lwis_buffer_cpu_access(lwis_client, &op);
- if (ret) {
- dev_err(lwis_dev->dev, "Failed to prepare for cpu access for fd %d\n", op.fd);
- return ret;
+ if (lwis_dev->type == DEVICE_TYPE_IOREG) {
+ struct lwis_ioreg_device *ioreg_dev;
+ ioreg_dev = container_of(lwis_dev, struct lwis_ioreg_device, base_dev);
+ if (ioreg_dev->reg_list.count > 0) {
+ k_info.info.num_regs = ioreg_dev->reg_list.count;
+ for (i = 0; i < ioreg_dev->reg_list.count; i++) {
+ if (i >= LWIS_MAX_REG_NUM) {
+ dev_err(lwis_dev->dev,
+ "Reg count larger than LWIS_MAX_REG_NUM\n");
+ break;
+ }
+ strscpy(k_info.info.regs[i].name, ioreg_dev->reg_list.block[i].name,
+ LWIS_MAX_NAME_STRING_LEN);
+ k_info.info.regs[i].reg_index = i;
+ k_info.info.regs[i].start = ioreg_dev->reg_list.block[i].start;
+ k_info.info.regs[i].size = ioreg_dev->reg_list.block[i].size;
+ }
+ }
}
- return 0;
+ if (lwis_dev->transaction_worker_thread) {
+ k_info.info.transaction_worker_thread_pid =
+ lwis_dev->transaction_worker_thread->pid;
+ }
+
+ k_info.header.ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_info, sizeof(k_info));
}
-static int ioctl_device_enable(struct lwis_client *lwis_client)
+static int cmd_device_enable(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *u_msg)
{
int ret = 0;
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
if (lwis_client->is_enabled) {
- return ret;
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
+
mutex_lock(&lwis_dev->client_lock);
if (lwis_dev->enabled > 0 && lwis_dev->enabled < INT_MAX) {
lwis_dev->enabled++;
lwis_client->is_enabled = true;
- mutex_unlock(&lwis_dev->client_lock);
- return 0;
+ ret = 0;
+ goto exit_locked;
} else if (lwis_dev->enabled == INT_MAX) {
dev_err(lwis_dev->dev, "Enable counter overflow\n");
ret = -EINVAL;
- goto error_locked;
+ goto exit_locked;
}
/* Clear event queues to make sure there is no stale event from
@@ -643,24 +470,28 @@ static int ioctl_device_enable(struct lwis_client *lwis_client)
ret = lwis_dev_power_up_locked(lwis_dev);
if (ret < 0) {
dev_err(lwis_dev->dev, "Failed to power up device\n");
- goto error_locked;
+ goto exit_locked;
}
lwis_dev->enabled++;
lwis_client->is_enabled = true;
+ lwis_dev->is_suspended = false;
dev_info(lwis_dev->dev, "Device enabled\n");
-error_locked:
+exit_locked:
mutex_unlock(&lwis_dev->client_lock);
- return ret;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_device_disable(struct lwis_client *lwis_client)
+static int cmd_device_disable(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *u_msg)
{
int ret = 0;
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
if (!lwis_client->is_enabled) {
- return ret;
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
mutex_lock(&lwis_dev->client_lock);
@@ -687,73 +518,81 @@ static int ioctl_device_disable(struct lwis_client *lwis_client)
if (lwis_dev->enabled > 1) {
lwis_dev->enabled--;
lwis_client->is_enabled = false;
- mutex_unlock(&lwis_dev->client_lock);
- return 0;
+ ret = 0;
+ goto exit_locked;
} else if (lwis_dev->enabled <= 0) {
dev_err(lwis_dev->dev, "Disabling a device that is already disabled\n");
ret = -EINVAL;
- goto error_locked;
+ goto exit_locked;
}
ret = lwis_dev_power_down_locked(lwis_dev);
if (ret < 0) {
dev_err(lwis_dev->dev, "Failed to power down device\n");
- goto error_locked;
+ goto exit_locked;
}
lwis_device_event_states_clear_locked(lwis_dev);
lwis_dev->enabled--;
lwis_client->is_enabled = false;
+ lwis_dev->is_suspended = false;
dev_info(lwis_dev->dev, "Device disabled\n");
-error_locked:
+exit_locked:
mutex_unlock(&lwis_dev->client_lock);
- return ret;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_echo(struct lwis_device *lwis_dev, struct lwis_echo __user *msg)
+static int copy_io_entries_from_cmd(struct lwis_device *lwis_dev,
+ struct lwis_cmd_io_entries __user *u_msg,
+ struct lwis_cmd_io_entries *k_msg,
+ struct lwis_io_entry **k_entries)
{
- struct lwis_echo echo_msg;
- char *buffer;
+ struct lwis_io_entry *io_entries;
+ uint32_t buf_size;
- if (copy_from_user((void *)&echo_msg, (void __user *)msg, sizeof(echo_msg))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(echo_msg));
- return -EFAULT;
+ /* Register io is not supported for the lwis device, return */
+ if (!lwis_dev->vops.register_io) {
+ dev_err(lwis_dev->dev, "Register IO not supported on this LWIS device\n");
+ return -EINVAL;
}
- if (echo_msg.size == 0) {
- return 0;
+ /* Copy io_entries from userspace */
+ if (copy_from_user(k_msg, (void __user *)u_msg, sizeof(*k_msg))) {
+ dev_err(lwis_dev->dev, "Failed to copy io_entries header from userspace.\n");
+ return -EFAULT;
}
-
- buffer = kmalloc(echo_msg.size + 1, GFP_KERNEL);
- if (!buffer) {
- dev_err(lwis_dev->dev, "Failed to allocate buffer for echo message\n");
+ buf_size = sizeof(struct lwis_io_entry) * k_msg->io.num_io_entries;
+ if (buf_size / sizeof(struct lwis_io_entry) != k_msg->io.num_io_entries) {
+ dev_err(lwis_dev->dev, "Failed to copy io_entries due to integer overflow.\n");
+ return -EOVERFLOW;
+ }
+ io_entries = lwis_allocator_allocate(lwis_dev, buf_size, GFP_KERNEL);
+ if (!io_entries) {
+ dev_err(lwis_dev->dev, "Failed to allocate io_entries buffer\n");
return -ENOMEM;
}
- if (copy_from_user(buffer, (void __user *)echo_msg.msg, echo_msg.size)) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes echo message from user\n",
- echo_msg.size);
- kfree(buffer);
+ if (copy_from_user(io_entries, (void __user *)k_msg->io.io_entries, buf_size)) {
+ dev_err(lwis_dev->dev, "Failed to copy io_entries from userspace.\n");
+ lwis_allocator_free(lwis_dev, io_entries);
return -EFAULT;
}
- buffer[echo_msg.size] = '\0';
+ *k_entries = io_entries;
- if (echo_msg.kernel_log) {
- dev_info(lwis_dev->dev, "LWIS_ECHO: %s\n", buffer);
- }
- kfree(buffer);
return 0;
}
-static int ioctl_device_reset(struct lwis_client *lwis_client, struct lwis_io_entries *user_msg)
+static int cmd_device_reset(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_io_entries __user *u_msg)
{
int ret = 0;
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
- struct lwis_io_entries k_msg;
+ struct lwis_cmd_io_entries k_msg;
struct lwis_io_entry *k_entries = NULL;
unsigned long flags;
bool device_enabled = false;
- ret = copy_io_entries(lwis_dev, user_msg, &k_msg, &k_entries);
+ ret = copy_io_entries_from_cmd(lwis_dev, u_msg, &k_msg, &k_entries);
if (ret) {
goto soft_reset_exit;
}
@@ -780,8 +619,8 @@ static int ioctl_device_reset(struct lwis_client *lwis_client, struct lwis_io_en
/* Perform reset routine defined by the io_entries */
if (device_enabled) {
- ret = lwis_ioctl_util_synchronous_process_io_entries(lwis_dev, k_msg.num_io_entries,
- k_entries, k_msg.io_entries);
+ ret = synchronous_process_io_entries(lwis_dev, k_msg.io.num_io_entries, k_entries,
+ k_msg.io.io_entries);
} else {
dev_warn(lwis_dev->dev,
"Device is not enabled, IoEntries will not be executed in DEVICE_RESET\n");
@@ -794,107 +633,410 @@ soft_reset_exit:
if (k_entries) {
lwis_allocator_free(lwis_dev, k_entries);
}
- return ret;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_dump_debug_state(struct lwis_client *lwis_client)
+static int cmd_device_suspend(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *u_msg)
+{
+ int ret = 0;
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+
+ if (!lwis_dev->suspend_sequence) {
+ dev_err(lwis_dev->dev, "No suspend sequence defined\n");
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ if (!lwis_client->is_enabled) {
+ dev_err(lwis_dev->dev, "Trying to suspend a disabled device\n");
+ header->ret_code = -EINVAL;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ if (lwis_dev->is_suspended) {
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ mutex_lock(&lwis_dev->client_lock);
+ /* Clear event states for this client */
+ lwis_client_event_states_clear(lwis_client);
+ mutex_unlock(&lwis_dev->client_lock);
+
+ /* Flush all periodic io to complete */
+ ret = lwis_periodic_io_client_flush(lwis_client);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Failed to wait for in-process periodic io to complete\n");
+ }
+
+ /* Flush all pending transactions */
+ ret = lwis_transaction_client_flush(lwis_client);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Failed to flush pending transactions\n");
+ }
+
+ /* Run cleanup transactions. */
+ lwis_transaction_client_cleanup(lwis_client);
+
+ mutex_lock(&lwis_dev->client_lock);
+ ret = lwis_dev_process_power_sequence(lwis_dev, lwis_dev->suspend_sequence,
+ /*set_active=*/false, /*skip_error=*/false);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Error lwis_dev_process_power_sequence (%d)\n", ret);
+ goto exit_locked;
+ }
+
+ lwis_device_event_states_clear_locked(lwis_dev);
+
+ lwis_dev->is_suspended = true;
+ dev_info(lwis_dev->dev, "Device suspended\n");
+exit_locked:
+ mutex_unlock(&lwis_dev->client_lock);
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_device_resume(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *u_msg)
+{
+ int ret = 0;
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+
+ if (!lwis_dev->resume_sequence) {
+ dev_err(lwis_dev->dev, "No resume sequence defined\n");
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ if (!lwis_dev->is_suspended) {
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ mutex_lock(&lwis_dev->client_lock);
+ /* Clear event queues to make sure there is no stale event from
+ * previous session */
+ lwis_client_event_queue_clear(lwis_client);
+ lwis_client_error_event_queue_clear(lwis_client);
+
+ ret = lwis_dev_process_power_sequence(lwis_dev, lwis_dev->resume_sequence,
+ /*set_active=*/true, /*skip_error=*/false);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Error lwis_dev_process_power_sequence (%d)\n", ret);
+ goto exit_locked;
+ }
+
+ lwis_dev->is_suspended = false;
+ dev_info(lwis_dev->dev, "Device resumed\n");
+exit_locked:
+ mutex_unlock(&lwis_dev->client_lock);
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_dump_debug_state(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *u_msg)
{
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
mutex_lock(&lwis_dev->client_lock);
/* Dump lwis device crash info */
- lwis_device_crash_info_dump(lwis_dev);
+ lwis_debug_crash_info_dump(lwis_dev);
mutex_unlock(&lwis_dev->client_lock);
- return 0;
+ header->ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_event_control_get(struct lwis_client *lwis_client,
- struct lwis_event_control __user *msg)
+static int cmd_dma_buffer_enroll(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dma_buffer_enroll __user *u_msg)
{
- unsigned long ret = 0;
- struct lwis_event_control control;
+ int ret = 0;
+ struct lwis_cmd_dma_buffer_enroll buf_info;
+ struct lwis_enrolled_buffer *buffer;
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
- if (copy_from_user((void *)&control, (void __user *)msg, sizeof(control))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(control));
+ buffer = kmalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer) {
+ dev_err(lwis_dev->dev, "Failed to allocate lwis_enrolled_buffer struct\n");
+ header->ret_code = -ENOMEM;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ if (copy_from_user((void *)&buf_info, (void __user *)u_msg, sizeof(buf_info))) {
+ dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(buf_info));
+ ret = -EFAULT;
+ goto error_enroll;
+ }
+
+ buffer->info.fd = buf_info.info.fd;
+ buffer->info.dma_read = buf_info.info.dma_read;
+ buffer->info.dma_write = buf_info.info.dma_write;
+
+ ret = lwis_buffer_enroll(lwis_client, buffer);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Failed to enroll buffer\n");
+ goto error_enroll;
+ }
+
+ buf_info.info.dma_vaddr = buffer->info.dma_vaddr;
+ buf_info.header.cmd_id = header->cmd_id;
+ buf_info.header.next = header->next;
+ buf_info.header.ret_code = ret;
+ ret = copy_pkt_to_user(lwis_dev, u_msg, (void *)&buf_info, sizeof(buf_info));
+ if (ret) {
+ lwis_buffer_disenroll(lwis_client, buffer);
+ goto error_enroll;
+ }
+
+ return ret;
+
+error_enroll:
+ kfree(buffer);
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_dma_buffer_disenroll(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dma_buffer_disenroll __user *u_msg)
+{
+ int ret = 0;
+ struct lwis_cmd_dma_buffer_disenroll info;
+ struct lwis_enrolled_buffer *buffer;
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+
+ if (copy_from_user((void *)&info, (void __user *)u_msg, sizeof(info))) {
+ dev_err(lwis_dev->dev, "Failed to copy DMA virtual address from user\n");
return -EFAULT;
}
- ret = lwis_client_event_control_get(lwis_client, control.event_id, &control);
+ buffer = lwis_client_enrolled_buffer_find(lwis_client, info.info.fd, info.info.dma_vaddr);
+ if (!buffer) {
+ dev_err(lwis_dev->dev, "Failed to find dma buffer for fd %d vaddr %pad\n",
+ info.info.fd, &info.info.dma_vaddr);
+ header->ret_code = -ENOENT;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+ ret = lwis_buffer_disenroll(lwis_client, buffer);
if (ret) {
- dev_err(lwis_dev->dev, "Failed to get event: %lld (err:%ld)\n", control.event_id,
- ret);
- return -EINVAL;
+ dev_err(lwis_dev->dev, "Failed to disenroll dma buffer for fd %d vaddr %pad\n",
+ info.info.fd, &info.info.dma_vaddr);
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
- if (copy_to_user((void __user *)msg, (void *)&control, sizeof(control))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes to user\n", sizeof(control));
+ kfree(buffer);
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_dma_buffer_cpu_access(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dma_buffer_cpu_access __user *u_msg)
+{
+ int ret = 0;
+ struct lwis_cmd_dma_buffer_cpu_access op;
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+
+ if (copy_from_user((void *)&op, (void __user *)u_msg, sizeof(op))) {
+ dev_err(lwis_dev->dev, "Failed to copy buffer CPU access operation from user\n");
return -EFAULT;
}
- return 0;
+ ret = lwis_buffer_cpu_access(lwis_client, &op.op);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Failed to prepare for cpu access for fd %d\n", op.op.fd);
+ }
+
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_dma_buffer_alloc(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dma_buffer_alloc __user *u_msg)
+{
+ int ret = 0;
+ struct lwis_cmd_dma_buffer_alloc alloc_info;
+ struct lwis_allocated_buffer *buffer;
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+
+ buffer = kmalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer) {
+ dev_err(lwis_dev->dev, "Failed to allocated lwis_allocated_buffer\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user((void *)&alloc_info, (void __user *)u_msg, sizeof(alloc_info))) {
+ dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(alloc_info));
+ ret = -EFAULT;
+ goto error_alloc;
+ }
+
+ ret = lwis_buffer_alloc(lwis_client, &alloc_info.info, buffer);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Failed to allocate buffer\n");
+ goto error_alloc;
+ }
+
+ alloc_info.header.ret_code = 0;
+ ret = copy_pkt_to_user(lwis_dev, u_msg, (void *)&alloc_info, sizeof(alloc_info));
+ if (ret) {
+ lwis_buffer_free(lwis_client, buffer);
+ ret = -EFAULT;
+ goto error_alloc;
+ }
+
+ return ret;
+
+error_alloc:
+ kfree(buffer);
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_event_control_set(struct lwis_client *lwis_client,
- struct lwis_event_control_list __user *msg)
+static int cmd_dma_buffer_free(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dma_buffer_free __user *u_msg)
{
- struct lwis_event_control_list k_msg;
+ int ret = 0;
+ struct lwis_cmd_dma_buffer_free info;
+ struct lwis_allocated_buffer *buffer;
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+
+ if (copy_from_user((void *)&info, (void __user *)u_msg, sizeof(info))) {
+ dev_err(lwis_dev->dev, "Failed to copy file descriptor from user\n");
+ return -EFAULT;
+ }
+
+ buffer = lwis_client_allocated_buffer_find(lwis_client, info.fd);
+ if (!buffer) {
+ dev_err(lwis_dev->dev, "Cannot find allocated buffer FD %d\n", info.fd);
+ header->ret_code = -ENOENT;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ ret = lwis_buffer_free(lwis_client, buffer);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Failed to free buffer FD %d\n", info.fd);
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ kfree(buffer);
+
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_reg_io(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_io_entries __user *u_msg)
+{
+ int ret = 0;
+ struct lwis_cmd_io_entries k_msg;
+ struct lwis_io_entry *k_entries = NULL;
+
+ ret = copy_io_entries_from_cmd(lwis_dev, u_msg, &k_msg, &k_entries);
+ if (ret) {
+ goto reg_io_exit;
+ }
+
+ /* Walk through and execute the entries */
+ ret = synchronous_process_io_entries(lwis_dev, k_msg.io.num_io_entries, k_entries,
+ k_msg.io.io_entries);
+
+reg_io_exit:
+ if (k_entries) {
+ lwis_allocator_free(lwis_dev, k_entries);
+ }
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_event_control_get(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_event_control_get __user *u_msg)
+{
+ int ret = 0;
+ struct lwis_cmd_event_control_get control;
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+
+ if (copy_from_user((void *)&control, (void __user *)u_msg, sizeof(control))) {
+ dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(control));
+ return -EFAULT;
+ }
+
+ ret = lwis_client_event_control_get(lwis_client, control.ctl.event_id, &control.ctl);
+ if (ret) {
+ dev_err(lwis_dev->dev, "Failed to get event: %lld (err:%d)\n", control.ctl.event_id,
+ ret);
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ control.header.ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&control, sizeof(control));
+}
+
+static int cmd_event_control_set(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_event_control_set __user *u_msg)
+{
+ struct lwis_cmd_event_control_set k_msg;
struct lwis_event_control *k_event_controls;
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
int ret = 0;
int i;
size_t buf_size;
- if (copy_from_user((void *)&k_msg, (void __user *)msg,
- sizeof(struct lwis_event_control_list))) {
- ret = -EFAULT;
+ if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
dev_err(lwis_dev->dev, "Failed to copy ioctl message from user\n");
- return ret;
+ return -EFAULT;
}
/* Copy event controls from user buffer. */
- buf_size = sizeof(struct lwis_event_control) * k_msg.num_event_controls;
- if (buf_size / sizeof(struct lwis_event_control) != k_msg.num_event_controls) {
+ buf_size = sizeof(struct lwis_event_control) * k_msg.list.num_event_controls;
+ if (buf_size / sizeof(struct lwis_event_control) != k_msg.list.num_event_controls) {
dev_err(lwis_dev->dev, "Failed to copy event controls due to integer overflow.\n");
- return -EOVERFLOW;
+ header->ret_code = -EOVERFLOW;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
k_event_controls = kmalloc(buf_size, GFP_KERNEL);
if (!k_event_controls) {
dev_err(lwis_dev->dev, "Failed to allocate event controls\n");
- return -ENOMEM;
+ header->ret_code = -ENOMEM;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
- if (copy_from_user(k_event_controls, (void __user *)k_msg.event_controls, buf_size)) {
- ret = -EFAULT;
+ if (copy_from_user(k_event_controls, (void __user *)k_msg.list.event_controls, buf_size)) {
dev_err(lwis_dev->dev, "Failed to copy event controls from user\n");
- goto out;
+ ret = -EFAULT;
+ goto exit;
}
- for (i = 0; i < k_msg.num_event_controls; i++) {
+ for (i = 0; i < k_msg.list.num_event_controls; i++) {
ret = lwis_client_event_control_set(lwis_client, &k_event_controls[i]);
if (ret) {
dev_err(lwis_dev->dev, "Failed to apply event control 0x%llx\n",
k_event_controls[i].event_id);
- goto out;
+ goto exit;
}
}
-out:
+exit:
kfree(k_event_controls);
- return ret;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_event_dequeue(struct lwis_client *lwis_client, struct lwis_event_info __user *msg)
+static int cmd_event_dequeue(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_event_dequeue __user *u_msg)
{
- unsigned long ret = 0;
- unsigned long err = 0;
- struct lwis_event_entry *event;
- struct lwis_event_info info_user;
+ struct lwis_cmd_event_dequeue info;
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+ struct lwis_event_entry *event;
+ int ret = 0;
+ int err = 0;
bool is_error_event = false;
- if (copy_from_user((void *)&info_user, (void __user *)msg, sizeof(info_user))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(info_user));
+ if (copy_from_user((void *)&info, (void __user *)u_msg, sizeof(info))) {
+ dev_err(lwis_dev->dev, "Failed to copy %zu bytes from user\n", sizeof(info));
return -EFAULT;
}
@@ -904,45 +1046,39 @@ static int ioctl_event_dequeue(struct lwis_client *lwis_client, struct lwis_even
if (ret == 0) {
is_error_event = true;
} else if (ret != -ENOENT) {
- dev_err(lwis_dev->dev, "Error dequeueing error event: %ld\n", ret);
+ dev_err(lwis_dev->dev, "Error dequeueing error event: %d\n", ret);
mutex_unlock(&lwis_dev->client_lock);
- return ret;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
} else {
/* Nothing at error event queue, continue to check normal
* event queue */
ret = lwis_client_event_peek_front(lwis_client, &event);
if (ret) {
if (ret != -ENOENT) {
- dev_err(lwis_dev->dev, "Error dequeueing event: %ld\n", ret);
+ dev_err(lwis_dev->dev, "Error dequeueing event: %d\n", ret);
}
mutex_unlock(&lwis_dev->client_lock);
- return ret;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
}
/* We need to check if we have an adequate payload buffer */
- if (event->event_info.payload_size > info_user.payload_buffer_size) {
+ if (event->event_info.payload_size > info.info.payload_buffer_size) {
/* Nope, we don't. Let's inform the user and bail */
- info_user.payload_size = event->event_info.payload_size;
+ info.info.payload_size = event->event_info.payload_size;
err = -EAGAIN;
} else {
- /*
- * Let's save the IOCTL inputs because they'll get overwritten
- */
- size_t user_buffer_size = info_user.payload_buffer_size;
- void *user_buffer = info_user.payload_buffer;
-
- /* Copy over the rest of the info */
- memcpy(&info_user, &event->event_info, sizeof(info_user));
-
- /* Restore the IOCTL inputs */
- info_user.payload_buffer_size = user_buffer_size;
- info_user.payload_buffer = user_buffer;
+ info.info.event_id = event->event_info.event_id;
+ info.info.event_counter = event->event_info.event_counter;
+ info.info.timestamp_ns = event->event_info.timestamp_ns;
+ info.info.payload_size = event->event_info.payload_size;
/* Here we have a payload and the buffer is big enough */
- if (event->event_info.payload_size > 0 && info_user.payload_buffer) {
+ if (event->event_info.payload_size > 0 && info.info.payload_buffer) {
/* Copy over the payload buffer to userspace */
- if (copy_to_user((void __user *)info_user.payload_buffer,
+ if (copy_to_user((void __user *)info.info.payload_buffer,
(void *)event->event_info.payload_buffer,
event->event_info.payload_size)) {
dev_err(lwis_dev->dev, "Failed to copy %zu bytes to user\n",
@@ -964,134 +1100,101 @@ static int ioctl_event_dequeue(struct lwis_client *lwis_client, struct lwis_even
ret = lwis_client_event_pop_front(lwis_client, NULL);
}
if (ret) {
- dev_err(lwis_dev->dev, "Error dequeueing event: %ld\n", ret);
+ dev_err(lwis_dev->dev, "Error dequeueing event: %d\n", ret);
mutex_unlock(&lwis_dev->client_lock);
- return ret;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
}
mutex_unlock(&lwis_dev->client_lock);
/* Now let's copy the actual info struct back to user */
- if (copy_to_user((void __user *)msg, (void *)&info_user, sizeof(info_user))) {
- dev_err(lwis_dev->dev, "Failed to copy %zu bytes to user\n", sizeof(info_user));
- return -EFAULT;
- }
- return err;
-}
-
-static int ioctl_time_query(struct lwis_client *client, int64_t __user *msg)
-{
- int ret = 0;
- int64_t timestamp = ktime_to_ns(lwis_get_time());
-
- if (copy_to_user((void __user *)msg, &timestamp, sizeof(timestamp))) {
- ret = -EFAULT;
- dev_err(client->lwis_dev->dev, "Failed to copy timestamp to userspace\n");
- }
-
- return ret;
+ info.header.ret_code = err;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&info, sizeof(info));
}
-int lwis_ioctl_util_construct_io_entry(struct lwis_client *client,
- struct lwis_io_entry *user_entries, size_t num_io_entries,
- struct lwis_io_entry **io_entries)
+static int cmd_fake_event_inject(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *u_msg)
{
- int i;
int ret = 0;
- int last_buf_alloc_idx = -1;
- size_t entry_size;
- struct lwis_io_entry *k_entries;
- uint8_t *user_buf;
- uint8_t *k_buf;
- struct lwis_device *lwis_dev = client->lwis_dev;
-
- entry_size = num_io_entries * sizeof(struct lwis_io_entry);
- if (entry_size / sizeof(struct lwis_io_entry) != num_io_entries) {
- dev_err(lwis_dev->dev, "Failed to prepare io entries due to integer overflow\n");
- return -EOVERFLOW;
- }
- k_entries = lwis_allocator_allocate(lwis_dev, entry_size);
- if (!k_entries) {
- dev_err(lwis_dev->dev, "Failed to allocate io entries\n");
- return -ENOMEM;
- }
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+ struct lwis_interrupt_list *list = lwis_dev->irqs;
+ int rt_irq;
- if (copy_from_user((void *)k_entries, (void __user *)user_entries, entry_size)) {
- ret = -EFAULT;
- dev_err(lwis_dev->dev, "Failed to copy io entries from user\n");
- goto error_free_entries;
+ if (lwis_dev->type != DEVICE_TYPE_TEST || list->count != TEST_DEVICE_IRQ_CNT) {
+ return -EINVAL;
}
- /*
- * For batch writes, need to allocate kernel buffers to deep copy the
- * write values. Don't need to do this for batch reads because memory
- * will be allocated in the form of lwis_io_result in io processing.
- */
- for (i = 0; i < num_io_entries; ++i) {
- if (k_entries[i].type == LWIS_IO_ENTRY_WRITE_BATCH) {
- user_buf = k_entries[i].rw_batch.buf;
- k_buf = lwis_allocator_allocate(lwis_dev,
- k_entries[i].rw_batch.size_in_bytes);
- if (!k_buf) {
- dev_err_ratelimited(lwis_dev->dev,
- "Failed to allocate io write buffer\n");
- ret = -ENOMEM;
- goto error_free_buf;
- }
- last_buf_alloc_idx = i;
- k_entries[i].rw_batch.buf = k_buf;
- if (copy_from_user(k_buf, (void __user *)user_buf,
- k_entries[i].rw_batch.size_in_bytes)) {
- ret = -EFAULT;
- dev_err_ratelimited(
- lwis_dev->dev,
- "Failed to copy io write buffer from userspace\n");
- goto error_free_buf;
- }
- }
+ /* Fake Event Injection */
+ rt_irq = lwis_fake_event_inject(&list->irq[0]);
+ if (rt_irq != TEST_DEVICE_FAKE_INJECTION_IRQ) {
+ dev_err(lwis_dev->dev, "Error fake injection: rt_irq = %d, expect rt_irq = %d\n",
+ rt_irq, TEST_DEVICE_FAKE_INJECTION_IRQ);
+ ret = -1;
}
- *io_entries = k_entries;
- return 0;
-
-error_free_buf:
- for (i = 0; i <= last_buf_alloc_idx; ++i) {
- if (k_entries[i].type == LWIS_IO_ENTRY_WRITE_BATCH) {
- lwis_allocator_free(lwis_dev, k_entries[i].rw_batch.buf);
- k_entries[i].rw_batch.buf = NULL;
- }
- }
-error_free_entries:
- lwis_allocator_free(lwis_dev, k_entries);
- *io_entries = NULL;
- return ret;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int construct_transaction(struct lwis_client *client,
- struct lwis_transaction_info __user *msg,
- struct lwis_transaction **transaction)
+static int construct_transaction_from_cmd(struct lwis_client *client, uint32_t cmd_id,
+ struct lwis_cmd_pkt __user *u_msg,
+ struct lwis_transaction **transaction)
{
int ret;
+ struct lwis_cmd_transaction_info k_info_v1;
+ struct lwis_cmd_transaction_info_v2 k_info_v2;
struct lwis_transaction *k_transaction;
- struct lwis_transaction_info *user_transaction;
struct lwis_device *lwis_dev = client->lwis_dev;
- k_transaction = kmalloc(sizeof(struct lwis_transaction), GFP_KERNEL);
+ k_transaction = kmalloc(sizeof(*k_transaction), GFP_KERNEL);
if (!k_transaction) {
dev_err(lwis_dev->dev, "Failed to allocate transaction info\n");
return -ENOMEM;
}
- user_transaction = (struct lwis_transaction_info *)msg;
- if (copy_from_user((void *)&k_transaction->info, (void __user *)user_transaction,
- sizeof(struct lwis_transaction_info))) {
- ret = -EFAULT;
- dev_err(lwis_dev->dev, "Failed to copy transaction info from user\n");
+ if (cmd_id == LWIS_CMD_ID_TRANSACTION_SUBMIT_V2 ||
+ cmd_id == LWIS_CMD_ID_TRANSACTION_REPLACE_V2) {
+ if (copy_from_user((void *)&k_info_v2, (void __user *)u_msg, sizeof(k_info_v2))) {
+ dev_err(lwis_dev->dev, "Failed to copy transaction info from user\n");
+ ret = -EFAULT;
+ goto error_free_transaction;
+ }
+ memcpy(&k_transaction->info, &k_info_v2.info, sizeof(k_transaction->info));
+ } else if (cmd_id == LWIS_CMD_ID_TRANSACTION_SUBMIT ||
+ cmd_id == LWIS_CMD_ID_TRANSACTION_REPLACE) {
+ if (copy_from_user((void *)&k_info_v1, (void __user *)u_msg, sizeof(k_info_v1))) {
+ dev_err(lwis_dev->dev, "Failed to copy transaction info from user\n");
+ ret = -EFAULT;
+ goto error_free_transaction;
+ }
+ k_transaction->info.trigger_event_id = k_info_v1.info.trigger_event_id;
+ k_transaction->info.trigger_event_counter = k_info_v1.info.trigger_event_counter;
+ k_transaction->info.num_io_entries = k_info_v1.info.num_io_entries;
+ k_transaction->info.io_entries = k_info_v1.info.io_entries;
+ k_transaction->info.run_in_event_context = k_info_v1.info.run_in_event_context;
+ k_transaction->info.reserved = k_info_v1.info.reserved;
+ k_transaction->info.emit_success_event_id = k_info_v1.info.emit_success_event_id;
+ k_transaction->info.emit_error_event_id = k_info_v1.info.emit_error_event_id;
+ k_transaction->info.is_level_triggered = k_info_v1.info.is_level_triggered;
+ k_transaction->info.id = k_info_v1.info.id;
+ k_transaction->info.current_trigger_event_counter =
+ k_info_v1.info.current_trigger_event_counter;
+ k_transaction->info.submission_timestamp_ns =
+ k_info_v1.info.submission_timestamp_ns;
+
+ k_transaction->info.trigger_condition.num_nodes = 0;
+ k_transaction->info.trigger_condition.operator_type =
+ LWIS_TRIGGER_NODE_OPERATOR_INVALID;
+ k_transaction->info.completion_fence_fd = LWIS_NO_COMPLETION_FENCE;
+ } else {
+ dev_err(lwis_dev->dev, "Invalid command id for transaction\n");
+ ret = -EINVAL;
goto error_free_transaction;
}
- ret = lwis_ioctl_util_construct_io_entry(client, k_transaction->info.io_entries,
- k_transaction->info.num_io_entries,
- &k_transaction->info.io_entries);
+ ret = construct_io_entry(client, k_transaction->info.io_entries,
+ k_transaction->info.num_io_entries,
+ &k_transaction->info.io_entries);
if (ret) {
dev_err(lwis_dev->dev, "Failed to prepare lwis io entries for transaction\n");
goto error_free_transaction;
@@ -1099,6 +1202,8 @@ static int construct_transaction(struct lwis_client *client,
k_transaction->resp = NULL;
k_transaction->is_weak_transaction = false;
+ k_transaction->remaining_entries_to_process = k_transaction->info.num_io_entries;
+ k_transaction->starting_read_buf = NULL;
INIT_LIST_HEAD(&k_transaction->event_list_node);
INIT_LIST_HEAD(&k_transaction->process_queue_node);
INIT_LIST_HEAD(&k_transaction->completion_fence_list);
@@ -1111,119 +1216,180 @@ error_free_transaction:
return ret;
}
-static int ioctl_transaction_submit(struct lwis_client *client,
- struct lwis_transaction_info __user *msg)
+static int copy_transaction_info_v2_to_v1_locked(struct lwis_transaction_info_v2 *info_v2,
+ struct lwis_transaction_info *info_v1)
+{
+ if (!info_v2 || !info_v1) {
+ return -EINVAL;
+ }
+
+ info_v1->trigger_event_id = info_v2->trigger_event_id;
+ info_v1->trigger_event_counter = info_v2->trigger_event_counter;
+ info_v1->num_io_entries = info_v2->num_io_entries;
+ info_v1->io_entries = info_v2->io_entries;
+ info_v1->run_in_event_context = info_v2->run_in_event_context;
+ info_v1->reserved = info_v2->reserved;
+ info_v1->emit_success_event_id = info_v2->emit_success_event_id;
+ info_v1->emit_error_event_id = info_v2->emit_error_event_id;
+ info_v1->is_level_triggered = info_v2->is_level_triggered;
+ info_v1->id = info_v2->id;
+ info_v1->current_trigger_event_counter = info_v2->current_trigger_event_counter;
+ info_v1->submission_timestamp_ns = info_v2->submission_timestamp_ns;
+
+ return 0;
+}
+
+static int cmd_transaction_submit(struct lwis_client *client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *u_msg)
{
- int ret = 0;
- unsigned long flags;
struct lwis_transaction *k_transaction = NULL;
- struct lwis_transaction_info k_transaction_info;
+ struct lwis_cmd_transaction_info k_cmd_transaction_info_v1;
+ struct lwis_cmd_transaction_info_v2 k_cmd_transaction_info_v2;
+ struct lwis_cmd_pkt *resp_header = NULL;
struct lwis_device *lwis_dev = client->lwis_dev;
+ int ret = 0;
+ unsigned long flags;
- if (lwis_dev->type == DEVICE_TYPE_SLC) {
+ if (lwis_dev->type == DEVICE_TYPE_SLC || lwis_dev->type == DEVICE_TYPE_DPM) {
dev_err(lwis_dev->dev, "not supported device type: %d\n", lwis_dev->type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_exit;
}
- ret = construct_transaction(client, msg, &k_transaction);
+ ret = construct_transaction_from_cmd(client, header->cmd_id, u_msg, &k_transaction);
if (ret) {
- return ret;
+ goto err_exit;
}
ret = lwis_initialize_transaction_fences(client, k_transaction);
if (ret) {
- return ret;
+ lwis_transaction_free(lwis_dev, &k_transaction);
+ goto err_exit;
}
spin_lock_irqsave(&client->transaction_lock, flags);
ret = lwis_transaction_submit_locked(client, k_transaction);
- k_transaction_info = k_transaction->info;
+ if (header->cmd_id == LWIS_CMD_ID_TRANSACTION_SUBMIT_V2) {
+ resp_header = &k_cmd_transaction_info_v2.header;
+ k_cmd_transaction_info_v2.info = k_transaction->info;
+ } else if (header->cmd_id == LWIS_CMD_ID_TRANSACTION_SUBMIT) {
+ resp_header = &k_cmd_transaction_info_v1.header;
+ ret = copy_transaction_info_v2_to_v1_locked(&k_transaction->info,
+ &k_cmd_transaction_info_v1.info);
+ }
spin_unlock_irqrestore(&client->transaction_lock, flags);
-
if (ret) {
- k_transaction_info.id = LWIS_ID_INVALID;
- lwis_transaction_free(lwis_dev, k_transaction);
+ k_cmd_transaction_info_v1.info.id = LWIS_ID_INVALID;
+ k_cmd_transaction_info_v2.info.id = LWIS_ID_INVALID;
+ lwis_transaction_free(lwis_dev, &k_transaction);
}
- if (copy_to_user((void __user *)msg, &k_transaction_info,
- sizeof(struct lwis_transaction_info))) {
- ret = -EFAULT;
- dev_err_ratelimited(lwis_dev->dev,
- "Failed to copy transaction results to userspace\n");
+ resp_header->cmd_id = header->cmd_id;
+ resp_header->next = header->next;
+ resp_header->ret_code = ret;
+ if (header->cmd_id == LWIS_CMD_ID_TRANSACTION_SUBMIT_V2) {
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_cmd_transaction_info_v2,
+ sizeof(k_cmd_transaction_info_v2));
+ } else if (header->cmd_id == LWIS_CMD_ID_TRANSACTION_SUBMIT) {
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_cmd_transaction_info_v1,
+ sizeof(k_cmd_transaction_info_v1));
}
- return ret;
+ ret = -EINVAL;
+
+err_exit:
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_transaction_replace(struct lwis_client *client,
- struct lwis_transaction_info __user *msg)
+static int cmd_transaction_cancel(struct lwis_client *client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_transaction_cancel __user *u_msg)
{
int ret = 0;
- unsigned long flags;
+ struct lwis_cmd_transaction_cancel k_msg;
+ struct lwis_device *lwis_dev = client->lwis_dev;
+
+ if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
+ dev_err(lwis_dev->dev, "Failed to copy transaction ID from user\n");
+ return -EFAULT;
+ }
+
+ ret = lwis_transaction_cancel(client, k_msg.id);
+ if (ret) {
+ dev_info_ratelimited(
+ lwis_dev->dev,
+ "Transaction id 0x%llx does not exist or is already done, not available for cancel(%d)\n",
+ k_msg.id, ret);
+ }
+
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_transaction_replace(struct lwis_client *client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *u_msg)
+{
struct lwis_transaction *k_transaction = NULL;
- struct lwis_transaction_info k_transaction_info;
+ struct lwis_cmd_transaction_info k_cmd_transaction_info_v1;
+ struct lwis_cmd_transaction_info_v2 k_cmd_transaction_info_v2;
+ struct lwis_cmd_pkt *resp_header = NULL;
struct lwis_device *lwis_dev = client->lwis_dev;
+ int ret = 0;
+ unsigned long flags;
- ret = construct_transaction(client, msg, &k_transaction);
+ ret = construct_transaction_from_cmd(client, header->cmd_id, u_msg, &k_transaction);
if (ret) {
- return ret;
+ goto err_exit;
}
ret = lwis_initialize_transaction_fences(client, k_transaction);
if (ret) {
- return ret;
+ lwis_transaction_free(lwis_dev, &k_transaction);
+ goto err_exit;
}
spin_lock_irqsave(&client->transaction_lock, flags);
ret = lwis_transaction_replace_locked(client, k_transaction);
- k_transaction_info = k_transaction->info;
+ if (header->cmd_id == LWIS_CMD_ID_TRANSACTION_REPLACE_V2) {
+ resp_header = &k_cmd_transaction_info_v2.header;
+ k_cmd_transaction_info_v2.info = k_transaction->info;
+ } else if (header->cmd_id == LWIS_CMD_ID_TRANSACTION_REPLACE) {
+ resp_header = &k_cmd_transaction_info_v1.header;
+ ret = copy_transaction_info_v2_to_v1_locked(&k_transaction->info,
+ &k_cmd_transaction_info_v1.info);
+ }
spin_unlock_irqrestore(&client->transaction_lock, flags);
-
if (ret) {
- k_transaction_info.id = LWIS_ID_INVALID;
- lwis_transaction_free(lwis_dev, k_transaction);
- }
-
- if (copy_to_user((void __user *)msg, &k_transaction_info,
- sizeof(struct lwis_transaction_info))) {
- ret = -EFAULT;
- dev_err_ratelimited(lwis_dev->dev,
- "Failed to copy transaction results to userspace\n");
+ k_cmd_transaction_info_v1.info.id = LWIS_ID_INVALID;
+ k_cmd_transaction_info_v2.info.id = LWIS_ID_INVALID;
+ lwis_transaction_free(lwis_dev, &k_transaction);
}
- return ret;
-}
-
-static int ioctl_transaction_cancel(struct lwis_client *client, int64_t __user *msg)
-{
- int ret = 0;
- int64_t id;
- struct lwis_device *lwis_dev = client->lwis_dev;
-
- if (copy_from_user((void *)&id, (void __user *)msg, sizeof(id))) {
- dev_err(lwis_dev->dev, "Failed to copy transaction ID from user\n");
- return -EFAULT;
+ resp_header->cmd_id = header->cmd_id;
+ resp_header->next = header->next;
+ resp_header->ret_code = ret;
+ if (header->cmd_id == LWIS_CMD_ID_TRANSACTION_REPLACE_V2) {
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_cmd_transaction_info_v2,
+ sizeof(k_cmd_transaction_info_v2));
+ } else if (header->cmd_id == LWIS_CMD_ID_TRANSACTION_REPLACE) {
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_cmd_transaction_info_v1,
+ sizeof(k_cmd_transaction_info_v1));
}
- ret = lwis_transaction_cancel(client, id);
- if (ret) {
- dev_info_ratelimited(
- lwis_dev->dev,
- "Transaction id 0x%llx does not exist or is already done, not available for cancel(%d)\n",
- id, ret);
- return ret;
- }
+ ret = -EINVAL;
- return 0;
+err_exit:
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int construct_periodic_io(struct lwis_client *client,
- struct lwis_periodic_io_info __user *msg,
- struct lwis_periodic_io **periodic_io)
+static int construct_periodic_io_from_cmd(struct lwis_client *client,
+ struct lwis_cmd_periodic_io_info __user *u_msg,
+ struct lwis_periodic_io **periodic_io)
{
int ret = 0;
struct lwis_periodic_io *k_periodic_io;
- struct lwis_periodic_io_info *user_periodic_io;
+ struct lwis_cmd_periodic_io_info k_info;
struct lwis_device *lwis_dev = client->lwis_dev;
k_periodic_io = kmalloc(sizeof(struct lwis_periodic_io), GFP_KERNEL);
@@ -1232,17 +1398,17 @@ static int construct_periodic_io(struct lwis_client *client,
return -ENOMEM;
}
- user_periodic_io = (struct lwis_periodic_io_info *)msg;
- if (copy_from_user((void *)&k_periodic_io->info, (void __user *)user_periodic_io,
- sizeof(struct lwis_periodic_io_info))) {
- ret = -EFAULT;
+ if (copy_from_user((void *)&k_info, (void __user *)u_msg, sizeof(k_info))) {
dev_err(lwis_dev->dev, "Failed to copy periodic io info from user\n");
+ ret = -EFAULT;
goto error_free_periodic_io;
}
- ret = lwis_ioctl_util_construct_io_entry(client, k_periodic_io->info.io_entries,
- k_periodic_io->info.num_io_entries,
- &k_periodic_io->info.io_entries);
+ memcpy(&k_periodic_io->info, &k_info.info, sizeof(k_periodic_io->info));
+
+ ret = construct_io_entry(client, k_periodic_io->info.io_entries,
+ k_periodic_io->info.num_io_entries,
+ &k_periodic_io->info.io_entries);
if (ret) {
dev_err(lwis_dev->dev, "Failed to prepare lwis io entries for periodic io\n");
goto error_free_periodic_io;
@@ -1259,99 +1425,104 @@ error_free_periodic_io:
return ret;
}
-static int ioctl_periodic_io_submit(struct lwis_client *client,
- struct lwis_periodic_io_info __user *msg)
+static int cmd_periodic_io_submit(struct lwis_client *client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_periodic_io_info __user *u_msg)
{
int ret = 0;
+ struct lwis_cmd_periodic_io_info k_periodic_io_info;
struct lwis_periodic_io *k_periodic_io = NULL;
struct lwis_device *lwis_dev = client->lwis_dev;
- ret = construct_periodic_io(client, msg, &k_periodic_io);
+ ret = construct_periodic_io_from_cmd(client, u_msg, &k_periodic_io);
if (ret) {
- return ret;
+ goto err_exit;
}
ret = lwis_periodic_io_submit(client, k_periodic_io);
+ k_periodic_io_info.info = k_periodic_io->info;
if (ret) {
- k_periodic_io->info.id = LWIS_ID_INVALID;
- if (copy_to_user((void __user *)msg, &k_periodic_io->info,
- sizeof(struct lwis_periodic_io_info))) {
- dev_err_ratelimited(lwis_dev->dev, "Failed to return info to userspace\n");
- }
+ k_periodic_io_info.info.id = LWIS_ID_INVALID;
lwis_periodic_io_free(lwis_dev, k_periodic_io);
- return ret;
+ goto err_exit;
}
- if (copy_to_user((void __user *)msg, &k_periodic_io->info,
- sizeof(struct lwis_periodic_io_info))) {
- dev_err_ratelimited(lwis_dev->dev,
- "Failed to copy periodic io results to userspace\n");
- return -EFAULT;
- }
+ k_periodic_io_info.header.cmd_id = header->cmd_id;
+ k_periodic_io_info.header.next = header->next;
+ k_periodic_io_info.header.ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&k_periodic_io_info,
+ sizeof(k_periodic_io_info));
- return ret;
+err_exit:
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_periodic_io_cancel(struct lwis_client *client, int64_t __user *msg)
+static int cmd_periodic_io_cancel(struct lwis_client *client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_periodic_io_cancel __user *u_msg)
{
int ret = 0;
- int64_t id;
+ struct lwis_cmd_periodic_io_cancel k_msg;
struct lwis_device *lwis_dev = client->lwis_dev;
- if (copy_from_user((void *)&id, (void __user *)msg, sizeof(id))) {
+ if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
dev_err(lwis_dev->dev, "Failed to copy periodic io ID from user\n");
return -EFAULT;
}
- ret = lwis_periodic_io_cancel(client, id);
+ ret = lwis_periodic_io_cancel(client, k_msg.id);
if (ret) {
- dev_err_ratelimited(lwis_dev->dev, "Failed to clear periodic io id 0x%llx\n", id);
- return ret;
+ dev_err_ratelimited(lwis_dev->dev, "Failed to clear periodic io id 0x%llx\n",
+ k_msg.id);
}
- return 0;
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_dpm_clk_update(struct lwis_device *lwis_dev,
- struct lwis_dpm_clk_settings __user *msg)
+static int cmd_dpm_clk_update(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dpm_clk_update __user *u_msg)
{
int ret;
- struct lwis_dpm_clk_settings k_msg;
+ struct lwis_cmd_dpm_clk_update k_msg;
struct lwis_clk_setting *clk_settings;
size_t buf_size;
- if (copy_from_user((void *)&k_msg, (void __user *)msg,
- sizeof(struct lwis_dpm_clk_settings))) {
+ if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
dev_err(lwis_dev->dev, "Failed to copy ioctl message from user\n");
return -EFAULT;
}
- buf_size = sizeof(struct lwis_clk_setting) * k_msg.num_settings;
- if (buf_size / sizeof(struct lwis_clk_setting) != k_msg.num_settings) {
+ buf_size = sizeof(struct lwis_clk_setting) * k_msg.settings.num_settings;
+ if (buf_size / sizeof(struct lwis_clk_setting) != k_msg.settings.num_settings) {
dev_err(lwis_dev->dev, "Failed to copy clk settings due to integer overflow.\n");
- return -EOVERFLOW;
+ ret = -EOVERFLOW;
+ goto exit;
}
clk_settings = kmalloc(buf_size, GFP_KERNEL);
if (!clk_settings) {
dev_err(lwis_dev->dev, "Failed to allocate clock settings\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto exit;
}
- if (copy_from_user(clk_settings, (void __user *)k_msg.settings, buf_size)) {
+ if (copy_from_user(clk_settings, (void __user *)k_msg.settings.settings, buf_size)) {
dev_err(lwis_dev->dev, "Failed to copy clk settings from user\n");
kfree(clk_settings);
- return -EFAULT;
+ ret = -EFAULT;
+ goto exit;
}
- ret = lwis_dpm_update_clock(lwis_dev, clk_settings, k_msg.num_settings);
+ ret = lwis_dpm_update_clock(lwis_dev, clk_settings, k_msg.settings.num_settings);
kfree(clk_settings);
- return ret;
+exit:
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_dpm_qos_update(struct lwis_device *lwis_dev,
- struct lwis_dpm_qos_requirements __user *msg)
+static int cmd_dpm_qos_update(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dpm_qos_update __user *u_msg)
{
- struct lwis_dpm_qos_requirements k_msg;
+ struct lwis_cmd_dpm_qos_update k_msg;
struct lwis_qos_setting *k_qos_settings;
int ret = 0;
int i;
@@ -1359,233 +1530,408 @@ static int ioctl_dpm_qos_update(struct lwis_device *lwis_dev,
if (lwis_dev->type != DEVICE_TYPE_DPM) {
dev_err(lwis_dev->dev, "not supported device type: %d\n", lwis_dev->type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto exit;
}
- if (copy_from_user((void *)&k_msg, (void __user *)msg,
- sizeof(struct lwis_dpm_qos_requirements))) {
+ if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
dev_err(lwis_dev->dev, "Failed to copy ioctl message from user\n");
return -EFAULT;
}
// Copy qos settings from user buffer.
- buf_size = sizeof(struct lwis_qos_setting) * k_msg.num_settings;
- if (buf_size / sizeof(struct lwis_qos_setting) != k_msg.num_settings) {
+ buf_size = sizeof(struct lwis_qos_setting) * k_msg.reqs.num_settings;
+ if (buf_size / sizeof(struct lwis_qos_setting) != k_msg.reqs.num_settings) {
dev_err(lwis_dev->dev, "Failed to copy qos settings due to integer overflow.\n");
- return -EOVERFLOW;
+ ret = -EOVERFLOW;
+ goto exit;
}
k_qos_settings = kmalloc(buf_size, GFP_KERNEL);
if (!k_qos_settings) {
dev_err(lwis_dev->dev, "Failed to allocate qos settings\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto exit;
}
- if (copy_from_user(k_qos_settings, (void __user *)k_msg.qos_settings, buf_size)) {
+ if (copy_from_user(k_qos_settings, (void __user *)k_msg.reqs.qos_settings, buf_size)) {
+ dev_err(lwis_dev->dev, "Failed to copy clk settings from user\n");
+ kfree(k_qos_settings);
ret = -EFAULT;
+ goto exit;
+ }
+
+ for (i = 0; i < k_msg.reqs.num_settings; i++) {
+ if (sizeof(struct lwis_qos_setting) != sizeof(struct lwis_qos_setting_v2)) {
+ struct lwis_qos_setting_v2 k_qos_setting_v2;
+ memcpy(&k_qos_setting_v2, &k_qos_settings[i],
+ sizeof(struct lwis_qos_setting));
+ k_qos_setting_v2.bts_block_name[0] = '\0';
+ ret = lwis_dpm_update_qos(lwis_dev, &k_qos_setting_v2);
+ } else {
+ ret = lwis_dpm_update_qos(lwis_dev,
+ (struct lwis_qos_setting_v2 *)&k_qos_settings[i]);
+ }
+ if (ret) {
+ dev_err(lwis_dev->dev, "Failed to apply qos setting, ret: %d\n", ret);
+ kfree(k_qos_settings);
+ goto exit;
+ }
+ }
+ kfree(k_qos_settings);
+exit:
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_dpm_qos_update_v2(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dpm_qos_update_v2 __user *u_msg)
+{
+ struct lwis_cmd_dpm_qos_update_v2 k_msg;
+ struct lwis_qos_setting_v2 *k_qos_settings;
+ int ret = 0;
+ int i;
+ size_t buf_size;
+
+ if (lwis_dev->type != DEVICE_TYPE_DPM) {
+ dev_err(lwis_dev->dev, "not supported device type: %d\n", lwis_dev->type);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (copy_from_user((void *)&k_msg, (void __user *)u_msg, sizeof(k_msg))) {
+ dev_err(lwis_dev->dev, "Failed to copy ioctl message from user\n");
+ return -EFAULT;
+ }
+
+ // Copy qos settings from user buffer.
+ buf_size = sizeof(struct lwis_qos_setting_v2) * k_msg.reqs.num_settings;
+ if (buf_size / sizeof(struct lwis_qos_setting_v2) != k_msg.reqs.num_settings) {
+ dev_err(lwis_dev->dev, "Failed to copy qos settings due to integer overflow.\n");
+ ret = -EOVERFLOW;
+ goto exit;
+ }
+ k_qos_settings = kmalloc(buf_size, GFP_KERNEL);
+ if (!k_qos_settings) {
+ dev_err(lwis_dev->dev, "Failed to allocate qos settings\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ if (copy_from_user(k_qos_settings, (void __user *)k_msg.reqs.qos_settings, buf_size)) {
dev_err(lwis_dev->dev, "Failed to copy clk settings from user\n");
- goto out;
+ kfree(k_qos_settings);
+ ret = -EFAULT;
+ goto exit;
}
- for (i = 0; i < k_msg.num_settings; i++) {
+ for (i = 0; i < k_msg.reqs.num_settings; i++) {
ret = lwis_dpm_update_qos(lwis_dev, &k_qos_settings[i]);
if (ret) {
dev_err(lwis_dev->dev, "Failed to apply qos setting, ret: %d\n", ret);
- goto out;
+ kfree(k_qos_settings);
+ goto exit;
}
}
-out:
kfree(k_qos_settings);
- return ret;
+exit:
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
}
-static int ioctl_dpm_get_clock(struct lwis_device *lwis_dev, struct lwis_qos_setting __user *msg)
+static int cmd_dpm_get_clock(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_dpm_clk_get __user *u_msg)
{
- struct lwis_qos_setting current_setting;
+ struct lwis_cmd_dpm_clk_get current_setting;
struct lwis_device *target_device;
+ int ret = 0;
if (lwis_dev->type != DEVICE_TYPE_DPM) {
dev_err(lwis_dev->dev, "not supported device type: %d\n", lwis_dev->type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_exit;
}
- if (copy_from_user((void *)&current_setting, (void __user *)msg,
- sizeof(struct lwis_qos_setting))) {
+ if (copy_from_user((void *)&current_setting, (void __user *)u_msg,
+ sizeof(current_setting))) {
dev_err(lwis_dev->dev, "failed to copy from user\n");
return -EFAULT;
}
- target_device = lwis_find_dev_by_id(current_setting.device_id);
+ target_device = lwis_find_dev_by_id(current_setting.setting.device_id);
if (!target_device) {
dev_err(lwis_dev->dev, "could not find lwis device by id %d\n",
- current_setting.device_id);
- return -ENODEV;
+ current_setting.setting.device_id);
+ ret = -ENODEV;
+ goto err_exit;
}
if (target_device->enabled == 0 && target_device->type != DEVICE_TYPE_DPM) {
dev_warn(target_device->dev, "%s disabled, can't get clk\n", target_device->name);
- return -EPERM;
+ ret = -EPERM;
+ goto err_exit;
}
- current_setting.frequency_hz = (int64_t)lwis_dpm_read_clock(target_device);
- if (copy_to_user((void __user *)msg, &current_setting, sizeof(struct lwis_qos_setting))) {
- dev_err(lwis_dev->dev, "failed to copy to user\n");
+ current_setting.setting.frequency_hz = (int64_t)lwis_dpm_read_clock(target_device);
+ current_setting.header.ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&current_setting, sizeof(current_setting));
+
+err_exit:
+ header->ret_code = ret;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+}
+
+static int cmd_fence_create(struct lwis_device *lwis_dev, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_fence_create __user *u_msg)
+{
+ int32_t fd_or_err;
+ struct lwis_cmd_fence_create fence_create;
+
+ if (copy_from_user((void *)&fence_create, (void __user *)u_msg, sizeof(fence_create))) {
+ dev_err(lwis_dev->dev, "failed to copy from user\n");
return -EFAULT;
}
- return 0;
+ fd_or_err = lwis_fence_create(lwis_dev);
+ if (fd_or_err < 0) {
+ header->ret_code = fd_or_err;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)header, sizeof(*header));
+ }
+
+ fence_create.fd = fd_or_err;
+ fence_create.header.ret_code = 0;
+ return copy_pkt_to_user(lwis_dev, u_msg, (void *)&fence_create, sizeof(fence_create));
}
-int lwis_ioctl_handler(struct lwis_client *lwis_client, unsigned int type, unsigned long param)
+static int handle_cmd_pkt(struct lwis_client *lwis_client, struct lwis_cmd_pkt *header,
+ struct lwis_cmd_pkt __user *user_msg)
{
- int ret = 0;
- bool device_disabled;
struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+ int ret = 0;
- mutex_lock(&lwis_dev->client_lock);
- device_disabled = (lwis_dev->enabled == 0);
- mutex_unlock(&lwis_dev->client_lock);
- /* Buffer dis/enroll is added here temporarily. Will need a proper
- fix to ensure buffer enrollment when device is enabled. */
- if (lwis_dev->type != DEVICE_TYPE_TOP && device_disabled && type != LWIS_GET_DEVICE_INFO &&
- type != LWIS_DEVICE_ENABLE && type != LWIS_DEVICE_RESET &&
- type != LWIS_EVENT_CONTROL_GET && type != LWIS_TIME_QUERY &&
- type != LWIS_EVENT_DEQUEUE && type != LWIS_BUFFER_ENROLL &&
- type != LWIS_BUFFER_DISENROLL && type != LWIS_BUFFER_FREE &&
- type != LWIS_DPM_QOS_UPDATE && type != LWIS_DPM_GET_CLOCK && type != LWIS_CMD_PACKET &&
- type != LWIS_DUMP_DEBUG_STATE) {
- ret = -EBADFD;
- dev_err_ratelimited(lwis_dev->dev, "Unsupported IOCTL on disabled device.\n");
- goto out;
- }
-
- switch (type) {
- case LWIS_CMD_PACKET:
- ret = lwis_ioctl_handle_cmd_pkt(lwis_client, (struct lwis_cmd_pkt *)param);
+ switch (header->cmd_id) {
+ case LWIS_CMD_ID_ECHO:
+ ret = cmd_echo(lwis_dev, header, (struct lwis_cmd_echo __user *)user_msg);
+ break;
+ case LWIS_CMD_ID_TIME_QUERY:
+ ret = cmd_time_query(lwis_dev, header,
+ (struct lwis_cmd_time_query __user *)user_msg);
break;
- case LWIS_GET_DEVICE_INFO:
+ case LWIS_CMD_ID_GET_DEVICE_INFO:
mutex_lock(&lwis_client->lock);
- ret = ioctl_get_device_info(lwis_dev, (struct lwis_device_info *)param);
+ ret = cmd_get_device_info(lwis_dev, header,
+ (struct lwis_cmd_device_info __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_BUFFER_ALLOC:
+ case LWIS_CMD_ID_DEVICE_ENABLE:
mutex_lock(&lwis_client->lock);
- ret = ioctl_buffer_alloc(lwis_client, (struct lwis_alloc_buffer_info *)param);
+ ret = cmd_device_enable(lwis_client, header,
+ (struct lwis_cmd_pkt __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_BUFFER_FREE:
+ case LWIS_CMD_ID_DEVICE_DISABLE:
mutex_lock(&lwis_client->lock);
- ret = ioctl_buffer_free(lwis_client, (int *)param);
+ ret = cmd_device_disable(lwis_client, header,
+ (struct lwis_cmd_pkt __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_BUFFER_ENROLL:
+ case LWIS_CMD_ID_DEVICE_RESET:
mutex_lock(&lwis_client->lock);
- ret = ioctl_buffer_enroll(lwis_client, (struct lwis_buffer_info *)param);
+ ret = cmd_device_reset(lwis_client, header,
+ (struct lwis_cmd_io_entries __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_BUFFER_DISENROLL:
+ case LWIS_CMD_ID_DEVICE_SUSPEND:
mutex_lock(&lwis_client->lock);
- ret = ioctl_buffer_disenroll(lwis_client,
- (struct lwis_enrolled_buffer_info *)param);
+ ret = cmd_device_suspend(lwis_client, header,
+ (struct lwis_cmd_pkt __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_BUFFER_CPU_ACCESS:
+ case LWIS_CMD_ID_DEVICE_RESUME:
mutex_lock(&lwis_client->lock);
- ret = ioctl_buffer_cpu_access(lwis_client,
- (struct lwis_buffer_cpu_access_op *)param);
+ ret = cmd_device_resume(lwis_client, header,
+ (struct lwis_cmd_pkt __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_REG_IO:
+ case LWIS_CMD_ID_DUMP_DEBUG_STATE:
+ ret = cmd_dump_debug_state(lwis_client, header,
+ (struct lwis_cmd_pkt __user *)user_msg);
+ break;
+ case LWIS_CMD_ID_DMA_BUFFER_ENROLL:
mutex_lock(&lwis_client->lock);
- ret = ioctl_reg_io(lwis_dev, (struct lwis_io_entries *)param);
+ ret = cmd_dma_buffer_enroll(lwis_client, header,
+ (struct lwis_cmd_dma_buffer_enroll __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_DEVICE_ENABLE:
+ case LWIS_CMD_ID_DMA_BUFFER_DISENROLL:
mutex_lock(&lwis_client->lock);
- ret = ioctl_device_enable(lwis_client);
+ ret = cmd_dma_buffer_disenroll(
+ lwis_client, header,
+ (struct lwis_cmd_dma_buffer_disenroll __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_DEVICE_DISABLE:
+ case LWIS_CMD_ID_DMA_BUFFER_CPU_ACCESS:
mutex_lock(&lwis_client->lock);
- ret = ioctl_device_disable(lwis_client);
+ ret = cmd_dma_buffer_cpu_access(
+ lwis_client, header,
+ (struct lwis_cmd_dma_buffer_cpu_access __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_ECHO:
- ret = ioctl_echo(lwis_dev, (struct lwis_echo *)param);
+ case LWIS_CMD_ID_DMA_BUFFER_ALLOC:
+ mutex_lock(&lwis_client->lock);
+ ret = cmd_dma_buffer_alloc(lwis_client, header,
+ (struct lwis_cmd_dma_buffer_alloc __user *)user_msg);
+ mutex_unlock(&lwis_client->lock);
break;
- case LWIS_DEVICE_RESET:
+ case LWIS_CMD_ID_DMA_BUFFER_FREE:
mutex_lock(&lwis_client->lock);
- ret = ioctl_device_reset(lwis_client, (struct lwis_io_entries *)param);
+ ret = cmd_dma_buffer_free(lwis_client, header,
+ (struct lwis_cmd_dma_buffer_free __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_DUMP_DEBUG_STATE:
- ret = ioctl_dump_debug_state(lwis_client);
+ case LWIS_CMD_ID_REG_IO:
+ mutex_lock(&lwis_client->lock);
+ ret = cmd_reg_io(lwis_dev, header, (struct lwis_cmd_io_entries __user *)user_msg);
+ mutex_unlock(&lwis_client->lock);
break;
- case LWIS_EVENT_CONTROL_GET:
+ case LWIS_CMD_ID_EVENT_CONTROL_GET:
mutex_lock(&lwis_client->lock);
- ret = ioctl_event_control_get(lwis_client, (struct lwis_event_control *)param);
+ ret = cmd_event_control_get(lwis_client, header,
+ (struct lwis_cmd_event_control_get __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_EVENT_CONTROL_SET:
+ case LWIS_CMD_ID_EVENT_CONTROL_SET:
mutex_lock(&lwis_client->lock);
- ret = ioctl_event_control_set(lwis_client, (struct lwis_event_control_list *)param);
+ ret = cmd_event_control_set(lwis_client, header,
+ (struct lwis_cmd_event_control_set __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_EVENT_DEQUEUE:
- ret = ioctl_event_dequeue(lwis_client, (struct lwis_event_info *)param);
+ case LWIS_CMD_ID_EVENT_DEQUEUE:
+ ret = cmd_event_dequeue(lwis_client, header,
+ (struct lwis_cmd_event_dequeue __user *)user_msg);
break;
- case LWIS_TIME_QUERY:
- ret = ioctl_time_query(lwis_client, (int64_t *)param);
+ case LWIS_CMD_ID_TRANSACTION_SUBMIT:
+ case LWIS_CMD_ID_TRANSACTION_SUBMIT_V2:
+ ret = cmd_transaction_submit(lwis_client, header,
+ (struct lwis_cmd_pkt __user *)user_msg);
break;
- case LWIS_TRANSACTION_SUBMIT:
- mutex_lock(&lwis_client->lock);
- ret = ioctl_transaction_submit(lwis_client, (struct lwis_transaction_info *)param);
- mutex_unlock(&lwis_client->lock);
+ case LWIS_CMD_ID_TRANSACTION_CANCEL:
+ ret = cmd_transaction_cancel(lwis_client, header,
+ (struct lwis_cmd_transaction_cancel __user *)user_msg);
+ break;
+ case LWIS_CMD_ID_TRANSACTION_REPLACE:
+ case LWIS_CMD_ID_TRANSACTION_REPLACE_V2:
+ ret = cmd_transaction_replace(lwis_client, header,
+ (struct lwis_cmd_pkt __user *)user_msg);
break;
- case LWIS_TRANSACTION_CANCEL:
+ case LWIS_CMD_ID_PERIODIC_IO_SUBMIT:
mutex_lock(&lwis_client->lock);
- ret = ioctl_transaction_cancel(lwis_client, (int64_t *)param);
+ ret = cmd_periodic_io_submit(lwis_client, header,
+ (struct lwis_cmd_periodic_io_info __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_TRANSACTION_REPLACE:
+ case LWIS_CMD_ID_PERIODIC_IO_CANCEL:
mutex_lock(&lwis_client->lock);
- ret = ioctl_transaction_replace(lwis_client, (struct lwis_transaction_info *)param);
+ ret = cmd_periodic_io_cancel(lwis_client, header,
+ (struct lwis_cmd_periodic_io_cancel __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_PERIODIC_IO_SUBMIT:
+ case LWIS_CMD_ID_DPM_CLK_UPDATE:
mutex_lock(&lwis_client->lock);
- ret = ioctl_periodic_io_submit(lwis_client, (struct lwis_periodic_io_info *)param);
+ ret = cmd_dpm_clk_update(lwis_dev, header,
+ (struct lwis_cmd_dpm_clk_update __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_PERIODIC_IO_CANCEL:
+ case LWIS_CMD_ID_DPM_QOS_UPDATE:
mutex_lock(&lwis_client->lock);
- ret = ioctl_periodic_io_cancel(lwis_client, (int64_t *)param);
+ ret = cmd_dpm_qos_update(lwis_dev, header,
+ (struct lwis_cmd_dpm_qos_update __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_DPM_CLK_UPDATE:
+ case LWIS_CMD_ID_DPM_QOS_UPDATE_V2:
mutex_lock(&lwis_client->lock);
- ret = ioctl_dpm_clk_update(lwis_dev, (struct lwis_dpm_clk_settings *)param);
+ ret = cmd_dpm_qos_update_v2(lwis_dev, header,
+ (struct lwis_cmd_dpm_qos_update_v2 __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_DPM_QOS_UPDATE:
+ case LWIS_CMD_ID_DPM_GET_CLOCK:
mutex_lock(&lwis_client->lock);
- ret = ioctl_dpm_qos_update(lwis_dev, (struct lwis_dpm_qos_requirements *)param);
+ ret = cmd_dpm_get_clock(lwis_dev, header,
+ (struct lwis_cmd_dpm_clk_get __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
- case LWIS_DPM_GET_CLOCK:
+ case LWIS_CMD_ID_FENCE_CREATE:
+ ret = cmd_fence_create(lwis_dev, header,
+ (struct lwis_cmd_fence_create __user *)user_msg);
+ break;
+ case LWIS_CMD_ID_EVENT_INJECTION:
mutex_lock(&lwis_client->lock);
- ret = ioctl_dpm_get_clock(lwis_dev, (struct lwis_qos_setting *)param);
+ ret = cmd_fake_event_inject(lwis_client, header,
+ (struct lwis_cmd_pkt __user *)user_msg);
mutex_unlock(&lwis_client->lock);
break;
-#ifdef LWIS_FENCE_ENABLED
- case LWIS_FENCE_CREATE:
- ret = ioctl_lwis_fence_create(lwis_dev, (int32_t *)param);
+ default:
+ dev_err_ratelimited(lwis_dev->dev, "Unknown command id 0x%x\n", header->cmd_id);
+ header->ret_code = -ENOSYS;
+ ret = copy_pkt_to_user(lwis_dev, user_msg, (void *)header, sizeof(*header));
+ }
+
+ return ret;
+}
+
+static int lwis_ioctl_handle_cmd_pkt(struct lwis_client *lwis_client,
+ struct lwis_cmd_pkt __user *user_msg)
+{
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+ struct lwis_cmd_pkt header;
+ int ret = 0;
+ bool device_disabled;
+
+ while (user_msg) {
+ /* Copy cmd packet header from userspace */
+ if (copy_from_user(&header, (void __user *)user_msg, sizeof(header))) {
+ dev_err(lwis_dev->dev,
+ "Failed to copy cmd packet header from userspace.\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&lwis_dev->client_lock);
+ device_disabled = (lwis_dev->enabled == 0);
+ mutex_unlock(&lwis_dev->client_lock);
+ if (lwis_dev->type != DEVICE_TYPE_TOP && device_disabled &&
+ (header.cmd_id == LWIS_CMD_ID_DMA_BUFFER_ALLOC ||
+ header.cmd_id == LWIS_CMD_ID_REG_IO ||
+ header.cmd_id == LWIS_CMD_ID_TRANSACTION_SUBMIT ||
+ header.cmd_id == LWIS_CMD_ID_TRANSACTION_REPLACE ||
+ header.cmd_id == LWIS_CMD_ID_PERIODIC_IO_SUBMIT)) {
+ dev_err_ratelimited(lwis_dev->dev,
+ "Unsupported IOCTL on disabled device.\n");
+ header.ret_code = -EBADFD;
+ return copy_pkt_to_user(lwis_dev, user_msg, (void *)&header,
+ sizeof(header));
+ }
+
+ ret = handle_cmd_pkt(lwis_client, &header, user_msg);
+ if (ret) {
+ return ret;
+ }
+ user_msg = header.next;
+ }
+
+ return ret;
+}
+
+int lwis_ioctl_handler(struct lwis_client *lwis_client, unsigned int type, unsigned long param)
+{
+ int ret = 0;
+ struct lwis_device *lwis_dev = lwis_client->lwis_dev;
+
+ switch (type) {
+ case LWIS_CMD_PACKET:
+ ret = lwis_ioctl_handle_cmd_pkt(lwis_client, (struct lwis_cmd_pkt *)param);
break;
-#endif
default:
dev_err_ratelimited(lwis_dev->dev, "Unknown IOCTL operation\n");
ret = -EINVAL;
};
-out:
if (ret && ret != -ENOENT && ret != -ETIMEDOUT && ret != -EAGAIN) {
lwis_ioctl_pr_err(lwis_dev, type, ret);
}
diff --git a/lwis_ioctl.h b/lwis_ioctl.h
index 51864bb..8fba49f 100644
--- a/lwis_ioctl.h
+++ b/lwis_ioctl.h
@@ -18,18 +18,4 @@
*/
int lwis_ioctl_handler(struct lwis_client *lwis_client, unsigned int type, unsigned long param);
-/*
- * lwis_ioctl_util_synchronous_process_io_entries: Synchronous process lwis_io_entry
- */
-int lwis_ioctl_util_synchronous_process_io_entries(struct lwis_device *lwis_dev, int num_io_entries,
- struct lwis_io_entry *io_entries,
- struct lwis_io_entry *user_msg);
-
-/*
- * lwis_ioctl_util_construct_io_entry: Allocate kernel lwis_io_entry from user space input
- */
-int lwis_ioctl_util_construct_io_entry(struct lwis_client *client,
- struct lwis_io_entry *user_entries, size_t num_io_entries,
- struct lwis_io_entry **io_entries);
-
#endif /* LWIS_IOCTL_H_ */
diff --git a/lwis_ioreg.c b/lwis_ioreg.c
index 90870aa..ce8ee75 100644
--- a/lwis_ioreg.c
+++ b/lwis_ioreg.c
@@ -271,28 +271,28 @@ static int ioreg_write_batch_internal(void __iomem *base, uint64_t offset, int v
case 8:
for (i = 0; i < size_in_bytes; ++i) {
writeb_relaxed(*(buf + i), is_offset_fixed ? (void __iomem *)(addr) :
- (void __iomem *)(addr + i));
+ (void __iomem *)(addr + i));
}
break;
case 16:
for (i = 0; i < size_in_bytes; i += 2) {
writew_relaxed(*(uint16_t *)(buf + i), is_offset_fixed ?
(void __iomem *)(addr) :
- (void __iomem *)(addr + i));
+ (void __iomem *)(addr + i));
}
break;
case 32:
for (i = 0; i < size_in_bytes; i += 4) {
writel_relaxed(*(uint32_t *)(buf + i), is_offset_fixed ?
(void __iomem *)(addr) :
- (void __iomem *)(addr + i));
+ (void __iomem *)(addr + i));
}
break;
case 64:
for (i = 0; i < size_in_bytes; i += 8) {
writeq_relaxed(*(uint64_t *)(buf + i), is_offset_fixed ?
(void __iomem *)(addr) :
- (void __iomem *)(addr + i));
+ (void __iomem *)(addr + i));
}
break;
default:
diff --git a/lwis_periodic_io.c b/lwis_periodic_io.c
index d12fde8..39145ab 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,20 +35,25 @@ 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;
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) {
periodic_io = list_entry(it_period, struct lwis_periodic_io, timer_list_node);
if (periodic_io->active) {
periodic_io_proxy = lwis_allocator_allocate(
- client->lwis_dev, sizeof(struct lwis_periodic_io_proxy));
+ 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 +63,12 @@ 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) {
+ 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 +99,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 +115,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,12 +195,12 @@ 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,
* or we just started the process. */
- if (!periodic_io->active &&
- (i == 0 || !periodic_io->contains_multiple_writes)) {
+ if (!periodic_io->active && (i == 0 || !periodic_io->contains_multiple_writes)) {
resp->error_code = -ECANCELED;
goto event_push;
}
@@ -234,7 +245,19 @@ static int process_io_entries(struct lwis_client *client,
read_buf += sizeof(struct lwis_periodic_io_result) +
io_result->io_result.num_value_bytes;
} else if (entry->type == LWIS_IO_ENTRY_POLL) {
- ret = lwis_io_entry_poll(lwis_dev, entry);
+ ret = lwis_io_entry_poll(lwis_dev, entry, /*is_short=*/false);
+ if (ret) {
+ resp->error_code = ret;
+ goto event_push;
+ }
+ } else if (entry->type == LWIS_IO_ENTRY_POLL_SHORT) {
+ ret = lwis_io_entry_poll(lwis_dev, entry, /*is_short=*/true);
+ if (ret) {
+ resp->error_code = ret;
+ goto event_push;
+ }
+ } else if (entry->type == LWIS_IO_ENTRY_WAIT) {
+ ret = lwis_io_entry_wait(lwis_dev, entry);
if (ret) {
resp->error_code = ret;
goto event_push;
@@ -256,6 +279,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) {
@@ -269,8 +294,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;
}
@@ -496,6 +523,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);
@@ -510,11 +546,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) {
@@ -524,6 +579,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;
}
@@ -537,7 +593,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;
}
@@ -558,7 +615,8 @@ static int mark_periodic_io_resp_error_locked(struct lwis_periodic_io *periodic_
}
/* Calling this function requires holding the client's periodic_io_lock */
-static struct lwis_periodic_io * periodic_io_find_locked(struct lwis_client *client, int64_t id) {
+static struct lwis_periodic_io *periodic_io_find_locked(struct lwis_client *client, int64_t id)
+{
int i;
struct hlist_node *tmp;
struct list_head *it_period, *it_period_tmp;
diff --git a/lwis_phy.c b/lwis_phy.c
index 6e4830a..7f2b8be 100644
--- a/lwis_phy.c
+++ b/lwis_phy.c
@@ -83,7 +83,7 @@ int lwis_phy_get(struct lwis_phy_list *list, char *name, struct device *dev)
/* Make sure PHY exists */
phy = devm_phy_get(dev, name);
- if (IS_ERR(phy)) {
+ if (IS_ERR_OR_NULL(phy)) {
pr_err("PHY %s not found\n", name);
return PTR_ERR(phy);
}
diff --git a/lwis_pinctrl.c b/lwis_pinctrl.c
index f8e758d..170912e 100644
--- a/lwis_pinctrl.c
+++ b/lwis_pinctrl.c
@@ -25,7 +25,7 @@ int lwis_pinctrl_set_state(struct pinctrl *pc, char *state_str)
}
state = pinctrl_lookup_state(pc, state_str);
- if (IS_ERR(state)) {
+ if (IS_ERR_OR_NULL(state)) {
pr_err("Cannot find mclk state %s\n", state_str);
return PTR_ERR(state);
}
diff --git a/lwis_regulator.c b/lwis_regulator.c
index 9f3b6fc..56d9d92 100644
--- a/lwis_regulator.c
+++ b/lwis_regulator.c
@@ -81,7 +81,7 @@ int lwis_regulator_get(struct lwis_regulator_list *list, char *name, int voltage
/* Make sure regulator exists */
reg = devm_regulator_get(dev, name);
- if (IS_ERR(reg)) {
+ if (IS_ERR_OR_NULL(reg)) {
return PTR_ERR(reg);
}
diff --git a/lwis_trace.h b/lwis_trace.h
index 04c8540..e1f14a6 100644
--- a/lwis_trace.h
+++ b/lwis_trace.h
@@ -19,9 +19,8 @@
#include "lwis_commands.h"
#include "lwis_device.h"
-#define LWIS_DEVICE_NAME_ENTRY \
- __array(char, lwis_name, LWIS_MAX_NAME_STRING_LEN)
-#define LWIS_DEVICE_NAME_ASSIGN \
+#define LWIS_DEVICE_NAME_ENTRY __array(char, lwis_name, LWIS_MAX_NAME_STRING_LEN)
+#define LWIS_DEVICE_NAME_ASSIGN \
strscpy(__entry->lwis_name, lwis_dev->name, LWIS_MAX_NAME_STRING_LEN)
#define LWIS_TRACE_DEVICE_NAME __entry->lwis_name
@@ -36,17 +35,17 @@ TRACE_EVENT(tracing_mark_write,
TP_printk("%c|%d|lwis-%s:%s|%lld", __entry->type, __entry->pid, LWIS_TRACE_DEVICE_NAME,
__get_str(func_name), __entry->value));
-#define LWIS_ATRACE_BEGIN(lwis_dev, func_name) \
+#define LWIS_ATRACE_BEGIN(lwis_dev, func_name) \
trace_tracing_mark_write(lwis_dev, 'B', current->tgid, func_name, 0)
#define LWIS_ATRACE_FUNC_BEGIN(lwis_dev, func_name) LWIS_ATRACE_BEGIN(lwis_dev, func_name)
-#define LWIS_ATRACE_END(lwis_dev, func_name) \
+#define LWIS_ATRACE_END(lwis_dev, func_name) \
trace_tracing_mark_write(lwis_dev, 'E', current->tgid, func_name, 0)
#define LWIS_ATRACE_FUNC_END(lwis_dev, func_name) LWIS_ATRACE_END(lwis_dev, func_name)
-#define LWIS_ATRACE_INT_PID(lwis_dev, func_name, value, pid) \
+#define LWIS_ATRACE_INT_PID(lwis_dev, func_name, value, pid) \
trace_tracing_mark_write(lwis_dev, 'C', pid, func_name, value)
-#define LWIS_ATRACE_INT(lwis_dev, func_name, value) \
+#define LWIS_ATRACE_INT(lwis_dev, func_name, value) \
LWIS_ATRACE_INT_PID(lwis_dev, func_name, value, current->tgid)
#endif /* _TRACE_LWIS_H_ */
diff --git a/lwis_transaction.c b/lwis_transaction.c
index cf9509e..89fa685 100644
--- a/lwis_transaction.c
+++ b/lwis_transaction.c
@@ -24,6 +24,7 @@
#include "lwis_device.h"
#include "lwis_event.h"
#include "lwis_fence.h"
+#include "lwis_i2c_bus_manager.h"
#include "lwis_io_entry.h"
#include "lwis_ioreg.h"
#include "lwis_util.h"
@@ -34,6 +35,9 @@
#define EXPLICIT_EVENT_COUNTER(x) \
((x) != LWIS_EVENT_COUNTER_ON_NEXT_OCCURRENCE && (x) != LWIS_EVENT_COUNTER_EVERY_TIME)
+bool lwis_transaction_debug;
+module_param(lwis_transaction_debug, bool, 0644);
+
static struct lwis_transaction_event_list *event_list_find(struct lwis_client *client,
int64_t event_id)
{
@@ -73,13 +77,11 @@ static void add_pending_transaction(struct lwis_client *client,
{
hash_add(client->pending_transactions, &transaction->pending_map_node,
transaction->info.id);
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(client->lwis_dev->dev,
"lwis_fence add transaction id %llu to lwis_client pending map",
transaction->info.id);
}
-#endif
}
static struct lwis_transaction *pending_transaction_peek(struct lwis_client *client,
@@ -97,7 +99,7 @@ static struct lwis_transaction *pending_transaction_peek(struct lwis_client *cli
}
static void save_transaction_to_history(struct lwis_client *client,
- struct lwis_transaction_info *trans_info,
+ struct lwis_transaction_info_v2 *trans_info,
int64_t process_timestamp, int64_t process_duration_ns)
{
client->debug_info.transaction_hist[client->debug_info.cur_transaction_hist_idx].info =
@@ -112,14 +114,16 @@ static void save_transaction_to_history(struct lwis_client *client,
}
}
-void lwis_transaction_free(struct lwis_device *lwis_dev, struct lwis_transaction *transaction)
+void lwis_transaction_free(struct lwis_device *lwis_dev, struct lwis_transaction **ptransaction)
{
int i;
struct lwis_fence_pending_signal *pending_fence;
struct list_head *it_fence, *it_fence_tmp;
+ struct lwis_transaction *transaction = *ptransaction;
if (transaction->is_weak_transaction) {
kfree(transaction);
+ transaction = NULL;
return;
}
@@ -145,35 +149,83 @@ void lwis_transaction_free(struct lwis_device *lwis_dev, struct lwis_transaction
}
}
lwis_allocator_free(lwis_dev, transaction->info.io_entries);
+
+ transaction->starting_read_buf = NULL;
+
if (transaction->resp) {
kfree(transaction->resp);
}
kfree(transaction);
+ *ptransaction = NULL;
}
-static int process_transaction(struct lwis_client *client, struct lwis_transaction *transaction,
+static int process_transaction(struct lwis_client *client, struct lwis_transaction **ptransaction,
struct list_head *pending_events, struct list_head *pending_fences,
- bool skip_err)
+ bool skip_err, bool check_transaction_limit)
{
int i;
int ret = 0;
int pending_status;
struct lwis_io_entry *entry = NULL;
struct lwis_device *lwis_dev = client->lwis_dev;
- struct lwis_transaction_info *info = &transaction->info;
+ struct lwis_transaction *transaction = *ptransaction;
+ struct lwis_transaction_info_v2 *info = &transaction->info;
struct lwis_transaction_response_header *resp = transaction->resp;
size_t resp_size;
uint8_t *read_buf;
struct lwis_io_result *io_result;
const int reg_value_bytewidth = lwis_dev->native_value_bitwidth / 8;
- int64_t process_duration_ns = 0;
- int64_t process_timestamp = ktime_to_ns(lwis_get_time());
+ int64_t process_duration_ns = -1;
+ int64_t process_timestamp = -1;
unsigned long flags;
+ int total_number_of_entries = info->num_io_entries;
+ int max_transaction_entry_limit = lwis_dev->transaction_process_limit;
+ int remaining_entries_to_be_processed = transaction->remaining_entries_to_process;
+ int number_of_entries_to_process_in_current_run = 0;
+ int processing_start_index = 0;
+ int processing_end_index = 0;
+
+ /*
+ * Process all the transactions at once if:
+ * 1. the processing device has no limitation on the number of entries to process per transaction
+ * 2. the transaction is running in event context
+ * 3. the transaction is being called as a part of cleanup
+ * Note: For #2 and #3, this transaction will not be queued for further processing by any worker thread
+ * later and therefore all entries need to be processed in the same run
+ */
+ if ((lwis_dev->transaction_process_limit <= 0) ||
+ (transaction->info.run_in_event_context) || (skip_err == true) ||
+ (check_transaction_limit == false)) {
+ max_transaction_entry_limit = total_number_of_entries;
+ }
+
+ number_of_entries_to_process_in_current_run =
+ (remaining_entries_to_be_processed > max_transaction_entry_limit) ?
+ max_transaction_entry_limit :
+ remaining_entries_to_be_processed;
+ processing_start_index = total_number_of_entries - remaining_entries_to_be_processed;
+ processing_end_index = processing_start_index + number_of_entries_to_process_in_current_run;
+ remaining_entries_to_be_processed =
+ remaining_entries_to_be_processed - number_of_entries_to_process_in_current_run;
+
+ if (lwis_transaction_debug) {
+ process_timestamp = ktime_to_ns(lwis_get_time());
+ }
+
resp_size = sizeof(struct lwis_transaction_response_header) + resp->results_size_bytes;
read_buf = (uint8_t *)resp + sizeof(struct lwis_transaction_response_header);
+
resp->completion_index = -1;
+ /*
+ * If the starting read buffer pointer is not null then use this cached location to correctly
+ * set the read buffer for the current transaction processing run.
+ */
+ if (transaction->starting_read_buf) {
+ read_buf = transaction->starting_read_buf;
+ }
+
/* 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) {
@@ -181,8 +233,8 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti
/*use_read_barrier=*/false,
/*use_write_barrier=*/true);
}
-
- for (i = 0; i < info->num_io_entries; ++i) {
+ lwis_i2c_bus_manager_lock_i2c_bus(lwis_dev);
+ for (i = processing_start_index; i < processing_end_index; ++i) {
entry = &info->io_entries[i];
if (entry->type == LWIS_IO_ENTRY_WRITE ||
entry->type == LWIS_IO_ENTRY_WRITE_BATCH ||
@@ -241,7 +293,33 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti
}
read_buf += sizeof(struct lwis_io_result) + io_result->num_value_bytes;
} else if (entry->type == LWIS_IO_ENTRY_POLL) {
- ret = lwis_io_entry_poll(lwis_dev, entry);
+ ret = lwis_io_entry_poll(lwis_dev, entry, /*is_short=*/false);
+ if (ret) {
+ resp->error_code = ret;
+ if (skip_err) {
+ dev_warn(
+ lwis_dev->dev,
+ "transaction type %d processing failed, skip this error and run the next command\n",
+ entry->type);
+ continue;
+ }
+ break;
+ }
+ } else if (entry->type == LWIS_IO_ENTRY_POLL_SHORT) {
+ ret = lwis_io_entry_poll(lwis_dev, entry, /*is_short=*/true);
+ if (ret) {
+ resp->error_code = ret;
+ if (skip_err) {
+ dev_warn(
+ lwis_dev->dev,
+ "transaction type %d processing failed, skip this error and run the next command\n",
+ entry->type);
+ continue;
+ }
+ break;
+ }
+ } else if (entry->type == LWIS_IO_ENTRY_WAIT) {
+ ret = lwis_io_entry_wait(lwis_dev, entry);
if (ret) {
resp->error_code = ret;
if (skip_err) {
@@ -281,7 +359,10 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti
resp->completion_index = i;
}
- process_duration_ns = ktime_to_ns(lwis_get_time() - process_timestamp);
+ lwis_i2c_bus_manager_unlock_i2c_bus(lwis_dev);
+ if (lwis_transaction_debug) {
+ process_duration_ns = ktime_to_ns(lwis_get_time() - process_timestamp);
+ }
/* Use read memory barrier at the end of I/O entries if the access protocol
* allows it */
@@ -289,6 +370,22 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti
lwis_dev->vops.register_io_barrier(lwis_dev, /*use_read_barrier=*/true,
/*use_write_barrier=*/false);
}
+
+ if ((remaining_entries_to_be_processed > 0) && (ret == 0)) {
+ /*
+ * If there are remaining entries to be processed in this transaction,
+ * don't delete this transaction and update the current remaining
+ * count of entries in the transaction. Stop processing further
+ * until there are no more remaining entries to be processed
+ * in the transaction.
+ */
+ spin_lock_irqsave(&client->transaction_lock, flags);
+ transaction->starting_read_buf = read_buf;
+ transaction->remaining_entries_to_process = remaining_entries_to_be_processed;
+ spin_unlock_irqrestore(&client->transaction_lock, flags);
+ return ret;
+ }
+
if (pending_events) {
lwis_pending_event_push(pending_events,
resp->error_code ? info->emit_error_event_id :
@@ -304,31 +401,54 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti
}
spin_lock_irqsave(&client->transaction_lock, flags);
+ transaction->remaining_entries_to_process = remaining_entries_to_be_processed;
+
if (pending_fences) {
/* Convert -ECANCELED error code to userspace Cancellation error code */
pending_status = resp->error_code == -ECANCELED ? 1 : resp->error_code;
lwis_pending_fences_move_all(lwis_dev, transaction, pending_fences, pending_status);
}
save_transaction_to_history(client, info, process_timestamp, process_duration_ns);
+
+ /*
+ * This check needs to be handled only for cases where we are processing
+ * the transaction based on the limit specified in the dts
+ * When the transactions are cancelled or executed in event context
+ * the limit doesn't dictate the number of entries that will be processed
+ */
+ if (check_transaction_limit) {
+ /* 1. If all of the entries are processed for a given transaction then
+ * delete the transaction from the queue and enable emit signals for
+ * pending events and fences
+ * 2. Delete transaction from the process queue after the limit is fulfilled
+ * or there is an error while processing
+ */
+ list_del(&transaction->process_queue_node);
+ }
+
if (info->trigger_event_counter == LWIS_EVENT_COUNTER_EVERY_TIME) {
- /* Only clean the transaction struct for this iteration. The
- * I/O entries are not being freed. */
+ /*
+ *Only clean the transaction struct for this iteration. The
+ * I/O entries are not being freed.
+ */
kfree(transaction->resp);
kfree(transaction);
+ transaction = NULL;
} else {
- lwis_transaction_free(lwis_dev, transaction);
+ lwis_transaction_free(lwis_dev, ptransaction);
}
spin_unlock_irqrestore(&client->transaction_lock, flags);
return ret;
}
-static void cancel_transaction(struct lwis_device *lwis_dev, struct lwis_transaction *transaction,
+static void cancel_transaction(struct lwis_device *lwis_dev, struct lwis_transaction **ptransaction,
int error_code, struct list_head *pending_events,
- struct list_head *pending_fences)
+ struct list_head *pending_fences, bool delete_pending_map_node)
{
int pending_status;
- struct lwis_transaction_info *info = &transaction->info;
+ struct lwis_transaction *transaction = *ptransaction;
+ struct lwis_transaction_info_v2 *info = &transaction->info;
struct lwis_transaction_response_header resp;
resp.id = info->id;
resp.error_code = error_code;
@@ -337,7 +457,7 @@ static void cancel_transaction(struct lwis_device *lwis_dev, struct lwis_transac
resp.completion_index = -1;
if (transaction->is_weak_transaction) {
- lwis_transaction_free(lwis_dev, transaction);
+ lwis_transaction_free(lwis_dev, ptransaction);
return;
}
@@ -350,33 +470,120 @@ static void cancel_transaction(struct lwis_device *lwis_dev, struct lwis_transac
pending_status = error_code == -ECANCELED ? 1 : error_code;
lwis_pending_fences_move_all(lwis_dev, transaction, pending_fences, pending_status);
}
- lwis_transaction_free(lwis_dev, transaction);
+
+ if (delete_pending_map_node) {
+ hash_del(&transaction->pending_map_node);
+ }
+
+ lwis_transaction_free(lwis_dev, ptransaction);
}
void lwis_process_transactions_in_queue(struct lwis_client *client)
{
unsigned long flags;
+ unsigned long flush_flags;
struct list_head *it_tran, *it_tran_tmp;
struct list_head pending_events;
struct list_head pending_fences;
struct lwis_transaction *transaction;
+ 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_events);
INIT_LIST_HEAD(&pending_fences);
spin_lock_irqsave(&client->transaction_lock, flags);
list_for_each_safe (it_tran, it_tran_tmp, &client->transaction_process_queue) {
+ if (!client->is_enabled) {
+ /*
+ * If client is not enabled, then we just need to requeue
+ * the transaction until the client is enabled. This will
+ * ensure that we don't loose the submitted transactions.
+ */
+ if (lwis_transaction_debug) {
+ dev_info(client->lwis_dev->dev,
+ "Client is not ready to process transactions");
+ }
+ spin_unlock_irqrestore(&client->transaction_lock, flags);
+ spin_lock_irqsave(&client->flush_lock, flush_flags);
+ if (client->flush_state == NOT_FLUSHING) {
+ if (i2c_bus_manager) {
+ 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->flush_lock, flush_flags);
+ return;
+ }
+
transaction = list_entry(it_tran, struct lwis_transaction, process_queue_node);
- list_del(&transaction->process_queue_node);
if (transaction->resp->error_code) {
- cancel_transaction(client->lwis_dev, transaction,
+ list_del(&transaction->process_queue_node);
+ cancel_transaction(client->lwis_dev, &transaction,
transaction->resp->error_code, &pending_events,
- &pending_fences);
+ &pending_fences, false);
} else {
spin_unlock_irqrestore(&client->transaction_lock, flags);
- process_transaction(client, transaction, &pending_events, &pending_fences,
- /*skip_err=*/false);
+ process_transaction(client, &transaction, &pending_events, &pending_fences,
+ /*skip_err=*/false, /*check_transaction_limit=*/true);
spin_lock_irqsave(&client->transaction_lock, flags);
+
+ /*
+ * Continue the loop if the transaction is complete and deleted or
+ * if the transaction exists but all the entries are processed
+ */
+ if ((transaction != NULL) &&
+ (transaction->remaining_entries_to_process > 0)) {
+ /*
+ * If the transaction exists and there are entries remaning to be processed,
+ * that would indicate the transaction processing limit has reached for this
+ * device and we stop processing its queue further
+ */
+ if (lwis_transaction_debug) {
+ dev_info(
+ client->lwis_dev->dev,
+ "Transaction processing limit reached, remaining entries to process %d\n",
+ transaction->remaining_entries_to_process);
+ }
+
+ /*
+ * Queue the remaining transaction again on the transaction worker/bus maanger worker
+ * to be processed again later if the client is not flushing
+ * If the client is flushing, cancel the remaining transaction
+ * and delete from the process queue node.
+ */
+ spin_lock_irqsave(&client->flush_lock, flush_flags);
+ if (client->flush_state == NOT_FLUSHING) {
+ if (lwis_transaction_debug) {
+ dev_info(
+ client->lwis_dev->dev,
+ "Client is not flushing, schedule the remaining work");
+ }
+
+ if (i2c_bus_manager) {
+ kthread_queue_work(&i2c_bus_manager->i2c_bus_worker,
+ &client->i2c_work);
+ } else {
+ kthread_queue_work(&client->lwis_dev->transaction_worker,
+ &client->transaction_work);
+ }
+ } else {
+ if (lwis_transaction_debug) {
+ dev_info(
+ client->lwis_dev->dev,
+ "Client is flushing, aborting the remaining transaction");
+ }
+ list_del(&transaction->process_queue_node);
+ cancel_transaction(client->lwis_dev, &transaction,
+ transaction->resp->error_code, &pending_events,
+ &pending_fences, false);
+ }
+ spin_unlock_irqrestore(&client->flush_lock, flush_flags);
+ break;
+ }
}
}
spin_unlock_irqrestore(&client->transaction_lock, flags);
@@ -419,7 +626,8 @@ static void cancel_all_transactions_in_queue_locked(struct lwis_client *client,
transaction =
list_entry(it_tran, struct lwis_transaction, process_queue_node);
list_del(&transaction->process_queue_node);
- cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL, NULL);
+ cancel_transaction(client->lwis_dev, &transaction, -ECANCELED, NULL, NULL,
+ false);
}
}
}
@@ -432,12 +640,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) ==
@@ -447,19 +660,32 @@ int lwis_transaction_client_flush(struct lwis_client *client)
list_for_each_safe (it_tran, it_tran_tmp, &it_evt_list->list) {
transaction = list_entry(it_tran, struct lwis_transaction, event_list_node);
list_del(&transaction->event_list_node);
- cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL, NULL);
+ cancel_transaction(client->lwis_dev, &transaction, -ECANCELED, NULL, NULL,
+ false);
}
hash_del(&it_evt_list->node);
kfree(it_evt_list);
}
hash_for_each_safe (client->pending_transactions, i, tmp, transaction, pending_map_node) {
- cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL, NULL);
- hash_del(&transaction->pending_map_node);
+ cancel_transaction(client->lwis_dev, &transaction, -ECANCELED, NULL, NULL, true);
}
spin_unlock_irqrestore(&client->transaction_lock, flags);
- if (client->lwis_dev->transaction_worker_thread)
- kthread_flush_worker(&client->lwis_dev->transaction_worker);
+ spin_lock_irqsave(&client->flush_lock, flags);
+ client->flush_state = FLUSHING;
+ spin_unlock_irqrestore(&client->flush_lock, flags);
+
+ 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->flush_lock, flags);
+ client->flush_state = NOT_FLUSHING;
+ spin_unlock_irqrestore(&client->flush_lock, flags);
spin_lock_irqsave(&client->transaction_lock, flags);
/* The transaction queue should be empty after canceling all transactions,
@@ -470,36 +696,12 @@ int lwis_transaction_client_flush(struct lwis_client *client)
return 0;
}
-static void print_buffer(struct lwis_client *client, const char *buf, int size)
-{
- char output_string[80], temp_string[8];
- int i;
-
- output_string[0] = '\0';
- for (i = 0; i < size; i++) {
- if (i % 16 == 0 && i != 0) {
- dev_info(client->lwis_dev->dev, "%s\n", output_string);
- output_string[0] = '\0';
- }
- sprintf(temp_string, "%02X ", buf[i]);
- if (i % 4 == 3) {
- strcat(temp_string, " ");
- }
- strcat(output_string, temp_string);
- }
- dev_info(client->lwis_dev->dev, "%s\n", output_string);
-}
-
int lwis_transaction_client_cleanup(struct lwis_client *client)
{
unsigned long flags;
struct list_head *it_tran, *it_tran_tmp;
struct lwis_transaction *transaction;
struct lwis_transaction_event_list *it_evt_list;
- struct list_head pending_events;
- struct lwis_event_entry *event;
-
- INIT_LIST_HEAD(&pending_events);
spin_lock_irqsave(&client->transaction_lock, flags);
/* Perform client defined clean-up routine. */
@@ -521,11 +723,15 @@ int lwis_transaction_client_cleanup(struct lwis_client *client)
}
list_del(&transaction->event_list_node);
if (transaction->resp->error_code || client->lwis_dev->enabled == 0) {
- cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL, NULL);
+ cancel_transaction(client->lwis_dev, &transaction, -ECANCELED, NULL, NULL,
+ false);
} else {
spin_unlock_irqrestore(&client->transaction_lock, flags);
- process_transaction(client, transaction, &pending_events, NULL,
- /*skip_err=*/true);
+ process_transaction(client, &transaction,
+ /*pending_events=*/NULL,
+ /*pending_fences=*/NULL,
+ /*skip_err=*/true,
+ /*check_transaction_limit=*/false);
spin_lock_irqsave(&client->transaction_lock, flags);
}
}
@@ -533,41 +739,6 @@ int lwis_transaction_client_cleanup(struct lwis_client *client)
kfree(it_evt_list);
spin_unlock_irqrestore(&client->transaction_lock, flags);
-
- while (!list_empty(&pending_events)) {
- struct lwis_transaction_response_header *resp;
- struct lwis_io_result *result;
- char *results_buf;
- int i;
-
- event = list_first_entry(&pending_events, struct lwis_event_entry, node);
- /*
- If there is no read result, the payload size will less than
- sizeof(struct lwis_transaction_response_header).
- We can skip it.
- */
- if (event->event_info.payload_size <=
- sizeof(struct lwis_transaction_response_header)) {
- list_del(&event->node);
- kfree(event);
- continue;
- }
- resp = (struct lwis_transaction_response_header *)event->event_info.payload_buffer;
- dev_info(client->lwis_dev->dev, "event_id = %llx, num_entries = %ld\n",
- event->event_info.event_id, resp->num_entries);
- results_buf = (char *)event->event_info.payload_buffer +
- sizeof(struct lwis_transaction_response_header);
- for (i = 0; i < resp->num_entries; i++) {
- dev_info(client->lwis_dev->dev, "entry-%d\n", i);
- result = (struct lwis_io_result *)results_buf;
- print_buffer(client, result->values, result->num_value_bytes);
- results_buf += sizeof(struct lwis_io_result) + result->num_value_bytes;
- }
-
- list_del(&event->node);
- kfree(event);
- }
-
return 0;
}
@@ -602,14 +773,12 @@ int lwis_trigger_event_add_weak_transaction(struct lwis_client *client, int64_t
return -EINVAL;
}
list_add_tail(&weak_transaction->event_list_node, &event_list->list);
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(
client->lwis_dev->dev,
"lwis_fence add weak transaction for event id-%lld triggered transaction id %llu",
event_id, transaction_id);
}
-#endif
return 0;
}
@@ -618,7 +787,7 @@ static int check_transaction_param_locked(struct lwis_client *client,
bool is_level_triggered)
{
struct lwis_device_event_state *event_state;
- struct lwis_transaction_info *info = &transaction->info;
+ struct lwis_transaction_info_v2 *info = &transaction->info;
struct lwis_device *lwis_dev = client->lwis_dev;
if (!client) {
@@ -706,7 +875,7 @@ static int prepare_transaction_fences_locked(struct lwis_client *client,
static int prepare_response_locked(struct lwis_client *client, struct lwis_transaction *transaction)
{
- struct lwis_transaction_info *info = &transaction->info;
+ struct lwis_transaction_info_v2 *info = &transaction->info;
int i;
size_t resp_size;
size_t read_buf_size = 0;
@@ -750,13 +919,19 @@ static int queue_transaction_locked(struct lwis_client *client,
struct lwis_transaction *transaction)
{
struct lwis_transaction_event_list *event_list;
- struct lwis_transaction_info *info = &transaction->info;
+ 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) {
+ kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work);
+ } 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);
@@ -777,7 +952,7 @@ static int queue_transaction_locked(struct lwis_client *client,
int lwis_transaction_submit_locked(struct lwis_client *client, struct lwis_transaction *transaction)
{
int ret;
- struct lwis_transaction_info *info = &transaction->info;
+ struct lwis_transaction_info_v2 *info = &transaction->info;
ret = check_transaction_param_locked(client, transaction,
/*is_level_triggered=*/info->is_level_triggered);
@@ -813,7 +988,7 @@ new_repeating_transaction_iteration(struct lwis_client *client,
"Failed to allocate repeating transaction instance\n");
return NULL;
}
- memcpy(&new_instance->info, &transaction->info, sizeof(struct lwis_transaction_info));
+ memcpy(&new_instance->info, &transaction->info, sizeof(transaction->info));
/* Allocate response buffer */
resp_buf = kmalloc(sizeof(struct lwis_transaction_response_header) +
@@ -828,6 +1003,10 @@ new_repeating_transaction_iteration(struct lwis_client *client,
memcpy(resp_buf, transaction->resp, sizeof(struct lwis_transaction_response_header));
new_instance->resp = (struct lwis_transaction_response_header *)resp_buf;
+ new_instance->is_weak_transaction = transaction->is_weak_transaction;
+ new_instance->remaining_entries_to_process = transaction->info.num_io_entries;
+ new_instance->starting_read_buf = NULL;
+
INIT_LIST_HEAD(&new_instance->event_list_node);
INIT_LIST_HEAD(&new_instance->process_queue_node);
INIT_LIST_HEAD(&new_instance->completion_fence_list);
@@ -838,13 +1017,14 @@ new_repeating_transaction_iteration(struct lwis_client *client,
static void defer_transaction_locked(struct lwis_client *client,
struct lwis_transaction *transaction,
struct list_head *pending_events,
- struct list_head *pending_fences, bool del_event_list_node)
+ struct list_head *pending_fences, bool del_event_list_node,
+ unsigned long* flags)
{
- unsigned long flags = 0;
if (del_event_list_node) {
list_del(&transaction->event_list_node);
}
+
/* I2C read/write cannot be executed in IRQ context */
if (in_irq() && client->lwis_dev->type == DEVICE_TYPE_I2C) {
list_add_tail(&transaction->process_queue_node, &client->transaction_process_queue);
@@ -852,10 +1032,10 @@ static void defer_transaction_locked(struct lwis_client *client,
}
if (transaction->info.run_in_event_context) {
- spin_unlock_irqrestore(&client->transaction_lock, flags);
- process_transaction(client, transaction, pending_events, pending_fences,
- /*skip_err=*/false);
- spin_lock_irqsave(&client->transaction_lock, flags);
+ spin_unlock_irqrestore(&client->transaction_lock, *flags);
+ process_transaction(client, &transaction, pending_events, pending_fences,
+ /*skip_err=*/false, /*check_transaction_limit=*/false);
+ spin_lock_irqsave(&client->transaction_lock, *flags);
} else {
list_add_tail(&transaction->process_queue_node, &client->transaction_process_queue);
}
@@ -871,6 +1051,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);
@@ -889,7 +1071,6 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id,
/* Go through all transactions under the chosen event list. */
list_for_each_safe (it_tran, it_tran_tmp, &event_list->list) {
transaction = list_entry(it_tran, struct lwis_transaction, event_list_node);
-
if (transaction->is_weak_transaction) {
weak_transaction = transaction;
transaction = pending_transaction_peek(client, weak_transaction->id);
@@ -902,18 +1083,17 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id,
if (lwis_event_triggered_condition_ready(transaction, weak_transaction,
event_id, event_counter)) {
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(
client->lwis_dev->dev,
"lwis_fence event id-%lld counter-%lld triggered transaction id %llu",
event_id, event_counter, transaction->info.id);
}
-#endif
hash_del(&transaction->pending_map_node);
defer_transaction_locked(client, transaction, pending_events,
&pending_fences,
- /* del_event_list_node */ false);
+ /* del_event_list_node */ false,
+ &flags);
}
continue;
}
@@ -931,7 +1111,8 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id,
if (trigger_counter == LWIS_EVENT_COUNTER_ON_NEXT_OCCURRENCE ||
trigger_counter == event_counter) {
defer_transaction_locked(client, transaction, pending_events,
- &pending_fences, /* del_event_list_node */ true);
+ &pending_fences, /* del_event_list_node */ true,
+ &flags);
} else if (trigger_counter == LWIS_EVENT_COUNTER_EVERY_TIME) {
new_instance = new_repeating_transaction_iteration(client, transaction);
if (!new_instance) {
@@ -942,14 +1123,19 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id,
continue;
}
defer_transaction_locked(client, new_instance, pending_events,
- &pending_fences, /* del_event_list_node */ false);
+ &pending_fences, /* del_event_list_node */ false,
+ &flags);
}
}
/* 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) {
+ 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->transaction_lock, flags);
@@ -968,6 +1154,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;
@@ -984,32 +1172,28 @@ void lwis_transaction_fence_trigger(struct lwis_client *client, struct lwis_fenc
transaction = pending_transaction_peek(client, transaction_id->id);
if (transaction == NULL) {
/* It means the transaction is already executed or is canceled */
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(
client->lwis_dev->dev,
"lwis_fence fd-%d did NOT triggered transaction id %llu, seems already triggered",
fence->fd, transaction_id->id);
}
-#endif
} else {
if (lwis_fence_triggered_condition_ready(transaction, fence->status)) {
hash_del(&transaction->pending_map_node);
if (fence->status == 0) {
list_add_tail(&transaction->process_queue_node,
&client->transaction_process_queue);
-#ifdef LWIS_FENCE_ENABLED
if (lwis_fence_debug) {
dev_info(
client->lwis_dev->dev,
"lwis_fence fd-%d triggered transaction id %llu",
fence->fd, transaction->info.id);
}
-#endif
} else {
- cancel_transaction(client->lwis_dev, transaction,
+ cancel_transaction(client->lwis_dev, &transaction,
-ECANCELED, &pending_events,
- &pending_fences);
+ &pending_fences, false);
}
}
}
@@ -1019,8 +1203,12 @@ 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) {
+ 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->transaction_lock, flags);
diff --git a/lwis_transaction.h b/lwis_transaction.h
index 67765b5..9f91b2f 100644
--- a/lwis_transaction.h
+++ b/lwis_transaction.h
@@ -18,14 +18,15 @@ struct lwis_device;
struct lwis_client;
struct lwis_fence;
-/* Transaction entry. Each entry belongs to two queues:
+/*
+ * Transaction entry. Each entry belongs to two queues:
* 1) Event list: Transactions are sorted by event IDs. This is to search for
* the appropriate transactions to trigger.
* 2) Process queue: When it's time to process, the transaction will be put
* into a queue.
*/
struct lwis_transaction {
- struct lwis_transaction_info info;
+ struct lwis_transaction_info_v2 info;
struct lwis_transaction_response_header *resp;
struct list_head event_list_node;
struct list_head process_queue_node;
@@ -47,13 +48,23 @@ struct lwis_transaction {
struct list_head completion_fence_list;
/* Precondition fence file pointer */
struct file *precondition_fence_fp;
+ /* If the transaction has more entries to process than the transaction_process_limit
+ for the processing device, then this will save the number of entries that are
+ remaining to be processed after a given transaction process cycle
+ */
+ int remaining_entries_to_process;
+ /* Starting read buffer pointer is set to the last read location when the transaction
+ process limit has reached. During the next run for the transaction, this pointer
+ will be referred to correctly point to the read buffer for the run.
+ */
+ uint8_t *starting_read_buf;
};
/* For debugging purposes, keeps track of the transaction information, as
* well as the time it executes and the time it took to execute.
*/
struct lwis_transaction_history {
- struct lwis_transaction_info info;
+ struct lwis_transaction_info_v2 info;
int64_t process_timestamp;
int64_t process_duration_ns;
};
@@ -81,7 +92,7 @@ void lwis_transaction_fence_trigger(struct lwis_client *client, struct lwis_fenc
int lwis_transaction_cancel(struct lwis_client *client, int64_t id);
-void lwis_transaction_free(struct lwis_device *lwis_dev, struct lwis_transaction *transaction);
+void lwis_transaction_free(struct lwis_device *lwis_dev, struct lwis_transaction **ptransaction);
/* Expects lwis_client->transaction_lock to be acquired before calling
* the following functions. */
diff --git a/lwis_util.c b/lwis_util.c
index 5a6321e..5a440d2 100644
--- a/lwis_util.c
+++ b/lwis_util.c
@@ -123,9 +123,9 @@ int lwis_create_kthread_workers(struct lwis_device *lwis_dev)
scnprintf(t_name, LWIS_MAX_NAME_STRING_LEN, "lwis_t_%s", lwis_dev->name);
kthread_init_worker(&lwis_dev->transaction_worker);
- lwis_dev->transaction_worker_thread = kthread_run(kthread_worker_fn,
- &lwis_dev->transaction_worker, t_name);
- if (IS_ERR(lwis_dev->transaction_worker_thread)) {
+ lwis_dev->transaction_worker_thread =
+ kthread_run(kthread_worker_fn, &lwis_dev->transaction_worker, t_name);
+ if (IS_ERR_OR_NULL(lwis_dev->transaction_worker_thread)) {
dev_err(lwis_dev->dev, "transaction kthread_run failed\n");
return -EINVAL;
}
@@ -133,16 +133,15 @@ int lwis_create_kthread_workers(struct lwis_device *lwis_dev)
return 0;
}
-int lwis_set_kthread_priority(struct lwis_device *lwis_dev, struct task_struct *task,
- u32 priority)
+int lwis_set_kthread_priority(struct lwis_device *lwis_dev, struct task_struct *task, u32 priority)
{
int policy;
struct sched_param param;
int ret;
if (priority >= MAX_PRIO) {
- dev_err(lwis_dev->dev, "transaction_thread_priority(%d) >= Max(%d)",
- priority, MAX_PRIO);
+ dev_err(lwis_dev->dev, "transaction_thread_priority(%d) >= Max(%d)", priority,
+ MAX_PRIO);
return -EINVAL;
}
if (priority < MAX_RT_PRIO) {
@@ -162,4 +161,9 @@ 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 8ce68f4..a313c0c 100644
--- a/lwis_util.h
+++ b/lwis_util.h
@@ -78,7 +78,11 @@ int lwis_create_kthread_workers(struct lwis_device *lwis_dev);
/*
* lwis_set_kthread_priority: Set kthread priority.
*/
-int lwis_set_kthread_priority(struct lwis_device *lwis_dev, struct task_struct *task,
- u32 priority);
+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_
diff --git a/lwis_version.c b/lwis_version.c
index cd1d030..001d6a8 100644
--- a/lwis_version.c
+++ b/lwis_version.c
@@ -40,13 +40,11 @@ void lwis_get_feature_flags(char *buffer, size_t buffer_size)
*/
strlcat(buffer, " cmd-pkt", buffer_size);
-#ifdef LWIS_FENCE_ENABLED
/*
* fence:
* Support fence feature
*/
strlcat(buffer, " fence", buffer_size);
-#endif
strlcat(buffer, "\n", buffer_size);
} \ No newline at end of file
diff --git a/platform/anchorage/lwis_platform_anchorage.c b/platform/anchorage/lwis_platform_anchorage.c
index c01b8c6..36fc0ba 100644
--- a/platform/anchorage/lwis_platform_anchorage.c
+++ b/platform/anchorage/lwis_platform_anchorage.c
@@ -27,6 +27,7 @@
int lwis_platform_probe(struct lwis_device *lwis_dev)
{
struct lwis_platform *platform;
+ int i;
if (!lwis_dev) {
return -ENODEV;
@@ -41,16 +42,19 @@ int lwis_platform_probe(struct lwis_device *lwis_dev)
/* Enable runtime power management for the platform device */
pm_runtime_enable(&lwis_dev->plat_dev->dev);
- lwis_dev->bts_index = BTS_UNSUPPORTED;
/* Only IOREG devices will access DMA resources */
if (lwis_dev->type != DEVICE_TYPE_IOREG) {
return 0;
}
+
/* Register to bts */
- lwis_dev->bts_index = bts_get_bwindex(lwis_dev->name);
- if (lwis_dev->bts_index < 0) {
- dev_err(lwis_dev->dev, "Failed to register to BTS, ret: %d\n", lwis_dev->bts_index);
- lwis_dev->bts_index = BTS_UNSUPPORTED;
+ for (i = 0; i < lwis_dev->bts_block_num; i++) {
+ lwis_dev->bts_indexes[i] = bts_get_bwindex(lwis_dev->bts_block_names[i]);
+ if (lwis_dev->bts_indexes[i] < 0) {
+ dev_err(lwis_dev->dev, "Failed to register to BTS, ret: %d\n",
+ lwis_dev->bts_indexes[i]);
+ lwis_dev->bts_indexes[i] = BTS_UNSUPPORTED;
+ }
}
return 0;
@@ -100,6 +104,18 @@ static int lwis_iommu_fault_handler(struct iommu_fault *fault, void *param)
#endif /* ENABLE_PAGE_FAULT_PANIC */
}
+static bool lwis_device_support_bts(struct lwis_device *lwis_dev)
+{
+ int i;
+
+ for (i = 0; i < lwis_dev->bts_block_num; i++) {
+ if (lwis_dev->bts_indexes[i] != BTS_UNSUPPORTED) {
+ return true;
+ }
+ }
+ return false;
+}
+
int lwis_platform_device_enable(struct lwis_device *lwis_dev)
{
int ret;
@@ -165,7 +181,7 @@ int lwis_platform_device_enable(struct lwis_device *lwis_dev)
}
}
- if (lwis_dev->bts_scenario_name) {
+ if (lwis_device_support_bts(lwis_dev) && lwis_dev->bts_scenario_name) {
lwis_dev->bts_scenario = bts_get_scenindex(lwis_dev->bts_scenario_name);
if (!lwis_dev->bts_scenario) {
dev_err(lwis_dev->dev, "Failed to get default camera BTS scenario.\n");
@@ -190,7 +206,7 @@ int lwis_platform_device_disable(struct lwis_device *lwis_dev)
return -ENODEV;
}
- if (lwis_dev->bts_scenario_name) {
+ if (lwis_device_support_bts(lwis_dev) && lwis_dev->bts_scenario_name) {
bts_del_scenario(lwis_dev->bts_scenario);
}
@@ -209,8 +225,7 @@ int lwis_platform_device_disable(struct lwis_device *lwis_dev)
return pm_runtime_put_sync(&lwis_dev->plat_dev->dev);
}
-int lwis_platform_update_qos(struct lwis_device *lwis_dev, int value,
- int32_t clock_family)
+int lwis_platform_update_qos(struct lwis_device *lwis_dev, int value, int32_t clock_family)
{
struct lwis_platform *platform;
struct exynos_pm_qos_request *qos_req;
@@ -309,26 +324,34 @@ int lwis_platform_update_bts(struct lwis_device *lwis_dev, int block, unsigned i
unsigned int bw_kb_read, unsigned int bw_kb_write,
unsigned int bw_kb_rt)
{
- int ret = 0;
+ int ret = 0, bts_index = lwis_dev->bts_indexes[block];
+ const char *block_name = lwis_dev->bts_block_names[block];
struct bts_bw bts_request;
- if (lwis_dev->bts_index == BTS_UNSUPPORTED) {
- dev_info(lwis_dev->dev, "%s doesn't support bts\n", lwis_dev->name);
- return ret;
+ if (block >= lwis_dev->bts_block_num) {
+ dev_err(lwis_dev->dev, "Invalid block index %d, %s only has %d bts blocks\n", block,
+ lwis_dev->name, lwis_dev->bts_block_num);
+ return -EINVAL;
+ }
+
+ if (bts_index == BTS_UNSUPPORTED) {
+ dev_err(lwis_dev->dev, "%s block %s doesn't support bts\n", lwis_dev->name,
+ block_name);
+ return -EINVAL;
}
bts_request.peak = bw_kb_peak;
bts_request.read = bw_kb_read;
bts_request.write = bw_kb_write;
bts_request.rt = bw_kb_rt;
- ret = bts_update_bw(lwis_dev->bts_index, bts_request);
+ ret = bts_update_bw(bts_index, bts_request);
if (ret < 0) {
dev_err(lwis_dev->dev, "Failed to update bandwidth to bts, ret: %d\n", ret);
} else {
dev_info(
lwis_dev->dev,
- "Updated bandwidth to bts for device %s: peak: %u, read: %u, write: %u, rt: %u\n",
- lwis_dev->name, bw_kb_peak, bw_kb_read, bw_kb_write, bw_kb_rt);
+ "Updated bandwidth to bts for device %s block %s: peak: %u, read: %u, write: %u, rt: %u\n",
+ lwis_dev->name, block_name, bw_kb_peak, bw_kb_read, bw_kb_write, bw_kb_rt);
}
return ret;
}
diff --git a/platform/anchorage/lwis_platform_anchorage_dma.c b/platform/anchorage/lwis_platform_anchorage_dma.c
index 401275c..22b4257 100644
--- a/platform/anchorage/lwis_platform_anchorage_dma.c
+++ b/platform/anchorage/lwis_platform_anchorage_dma.c
@@ -37,8 +37,8 @@ struct dma_buf *lwis_platform_dma_buffer_alloc(size_t len, unsigned int flags)
dmabuf = dma_heap_buffer_alloc(heap, len, O_RDWR, 0);
if (IS_ERR_OR_NULL(dmabuf)) {
- pr_err("DMA-BUF heap failed to alloc %#zx bytes. Error code %lu\n",
- len, PTR_ERR(dmabuf));
+ pr_err("DMA-BUF heap failed to alloc %#zx bytes. Error code %lu\n", len,
+ PTR_ERR(dmabuf));
dmabuf = NULL;
}
diff --git a/platform/busan/lwis_platform_busan.c b/platform/busan/lwis_platform_busan.c
index 2dd543f..19588a9 100644
--- a/platform/busan/lwis_platform_busan.c
+++ b/platform/busan/lwis_platform_busan.c
@@ -24,6 +24,7 @@
int lwis_platform_probe(struct lwis_device *lwis_dev)
{
struct lwis_platform *platform;
+ int i;
if (!lwis_dev) {
return -ENODEV;
@@ -38,16 +39,19 @@ int lwis_platform_probe(struct lwis_device *lwis_dev)
/* Enable runtime power management for the platform device */
pm_runtime_enable(&lwis_dev->plat_dev->dev);
- lwis_dev->bts_index = BTS_UNSUPPORTED;
/* Only IOREG devices will access DMA resources */
if (lwis_dev->type != DEVICE_TYPE_IOREG) {
return 0;
}
+
/* Register to bts */
- lwis_dev->bts_index = bts_get_bwindex(lwis_dev->name);
- if (lwis_dev->bts_index < 0) {
- dev_err(lwis_dev->dev, "Failed to register to BTS, ret: %d\n", lwis_dev->bts_index);
- lwis_dev->bts_index = BTS_UNSUPPORTED;
+ for (i = 0; i < lwis_dev->bts_block_num; i++) {
+ lwis_dev->bts_indexes[i] = bts_get_bwindex(lwis_dev->bts_block_names[i]);
+ if (lwis_dev->bts_indexes[i] < 0) {
+ dev_err(lwis_dev->dev, "Failed to register to BTS, ret: %d\n",
+ lwis_dev->bts_indexes[i]);
+ lwis_dev->bts_indexes[i] = BTS_UNSUPPORTED;
+ }
}
return 0;
@@ -97,6 +101,18 @@ static int lwis_iommu_fault_handler(struct iommu_fault *fault, void *param)
#endif /* ENABLE_PAGE_FAULT_PANIC */
}
+static bool lwis_device_support_bts(struct lwis_device *lwis_dev)
+{
+ int i;
+
+ for (i = 0; i < lwis_dev->bts_block_num; i++) {
+ if (lwis_dev->bts_indexes[i] != BTS_UNSUPPORTED) {
+ return true;
+ }
+ }
+ return false;
+}
+
int lwis_platform_device_enable(struct lwis_device *lwis_dev)
{
int ret;
@@ -151,7 +167,7 @@ int lwis_platform_device_enable(struct lwis_device *lwis_dev)
}
}
- if (lwis_dev->bts_index != BTS_UNSUPPORTED && lwis_dev->bts_scenario_name) {
+ if (lwis_device_support_bts(lwis_dev) && lwis_dev->bts_scenario_name) {
lwis_dev->bts_scenario = bts_get_scenindex(lwis_dev->bts_scenario_name);
if (!lwis_dev->bts_scenario) {
dev_err(lwis_dev->dev, "Failed to get default camera BTS scenario.\n");
@@ -176,7 +192,7 @@ int lwis_platform_device_disable(struct lwis_device *lwis_dev)
return -ENODEV;
}
- if (lwis_dev->bts_index != BTS_UNSUPPORTED && lwis_dev->bts_scenario_name) {
+ if (lwis_device_support_bts(lwis_dev) && lwis_dev->bts_scenario_name) {
bts_del_scenario(lwis_dev->bts_scenario);
}
@@ -285,26 +301,34 @@ int lwis_platform_update_bts(struct lwis_device *lwis_dev, int block, unsigned i
unsigned int bw_kb_read, unsigned int bw_kb_write,
unsigned int bw_kb_rt)
{
- int ret = 0;
+ int ret = 0, bts_index = lwis_dev->bts_indexes[block];
+ const char *block_name = lwis_dev->bts_block_names[block];
struct bts_bw bts_request;
- if (lwis_dev->bts_index == BTS_UNSUPPORTED) {
- dev_info(lwis_dev->dev, "%s doesn't support bts\n", lwis_dev->name);
- return ret;
+ if (block >= lwis_dev->bts_block_num) {
+ dev_err(lwis_dev->dev, "Invalid block index %d, %s only has %d bts blocks\n", block,
+ lwis_dev->name, lwis_dev->bts_block_num);
+ return -EINVAL;
+ }
+
+ if (bts_index == BTS_UNSUPPORTED) {
+ dev_err(lwis_dev->dev, "%s block %s doesn't support bts\n", lwis_dev->name,
+ block_name);
+ return -EINVAL;
}
bts_request.peak = bw_kb_peak;
bts_request.read = bw_kb_read;
bts_request.write = bw_kb_write;
bts_request.rt = bw_kb_rt;
- ret = bts_update_bw(lwis_dev->bts_index, bts_request);
+ ret = bts_update_bw(bts_index, bts_request);
if (ret < 0) {
dev_err(lwis_dev->dev, "Failed to update bandwidth to bts, ret: %d\n", ret);
} else {
dev_info(
lwis_dev->dev,
- "Updated bandwidth to bts for device %s: peak: %u, read: %u, write: %u, rt: %u\n",
- lwis_dev->name, bw_kb_peak, bw_kb_read, bw_kb_write, bw_kb_rt);
+ "Updated bandwidth to bts for device %s block %s: peak: %u, read: %u, write: %u, rt: %u\n",
+ lwis_dev->name, block_name, bw_kb_peak, bw_kb_read, bw_kb_write, bw_kb_rt);
}
return ret;
}