Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4bb6a941 authored by Gaurav Jindal's avatar Gaurav Jindal Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarGaurav Jindal <gjindal@codeaurora.org>
parent 88b74d63
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
// 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>
@@ -117,6 +117,19 @@ static int __cam_fd_ctx_release_dev_in_activated(struct cam_context *ctx,
	return rc;
}

static int __cam_fd_ctx_dump_dev_in_activated(
	struct cam_context *ctx,
	struct cam_dump_req_cmd *cmd)
{
	int rc;

	rc = cam_context_dump_dev_to_hw(ctx, cmd);
	if (rc)
		CAM_ERR(CAM_FD, "Failed to dump device, rc=%d", rc);

	return rc;
}

static int __cam_fd_ctx_flush_dev_in_activated(struct cam_context *ctx,
	struct cam_flush_dev_cmd *cmd)
{
@@ -198,6 +211,7 @@ static struct cam_ctx_ops
			.release_dev = __cam_fd_ctx_release_dev_in_activated,
			.config_dev = __cam_fd_ctx_config_dev_in_activated,
			.flush_dev = __cam_fd_ctx_flush_dev_in_activated,
			.dump_dev = __cam_fd_ctx_dump_dev_in_activated,
		},
		.crm_ops = {},
		.irq_ops = __cam_fd_ctx_handle_irq_in_activated,
+134 −1
Original line number Diff line number Diff line
// 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>
@@ -883,6 +883,7 @@ static int cam_fd_mgr_util_submit_frame(void *priv, void *data)
	hw_device->cur_hw_ctx = hw_ctx;
	hw_device->req_id = frame_req->request_id;
	mutex_unlock(&hw_device->lock);
	frame_req->submit_timestamp = ktime_get();

	rc = cam_fd_mgr_util_put_frame_req(
		&hw_mgr->frame_processing_list, &frame_req);
@@ -1504,6 +1505,137 @@ static int cam_fd_mgr_hw_flush(void *hw_mgr_priv,
	return rc;
}

static int cam_fd_mgr_hw_dump(
	void *hw_mgr_priv,
	void *hw_dump_args)
{
	int                              rc;
	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_fd_hw_mgr            *hw_mgr;
	struct cam_hw_dump_args         *dump_args;
	struct cam_fd_hw_mgr_ctx        *hw_ctx;
	struct cam_fd_device            *hw_device;
	struct cam_fd_hw_dump_args       fd_dump_args;
	struct cam_fd_hw_dump_header    *hdr;
	struct cam_fd_mgr_frame_request *frame_req, *req_temp;

	hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
	dump_args = (struct cam_hw_dump_args *)hw_dump_args;
	if (!hw_mgr || !dump_args) {
		CAM_ERR(CAM_FD, "Invalid args %pK %pK",
			hw_mgr, dump_args);
		return -EINVAL;
	}

	hw_ctx = (struct cam_fd_hw_mgr_ctx *)dump_args->ctxt_to_hw_map;

	if (!hw_ctx) {
		CAM_ERR(CAM_FD, "Invalid ctx");
		return -EINVAL;
	}

	rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);

	if (rc) {
		CAM_ERR(CAM_FD, "Error in getting device %d", rc);
		return rc;
	}

	list_for_each_entry_safe(frame_req, req_temp,
		&hw_mgr->frame_processing_list, list) {
		if (frame_req->request_id == dump_args->request_id)
			goto hw_dump;
	}

	CAM_DBG(CAM_FD, "fd dump cannot find req %llu",
		dump_args->request_id);
	return rc;
hw_dump:
	cur_time = ktime_get();
	diff = ktime_us_delta(frame_req->submit_timestamp, cur_time);
	cur_ts = ktime_to_timespec64(cur_time);
	req_ts = ktime_to_timespec64(frame_req->submit_timestamp);
	if (diff < CAM_FD_RESPONSE_TIME_THRESHOLD) {
		CAM_INFO(CAM_FD, "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);
		return 0;
	}
	CAM_INFO(CAM_FD, "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);
	rc  = cam_mem_get_cpu_buf(dump_args->buf_handle,
		&fd_dump_args.cpu_addr, &fd_dump_args.buf_len);
	if (rc) {
		CAM_ERR(CAM_FD, "Invalid handle %u rc %d",
			dump_args->buf_handle, rc);
		return rc;
	}
	if (fd_dump_args.buf_len <= dump_args->offset) {
		CAM_WARN(CAM_FD, "dump offset overshoot len %zu offset %zu",
			fd_dump_args.buf_len, dump_args->offset);
		return -ENOSPC;
	}
	remain_len = fd_dump_args.buf_len - dump_args->offset;
	min_len =  sizeof(struct cam_fd_hw_dump_header) +
		(CAM_FD_HW_DUMP_NUM_WORDS * sizeof(uint64_t));

	if (remain_len < min_len) {
		CAM_WARN(CAM_FD, "dump buffer exhaust remain %zu min %u",
			remain_len, min_len);
		return -ENOSPC;
	}

	dst = (uint8_t *)fd_dump_args.cpu_addr + dump_args->offset;
	hdr = (struct cam_fd_hw_dump_header *)dst;
	scnprintf(hdr->tag, CAM_FD_HW_DUMP_TAG_MAX_LEN,
		"FD_REQ:");
	hdr->word_size = sizeof(uint64_t);
	addr = (uint64_t *)(dst + sizeof(struct cam_fd_hw_dump_header));
	start = addr;
	*addr++ = frame_req->request_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_fd_hw_dump_header);

	fd_dump_args.request_id = dump_args->request_id;
	fd_dump_args.offset = dump_args->offset;
	if (hw_device->hw_intf->hw_ops.process_cmd) {
		rc = hw_device->hw_intf->hw_ops.process_cmd(
			hw_device->hw_intf->hw_priv,
			CAM_FD_HW_CMD_HW_DUMP,
			&fd_dump_args,
			sizeof(struct
			cam_fd_hw_dump_args));
		if (rc) {
			CAM_ERR(CAM_FD, "Hw Dump cmd fails req %lld rc %d",
				frame_req->request_id, rc);
			return rc;
		}
	}
	CAM_DBG(CAM_FD, "Offset before %zu after %zu",
		dump_args->offset, fd_dump_args.offset);
	dump_args->offset = fd_dump_args.offset;
	return rc;
}

static int cam_fd_mgr_hw_stop(void *hw_mgr_priv, void *mgr_stop_args)
{
	struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
@@ -1944,6 +2076,7 @@ int cam_fd_hw_mgr_init(struct device_node *of_node,
	hw_mgr_intf->hw_write = NULL;
	hw_mgr_intf->hw_close = NULL;
	hw_mgr_intf->hw_flush = cam_fd_mgr_hw_flush;
	hw_mgr_intf->hw_dump = cam_fd_mgr_hw_dump;

	return rc;

+9 −1
Original line number Diff line number Diff line
/* 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_FD_HW_MGR_H_
@@ -21,6 +21,12 @@
#define CAM_FD_HW_MAX            1
#define CAM_FD_WORKQ_NUM_TASK    10

/*
 * Response time threshold in ms beyond which a request is not expected to be
 * with FD hw
 */
#define CAM_FD_RESPONSE_TIME_THRESHOLD   100000

struct cam_fd_hw_mgr;

/**
@@ -100,6 +106,7 @@ struct cam_fd_device {
 * @hw_update_entries     : HW update entries corresponding to this request
 *                          which needs to be submitted to HW through CDM
 * @num_hw_update_entries : Number of HW update entries
 * @submit_timestamp      : Time stamp for submit req with hw
 */
struct cam_fd_mgr_frame_request {
	struct list_head               list;
@@ -108,6 +115,7 @@ struct cam_fd_mgr_frame_request {
	struct cam_fd_hw_req_private   hw_req_private;
	struct cam_hw_update_entry     hw_update_entries[CAM_FD_MAX_HW_ENTRIES];
	uint32_t                       num_hw_update_entries;
	ktime_t                        submit_timestamp;
};

/**
+80 −1
Original line number Diff line number Diff line
// 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.
 */

#include "cam_fd_hw_core.h"
@@ -516,6 +516,80 @@ static int cam_fd_hw_util_processcmd_frame_done(struct cam_hw_info *fd_hw,
	return 0;
}

static int cam_fd_hw_util_processcmd_hw_dump(
	struct cam_hw_info *fd_hw,
	void               *args)
{
	int                            i, j;
	uint8_t                       *dst;
	uint32_t                      *addr, *start;
	uint32_t                       num_reg, min_len;
	uint64_t                       remain_len;
	struct cam_hw_soc_info        *soc_info;
	struct cam_fd_hw_dump_header  *hdr;
	struct cam_fd_hw_dump_args    *dump_args;

	if (!fd_hw || !args) {
		CAM_ERR(CAM_FD, "Invalid args %pK %pK",
			fd_hw, args);
		return -EINVAL;
	}

	mutex_lock(&fd_hw->hw_mutex);

	if (fd_hw->hw_state == CAM_HW_STATE_POWER_DOWN) {
		CAM_INFO(CAM_FD, "power off state");
		mutex_unlock(&fd_hw->hw_mutex);
		return 0;
	}

	dump_args = (struct cam_fd_hw_dump_args *)args;
	soc_info = &fd_hw->soc_info;

	if (dump_args->buf_len <= dump_args->offset) {
		CAM_WARN(CAM_FD, "dump offset overshoot len %zu offset %zu",
			dump_args->buf_len, dump_args->offset);
		mutex_unlock(&fd_hw->hw_mutex);
		return -ENOSPC;
	}

	remain_len = dump_args->buf_len - dump_args->offset;
	min_len =  sizeof(struct cam_fd_hw_dump_header) +
		    soc_info->reg_map[0].size + sizeof(uint32_t);

	if (remain_len < min_len) {
		CAM_WARN(CAM_FD, "dump buffer exhaust remain %zu min %u",
			remain_len, min_len);
		mutex_unlock(&fd_hw->hw_mutex);
		return -ENOSPC;
	}

	dst = (uint8_t *)dump_args->cpu_addr + dump_args->offset;
	hdr = (struct cam_fd_hw_dump_header *)dst;
	scnprintf(hdr->tag, CAM_FD_HW_DUMP_TAG_MAX_LEN,
		"FD_REG:");
	hdr->word_size = sizeof(uint32_t);
	addr = (uint32_t *)(dst + sizeof(struct cam_fd_hw_dump_header));
	start = addr;
	*addr++ = soc_info->index;

	for (j = 0; j < soc_info->num_reg_map; j++) {
		num_reg = soc_info->reg_map[j].size/4;
		for (i = 0; i < num_reg; i++) {
			*addr++ = soc_info->mem_block[j]->start + i*4;
			*addr++ = cam_io_r(soc_info->reg_map[j].mem_base +
				(i*4));
		}
	}

	mutex_unlock(&fd_hw->hw_mutex);
	hdr->size = hdr->word_size * (addr - start);
	dump_args->offset += hdr->size +
		sizeof(struct cam_fd_hw_dump_header);
	CAM_DBG(CAM_FD, "%zu", dump_args->offset);
	return 0;
}

irqreturn_t cam_fd_hw_irq(int irq_num, void *data)
{
	struct cam_hw_info *fd_hw = (struct cam_hw_info *)data;
@@ -1156,6 +1230,11 @@ int cam_fd_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
			cmd_frame_results);
		break;
	}
	case CAM_FD_HW_CMD_HW_DUMP: {
		rc = cam_fd_hw_util_processcmd_hw_dump(fd_hw,
			cmd_args);
		break;
	}
	default:
		break;
	}
+33 −1
Original line number Diff line number Diff line
/* 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_FD_HW_INTF_H_
@@ -24,6 +24,8 @@

#define CAM_FD_MAX_IO_BUFFERS        5
#define CAM_FD_MAX_HW_ENTRIES        5
#define CAM_FD_HW_DUMP_TAG_MAX_LEN   32
#define CAM_FD_HW_DUMP_NUM_WORDS     5

/**
 * enum cam_fd_hw_type - Enum for FD HW type
@@ -81,12 +83,14 @@ enum cam_fd_hw_irq_type {
 * @CAM_FD_HW_CMD_UPDATE_SOC        : Command to process soc update
 * @CAM_FD_HW_CMD_REGISTER_CALLBACK : Command to set hw mgr callback
 * @CAM_FD_HW_CMD_MAX               : Indicates max cmd
 * @CAM_FD_HW_CMD_HW_DUMP           : Command to dump fd hw information
 */
enum cam_fd_hw_cmd_type {
	CAM_FD_HW_CMD_PRESTART,
	CAM_FD_HW_CMD_FRAME_DONE,
	CAM_FD_HW_CMD_UPDATE_SOC,
	CAM_FD_HW_CMD_REGISTER_CALLBACK,
	CAM_FD_HW_CMD_HW_DUMP,
	CAM_FD_HW_CMD_MAX,
};

@@ -279,4 +283,32 @@ struct cam_fd_hw_cmd_set_irq_cb {
	void *data;
};

/**
 * struct cam_fd_hw_dump_args : Args for dump request
 *
 * @request_id   : Issue request id
 * @offset       : offset of the buffer
 * @buf_len      : Length of target buffer
 * @cpu_addr     : start address of the target buffer
 */
struct cam_fd_hw_dump_args {
	uint64_t  request_id;
	size_t    offset;
	size_t    buf_len;
	uintptr_t cpu_addr;
};

/**
 * struct cam_fd_hw_dump_header : fd hw dump header
 *
 * @tag       : fd hw dump header tag
 * @size      : Size of data
 * @word_size : size of each word
 */
struct cam_fd_hw_dump_header {
	uint8_t  tag[CAM_FD_HW_DUMP_TAG_MAX_LEN];
	uint64_t size;
	uint32_t word_size;
};

#endif /* _CAM_FD_HW_INTF_H_ */
Loading