Files
android_kernel_samsung_sm86…/drivers/cam_utils/cam_common_util.c
Gaurav Jindal d266ac8813 msm: camera: common: Add camera minidump changes
Based on targets, in case of device crash, in place
of complete memory dump, mini dump can be extracted.
This commit adds changes for basic framework
to interact with base kernel for mini dump.
This commit also adds changes for ISP, CRM, SMMU and
Memory Manager.

CRs-Fixed: 2993116
Change-Id: I02620bd79ee2f84847381509a5eb030ffb1ca9d4
Signed-off-by: Gaurav Jindal <gjindal@codeaurora.org>
2021-08-22 12:00:40 -07:00

253 lines
6.1 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/string.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/completion.h>
#include <linux/module.h>
#include <linux/iopoll.h>
#include <linux/moduleparam.h>
#include "cam_common_util.h"
#include "cam_debug_util.h"
#if IS_REACHABLE(CONFIG_QCOM_VA_MINIDUMP)
#include <soc/qcom/minidump.h>
static struct cam_common_mini_dump_dev_info g_minidump_dev_info;
#endif
static uint timeout_multiplier = 1;
module_param(timeout_multiplier, uint, 0644);
int cam_common_util_get_string_index(const char **strings,
uint32_t num_strings, const char *matching_string, uint32_t *index)
{
int i;
for (i = 0; i < num_strings; i++) {
if (strnstr(strings[i], matching_string, strlen(strings[i]))) {
CAM_DBG(CAM_UTIL, "matched %s : %d\n",
matching_string, i);
*index = i;
return 0;
}
}
return -EINVAL;
}
uint32_t cam_common_util_remove_duplicate_arr(int32_t *arr, uint32_t num)
{
int i, j;
uint32_t wr_idx = 1;
if (!arr) {
CAM_ERR(CAM_UTIL, "Null input array");
return 0;
}
for (i = 1; i < num; i++) {
for (j = 0; j < wr_idx ; j++) {
if (arr[i] == arr[j])
break;
}
if (j == wr_idx)
arr[wr_idx++] = arr[i];
}
return wr_idx;
}
unsigned long cam_common_wait_for_completion_timeout(
struct completion *complete,
unsigned long timeout_jiffies)
{
unsigned long wait_jiffies;
unsigned long rem_jiffies;
if (!complete) {
CAM_ERR(CAM_UTIL, "Null complete pointer");
return 0;
}
if (timeout_multiplier < 1)
timeout_multiplier = 1;
wait_jiffies = timeout_jiffies * timeout_multiplier;
rem_jiffies = wait_for_completion_timeout(
complete, wait_jiffies);
return rem_jiffies;
}
int cam_common_read_poll_timeout(
void __iomem *addr,
unsigned long delay,
unsigned long timeout,
uint32_t mask,
uint32_t check_val,
uint32_t *status)
{
unsigned long wait_time_us;
int rc = -EINVAL;
if (!addr || !status) {
CAM_ERR(CAM_UTIL, "Invalid param addr: %pK status: %pK",
addr, status);
return rc;
}
if (timeout_multiplier < 1)
timeout_multiplier = 1;
wait_time_us = timeout * timeout_multiplier;
rc = readl_poll_timeout(addr,
*status, (*status & mask) == check_val, delay, wait_time_us);
return rc;
}
int cam_common_modify_timer(struct timer_list *timer, int32_t timeout_val)
{
if (!timer) {
CAM_ERR(CAM_UTIL, "Invalid reference to system timer");
return -EINVAL;
}
if (timeout_multiplier < 1)
timeout_multiplier = 1;
CAM_DBG(CAM_UTIL, "Starting timer to fire in %d ms. (jiffies=%lu)\n",
(timeout_val * timeout_multiplier), jiffies);
mod_timer(timer,
(jiffies + msecs_to_jiffies(timeout_val * timeout_multiplier)));
return 0;
}
void cam_common_util_thread_switch_delay_detect(
const char *token, ktime_t scheduled_time, uint32_t threshold)
{
uint64_t diff;
ktime_t cur_time;
struct timespec64 cur_ts;
struct timespec64 scheduled_ts;
cur_time = ktime_get();
diff = ktime_ms_delta(cur_time, scheduled_time);
if (diff > threshold) {
scheduled_ts = ktime_to_timespec64(scheduled_time);
cur_ts = ktime_to_timespec64(cur_time);
CAM_WARN_RATE_LIMIT_CUSTOM(CAM_UTIL, 1, 1,
"%s delay detected %ld:%06ld cur %ld:%06ld diff %ld: threshold %d",
token, scheduled_ts.tv_sec,
scheduled_ts.tv_nsec/NSEC_PER_USEC,
cur_ts.tv_sec, cur_ts.tv_nsec/NSEC_PER_USEC,
diff, threshold);
}
}
#if IS_REACHABLE(CONFIG_QCOM_VA_MINIDUMP)
static void cam_common_mini_dump_handler(void *dst, unsigned long len)
{
int i = 0;
uint8_t *waddr;
unsigned long bytes_written = 0;
unsigned long remain_len = len;
struct cam_common_mini_dump_data *md;
if (len < sizeof(*md)) {
CAM_WARN(CAM_UTIL, "Insufficient len %lu", len);
return;
}
md = (struct cam_common_mini_dump_data *)dst;
waddr = (uint8_t *)md + sizeof(*md);
remain_len -= sizeof(*md);
for (i = 0; i < CAM_COMMON_MINI_DUMP_DEV_NUM; i++) {
if (!g_minidump_dev_info.dump_cb[i])
continue;
memcpy(md->name[i], g_minidump_dev_info.name[i],
strlen(g_minidump_dev_info.name[i]));
md->waddr[i] = (void *)waddr;
bytes_written = g_minidump_dev_info.dump_cb[i](
(void *)waddr, remain_len);
md->size[i] = bytes_written;
if (bytes_written >= len) {
CAM_WARN(CAM_UTIL, "No more space to dump");
goto nomem;
}
remain_len -= bytes_written;
waddr += bytes_written;
}
return;
nomem:
for (; i >=0; i--)
CAM_WARN(CAM_UTIL, "%s: Dumped len: %lu", md->name[i], md->size[i]);
}
static int cam_common_md_notify_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct va_md_entry cbentry;
int rc = 0;
cbentry.vaddr = 0x0;
strlcpy(cbentry.owner, "Camera", sizeof(cbentry.owner));
cbentry.size = CAM_COMMON_MINI_DUMP_SIZE;
cbentry.cb = cam_common_mini_dump_handler;
rc = qcom_va_md_add_region(&cbentry);
if (rc) {
CAM_ERR(CAM_UTIL, "Va Region add falied %d", rc);
return NOTIFY_STOP_MASK;
}
return NOTIFY_OK;
}
static struct notifier_block cam_common_md_notify_blk = {
.notifier_call = cam_common_md_notify_handler,
.priority = INT_MAX,
};
int cam_common_register_mini_dump_cb(
cam_common_mini_dump_cb mini_dump_cb,
uint8_t *dev_name)
{
int rc = 0;
if (g_minidump_dev_info.num_devs >= CAM_COMMON_MINI_DUMP_DEV_NUM) {
CAM_ERR(CAM_UTIL, "No free index available");
return -EINVAL;
}
if (!mini_dump_cb || !dev_name) {
CAM_ERR(CAM_UTIL, "Invalid params");
return -EINVAL;
}
g_minidump_dev_info.dump_cb[g_minidump_dev_info.num_devs] =
mini_dump_cb;
scnprintf(g_minidump_dev_info.name[g_minidump_dev_info.num_devs],
CAM_COMMON_MINI_DUMP_DEV_NAME_LEN, dev_name);
g_minidump_dev_info.num_devs++;
if (!g_minidump_dev_info.is_registered) {
rc = qcom_va_md_register("Camera", &cam_common_md_notify_blk);
if (rc) {
CAM_ERR(CAM_UTIL, "Camera VA minidump register failed");
goto end;
}
g_minidump_dev_info.is_registered = true;
}
end:
return rc;
}
#endif