msm: camera: common: LDAR dump NRT devices information

When user space detects an error or does not receive
response for a request, Lets do a reset(LDAR) is triggered.
Before LDAR, user space sends flush command to the
kernel space.
In order to debug the cause for this situation and to dump
the information, user space sends a dump command to the
kernel space before sending flush.
As a part of this command, it passes the culprit request id
and the buffer into which the information can be dumped.
Kernel space traverses across the drivers and find the culprit hw
and dumps the relevant information in the buffer.
This data is written to a file for offline processing.
This commit dumps the information for NRT devices; JPEG,
LRME, FD and ICP.
For LRME, FD, JPEG context information is dumped.
FOR ICP, fw image is dumped.

Change-Id: I123e9b8289521a40d88156ba9bd0003ad9602f01
CRs-Fixed: 2602180
Signed-off-by: Gaurav Jindal <gjindal@codeaurora.org>
This commit is contained in:
Gaurav Jindal
2019-10-28 19:54:39 +05:30
committed by Venkat Chinta
parent 8d05aad1b0
commit dbc2111c4f
23 changed files with 1032 additions and 19 deletions

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -86,6 +86,21 @@ static int __cam_lrme_ctx_config_dev_in_activated(struct cam_context *ctx,
return rc;
}
static int __cam_lrme_ctx_dump_dev_in_activated(
struct cam_context *ctx,
struct cam_dump_req_cmd *cmd)
{
int rc = 0;
CAM_DBG(CAM_LRME, "Enter ctx %d", ctx->ctx_id);
rc = cam_context_dump_dev_to_hw(ctx, cmd);
if (rc)
CAM_ERR(CAM_LRME, "Failed to dump device");
return rc;
}
static int __cam_lrme_ctx_flush_dev_in_activated(struct cam_context *ctx,
struct cam_flush_dev_cmd *cmd)
{
@@ -199,6 +214,7 @@ static struct cam_ctx_ops
.release_dev = __cam_lrme_ctx_release_dev_in_activated,
.stop_dev = __cam_lrme_ctx_stop_dev_in_activated,
.flush_dev = __cam_lrme_ctx_flush_dev_in_activated,
.dump_dev = __cam_lrme_ctx_dump_dev_in_activated,
},
.crm_ops = {},
.irq_ops = __cam_lrme_ctx_handle_irq_in_activated,

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -649,6 +649,50 @@ static int cam_lrme_mgr_hw_release(void *hw_mgr_priv, void *hw_release_args)
return rc;
}
static int cam_lrme_mgr_hw_dump(void *hw_mgr_priv, void *hw_dump_args)
{
struct cam_hw_dump_args *dump_args = hw_dump_args;
struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
struct cam_lrme_device *hw_device;
int rc = 0;
uint32_t device_index;
struct cam_lrme_hw_dump_args lrme_dump_args;
device_index = CAM_LRME_DECODE_DEVICE_INDEX(dump_args->ctxt_to_hw_map);
if (device_index >= hw_mgr->device_count) {
CAM_ERR(CAM_LRME, "Invalid device index %d", device_index);
return -EPERM;
}
CAM_DBG(CAM_LRME, "Start device index %d", device_index);
rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device);
if (rc) {
CAM_ERR(CAM_LRME, "Failed to get hw device");
return rc;
}
rc = cam_mem_get_cpu_buf(dump_args->buf_handle,
&lrme_dump_args.cpu_addr,
&lrme_dump_args.buf_len);
if (rc) {
CAM_ERR(CAM_LRME, "Invalid handle %u rc %d",
dump_args->buf_handle, rc);
return rc;
}
lrme_dump_args.offset = dump_args->offset;
lrme_dump_args.request_id = dump_args->request_id;
rc = hw_device->hw_intf.hw_ops.process_cmd(
hw_device->hw_intf.hw_priv,
CAM_LRME_HW_CMD_DUMP,
&lrme_dump_args,
sizeof(struct cam_lrme_hw_dump_args));
CAM_DBG(CAM_LRME, "Offset before %zu after %zu",
dump_args->offset, lrme_dump_args.offset);
dump_args->offset = lrme_dump_args.offset;
return rc;
}
static int cam_lrme_mgr_hw_flush(void *hw_mgr_priv, void *hw_flush_args)
{ int rc = 0, i;
struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
@@ -1147,6 +1191,7 @@ int cam_lrme_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf,
hw_mgr_intf->hw_flush = cam_lrme_mgr_hw_flush;
g_lrme_hw_mgr.event_cb = cam_lrme_dev_buf_done_cb;
hw_mgr_intf->hw_dump = cam_lrme_mgr_hw_dump;
cam_lrme_mgr_create_debugfs_entry();

View File

@@ -3,6 +3,7 @@
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/timer.h>
#include "cam_lrme_hw_core.h"
#include "cam_lrme_hw_soc.h"
#include "cam_smmu_api.h"
@@ -21,6 +22,159 @@ static void cam_lrme_dump_registers(void __iomem *base)
cam_io_dump(base, 0x900, (0x928 - 0x900) / 0x4);
}
static int cam_lrme_dump_regs_to_buf(
struct cam_lrme_frame_request *req,
struct cam_hw_info *lrme_hw,
struct cam_lrme_hw_dump_args *dump_args)
{
int i;
uint8_t *dst;
uint32_t *addr, *start;
uint32_t num_reg, min_len;
size_t remain_len;
struct cam_hw_soc_info *soc_info;
struct cam_lrme_hw_dump_header *hdr;
if (!lrme_hw || !req || !dump_args) {
CAM_ERR(CAM_LRME, "Invalid params %pK, %pK, %pK",
lrme_hw, req, dump_args);
return -EINVAL;
}
soc_info = &lrme_hw->soc_info;
if (dump_args->buf_len <= dump_args->offset) {
CAM_WARN(CAM_LRME, "dump buffer overshoot len %zu offset %zu",
dump_args->buf_len, dump_args->offset);
return -ENOSPC;
}
remain_len = dump_args->buf_len - dump_args->offset;
min_len = sizeof(struct cam_lrme_hw_dump_header) +
soc_info->reg_map[0].size + sizeof(uint32_t);
if (remain_len < min_len) {
CAM_WARN(CAM_LRME, "dump buffer exhaust remain %zu min %u",
remain_len, min_len);
return -ENOSPC;
}
dst = (uint8_t *)dump_args->cpu_addr + dump_args->offset;
hdr = (struct cam_lrme_hw_dump_header *)dst;
scnprintf(hdr->tag, CAM_LRME_HW_DUMP_TAG_MAX_LEN,
"LRME_REG:");
hdr->word_size = sizeof(uint32_t);
addr = (uint32_t *)(dst + sizeof(struct cam_lrme_hw_dump_header));
start = addr;
*addr++ = soc_info->index;
num_reg = soc_info->reg_map[0].size/4;
for (i = 0; i < num_reg; i++) {
*addr++ = soc_info->mem_block[0]->start + (i*4);
*addr++ = cam_io_r(soc_info->reg_map[0].mem_base + (i*4));
}
hdr->size = hdr->word_size * (addr - start);
dump_args->offset += hdr->size +
sizeof(struct cam_lrme_hw_dump_header);
CAM_DBG(CAM_LRME, "offset %zu", dump_args->offset);
return 0;
}
static int cam_lrme_hw_dump(
struct cam_hw_info *lrme_hw,
struct cam_lrme_hw_dump_args *dump_args)
{
uint8_t *dst;
ktime_t cur_time;
size_t remain_len;
uint32_t min_len;
uint64_t diff;
uint64_t *addr, *start;
struct timespec64 cur_ts;
struct timespec64 req_ts;
struct cam_lrme_core *lrme_core;
struct cam_lrme_frame_request *req = NULL;
struct cam_lrme_hw_dump_header *hdr;
mutex_lock(&lrme_hw->hw_mutex);
if (lrme_hw->hw_state == CAM_HW_STATE_POWER_DOWN) {
CAM_DBG(CAM_LRME, "LRME HW is in off state");
mutex_unlock(&lrme_hw->hw_mutex);
return 0;
}
lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
if (lrme_core->req_submit &&
lrme_core->req_submit->req_id == dump_args->request_id)
req = lrme_core->req_submit;
else if (lrme_core->req_proc &&
lrme_core->req_proc->req_id == dump_args->request_id)
req = lrme_core->req_proc;
if (!req) {
CAM_DBG(CAM_LRME, "LRME req %lld not with hw",
dump_args->request_id);
mutex_unlock(&lrme_hw->hw_mutex);
return 0;
}
cur_time = ktime_get();
diff = ktime_us_delta(req->submit_timestamp, cur_time);
cur_ts = ktime_to_timespec64(cur_time);
req_ts = ktime_to_timespec64(req->submit_timestamp);
if (diff < CAM_LRME_RESPONSE_TIME_THRESHOLD) {
CAM_INFO(CAM_LRME, "No error req %lld %ld:%06ld %ld:%06ld",
dump_args->request_id,
req_ts.tv_sec,
req_ts.tv_nsec/NSEC_PER_USEC,
cur_ts.tv_sec,
cur_ts.tv_nsec/NSEC_PER_USEC);
mutex_unlock(&lrme_hw->hw_mutex);
return 0;
}
CAM_INFO(CAM_LRME, "Error req %lld %ld:%06ld %ld:%06ld",
dump_args->request_id,
req_ts.tv_sec,
req_ts.tv_nsec/NSEC_PER_USEC,
cur_ts.tv_sec,
cur_ts.tv_nsec/NSEC_PER_USEC);
if (dump_args->buf_len <= dump_args->offset) {
CAM_WARN(CAM_LRME, "dump buffer overshoot len %zu offset %zu",
dump_args->buf_len, dump_args->offset);
mutex_unlock(&lrme_hw->hw_mutex);
return 0;
}
remain_len = dump_args->buf_len - dump_args->offset;
min_len = sizeof(struct cam_lrme_hw_dump_header) +
(CAM_LRME_HW_DUMP_NUM_WORDS * sizeof(uint64_t));
if (remain_len < min_len) {
CAM_WARN(CAM_LRME, "dump buffer exhaust remain %zu min %u",
remain_len, min_len);
mutex_unlock(&lrme_hw->hw_mutex);
return 0;
}
dst = (uint8_t *)dump_args->cpu_addr + dump_args->offset;
hdr = (struct cam_lrme_hw_dump_header *)dst;
scnprintf(hdr->tag, CAM_LRME_HW_DUMP_TAG_MAX_LEN,
"LRME_REQ:");
hdr->word_size = sizeof(uint64_t);
addr = (uint64_t *)(dst + sizeof(struct cam_lrme_hw_dump_header));
start = addr;
*addr++ = req->req_id;
*addr++ = req_ts.tv_sec;
*addr++ = req_ts.tv_nsec/NSEC_PER_USEC;
*addr++ = cur_ts.tv_sec;
*addr++ = cur_ts.tv_nsec/NSEC_PER_USEC;
hdr->size = hdr->word_size * (addr - start);
dump_args->offset += hdr->size +
sizeof(struct cam_lrme_hw_dump_header);
cam_lrme_dump_regs_to_buf(req, lrme_hw, dump_args);
mutex_unlock(&lrme_hw->hw_mutex);
return 0;
}
static void cam_lrme_cdm_write_reg_val_pair(uint32_t *buffer,
uint32_t *index, uint32_t reg_offset, uint32_t reg_value)
{
@@ -959,6 +1113,8 @@ int cam_lrme_hw_submit_req(void *hw_priv, void *hw_submit_args,
goto error;
}
frame_req->submit_timestamp = ktime_get();
switch (lrme_core->state) {
case CAM_LRME_CORE_STATE_PROCESSING:
lrme_core->state = CAM_LRME_CORE_STATE_REQ_PROC_PEND;
@@ -1268,6 +1424,12 @@ int cam_lrme_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
break;
}
case CAM_LRME_HW_CMD_DUMP: {
struct cam_lrme_hw_dump_args *dump_args =
(struct cam_lrme_hw_dump_args *)cmd_args;
rc = cam_lrme_hw_dump(lrme_hw, dump_args);
break;
}
default:
break;
}

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_LRME_HW_CORE_H_
@@ -35,6 +35,10 @@
#define CAM_LRME_MAX_REG_PAIR_NUM 60
#define CAM_LRME_RESPONSE_TIME_THRESHOLD 100000
#define CAM_LRME_HW_DUMP_TAG_MAX_LEN 32
#define CAM_LRME_HW_DUMP_NUM_WORDS 5
/**
* enum cam_lrme_irq_set
*
@@ -432,6 +436,20 @@ struct cam_lrme_hw_info {
struct cam_lrme_titan_reg titan_reg;
};
/**
* struct cam_lrme_hw_dump_header : LRME hw dump header
*
* @tag : LRME hw dump header tag
* @size : Size of data
* @word_size : size of each word
*/
struct cam_lrme_hw_dump_header {
uint8_t tag[CAM_LRME_HW_DUMP_TAG_MAX_LEN];
uint64_t size;
uint32_t word_size;
};
int cam_lrme_hw_process_irq(void *priv, void *data);
int cam_lrme_hw_submit_req(void *hw_priv, void *hw_submit_args,
uint32_t arg_size);

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_LRME_HW_INTF_H_
@@ -59,12 +59,14 @@ enum cam_lrme_cb_type {
* @CAM_LRME_HW_CMD_REGISTER_CB : register HW manager callback
* @CAM_LRME_HW_CMD_SUBMIT : Submit frame to HW
* @CAM_LRME_HW_CMD_DUMP_REGISTER : dump register values
* @CAM_LRME_HW_CMD_DUMP : dump register values to buffer
*/
enum cam_lrme_hw_cmd_type {
CAM_LRME_HW_CMD_PREPARE_HW_UPDATE,
CAM_LRME_HW_CMD_REGISTER_CB,
CAM_LRME_HW_CMD_SUBMIT,
CAM_LRME_HW_CMD_DUMP_REGISTER,
CAM_LRME_HW_CMD_DUMP,
};
/**
@@ -87,6 +89,7 @@ enum cam_lrme_hw_reset_type {
* @hw_device : Pointer to HW device
* @hw_update_entries : List of hw_update_entries
* @num_hw_update_entries : number of hw_update_entries
* @submit_timestamp : timestamp of submitting request with hw
*/
struct cam_lrme_frame_request {
struct list_head frame_list;
@@ -95,6 +98,7 @@ struct cam_lrme_frame_request {
struct cam_lrme_device *hw_device;
struct cam_hw_update_entry hw_update_entries[CAM_LRME_MAX_HW_ENTRIES];
uint32_t num_hw_update_entries;
ktime_t submit_timestamp;
};
/**
@@ -192,6 +196,21 @@ struct cam_lrme_hw_submit_args {
struct cam_lrme_frame_request *frame_req;
};
/**
* struct cam_lrme_hw_dump_args : Args for dump request
*
* @request_id : Issue request id
* @cpu_addr : start address of the target buffer
* @offset : offset of the buffer
* @buf_len : Length of target buffer
*/
struct cam_lrme_hw_dump_args {
uint64_t request_id;
uintptr_t cpu_addr;
size_t offset;
size_t buf_len;
};
/**
* @brief : API to register LRME hw to platform framework.
* @return struct platform_device pointer on on success, or ERR_PTR() on error.
@@ -202,4 +221,6 @@ int cam_lrme_hw_init_module(void);
* @brief : API to remove LRME Hw from platform framework.
*/
void cam_lrme_hw_exit_module(void);
#endif /* _CAM_LRME_HW_INTF_H_ */