diff options
author | Will McVicker <willmcvicker@google.com> | 2024-04-15 11:14:18 -0700 |
---|---|---|
committer | Will McVicker <willmcvicker@google.com> | 2024-04-15 11:19:24 -0700 |
commit | 951a7837ea2391035fe384a81b14748d093926b6 (patch) | |
tree | 821286663e7ba94b7dd5db4b6da28b1edb3cbf19 | |
parent | e5d0b690ab61ca932240b5a77b05214397112a7a (diff) | |
parent | e93b073b2f1f5f9a0c479714efceb9ac78c027cd (diff) | |
download | lwis-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>
52 files changed, 3210 insertions, 2827 deletions
@@ -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 *)¤t_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 *)¤t_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, ®ister_io_history_fops); + dbg_reg_io_file = debugfs_create_file("io_history", 0444, dbg_dir, lwis_dev, + ®ister_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; @@ -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; +} @@ -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_ */ @@ -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, ®_value, lwis_dev); + ret = i2c_read(i2c, entry->mod.offset, ®_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; @@ -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, ×tamp, 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 *)¤t_setting, (void __user *)msg, - sizeof(struct lwis_qos_setting))) { + if (copy_from_user((void *)¤t_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, ¤t_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 *)¤t_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; @@ -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; } |