summary refs log tree commit diff
path: root/drivers/net/wireless/ath/ath11k/coredump.c
blob: 32d37dda570c3b1054265d1daf84e395285a8ec4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
 * Copyright (c) 2020 The Linux Foundation. All rights reserved.
 */

#include <linux/devcoredump.h>
#include "coredump.h"
#include "pci.h"
#include "debug.h"

static struct ath11k_dump_file_data *
ath11k_coredump_build(struct ath11k_mhi_fw_crash_data *crash_data,
		      struct fw_remote_crash_data *remote_crash_data,
		      struct register_crash_data *reg_crash_data)
{
	struct ath11k_dump_file_data *dump_data;
	struct ath11k_tlv_dump_data *dump_tlv;
	size_t hdr_len = sizeof(*dump_data);
	size_t len, sofar = 0;
	unsigned char *buf;
	struct timespec64 timestamp;

	len = hdr_len;

	len += sizeof(*dump_tlv) + crash_data->paging_dump_buf_len;
	len += sizeof(*dump_tlv) + crash_data->ramdump_buf_len;
	len += sizeof(*dump_tlv) + remote_crash_data->remote_buf_len;
	len += sizeof(*dump_tlv) + reg_crash_data->reg_buf_len;
	len += sizeof(*dump_tlv) + reg_crash_data->reg_rddm_buf_len;
	sofar += hdr_len;

	buf = vzalloc(len);
	if (!buf)
		return NULL;

	dump_data = (struct ath11k_dump_file_data *)(buf);
	strlcpy(dump_data->df_magic, "ATH11K-FW-DUMP",
		sizeof(dump_data->df_magic));
	dump_data->len = cpu_to_le32(len);
	dump_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_VERSION);
	guid_gen(&dump_data->guid);
	ktime_get_real_ts64(&timestamp);
	dump_data->tv_sec = cpu_to_le64(timestamp.tv_sec);
	dump_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec);

	/* Gather FW paging dump */
	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
	dump_tlv->type = cpu_to_le32(ATH11K_FW_CRASH_PAGING_DATA);
	dump_tlv->tlv_len = cpu_to_le32(crash_data->paging_dump_buf_len);
	memcpy(dump_tlv->tlv_data, crash_data->paging_dump_buf,
	       crash_data->paging_dump_buf_len);
	sofar += sizeof(*dump_tlv) + crash_data->paging_dump_buf_len;

	/* Gather RDDM dump */
	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
	dump_tlv->type = cpu_to_le32(ATH11K_FW_CRASH_RDDM_DATA);
	dump_tlv->tlv_len = cpu_to_le32(crash_data->ramdump_buf_len);
	memcpy(dump_tlv->tlv_data, crash_data->ramdump_buf,
	       crash_data->ramdump_buf_len);
	sofar += sizeof(*dump_tlv) + crash_data->ramdump_buf_len;

	/* gather remote memory */
	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
	dump_tlv->type = cpu_to_le32(ATH11K_FW_REMOTE_MEM_DATA);
	dump_tlv->tlv_len = cpu_to_le32(remote_crash_data->remote_buf_len);
	memcpy(dump_tlv->tlv_data, remote_crash_data->remote_buf,
	       remote_crash_data->remote_buf_len);
	sofar += sizeof(*dump_tlv) + remote_crash_data->remote_buf_len;

	/* gather register memory */
	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
	dump_tlv->type = cpu_to_le32(ATH11K_FW_REGISTER_DATA);
	dump_tlv->tlv_len = cpu_to_le32(reg_crash_data->reg_buf_len);
	memcpy(dump_tlv->tlv_data, reg_crash_data->reg_buf,
	       reg_crash_data->reg_buf_len);
	sofar += sizeof(*dump_tlv) + reg_crash_data->reg_buf_len;

	/* gather register for rddm fail */
	dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar);
	dump_tlv->type = cpu_to_le32(ATH11K_FW_REGISTER_RDDM_DATA);
	dump_tlv->tlv_len = cpu_to_le32(reg_crash_data->reg_rddm_buf_len);
	memcpy(dump_tlv->tlv_data, reg_crash_data->reg_rddm_buf,
	       reg_crash_data->reg_rddm_buf_len);
	sofar += sizeof(*dump_tlv) + reg_crash_data->reg_rddm_buf_len;

	return dump_data;
}

static int ath11k_coredump_submit(struct ath11k_pci *ab_pci)
{
	struct ath11k_dump_file_data *dump;

	dump = ath11k_coredump_build(&ab_pci->mhi_fw_crash_data,
				     &ab_pci->ab->remote_crash_data,
				     &ab_pci->reg_data);
	if (!dump)
		return -ENODATA;

	dev_coredumpv(ab_pci->mhi_ctrl->cntrl_dev, dump,
		      le32_to_cpu(dump->len), GFP_KERNEL);

	return 0;
}

static void ath11k_coredump_buf_release(struct ath11k_pci *ab_pci)
{
	struct fw_remote_crash_data *remote = &ab_pci->ab->remote_crash_data;
	struct ath11k_mhi_fw_crash_data *mhi = &ab_pci->mhi_fw_crash_data;
	struct register_crash_data *reg = &ab_pci->reg_data;

	if (remote->remote_buf) {
		vfree(remote->remote_buf);
		remote->remote_buf = NULL;
	}

	if (mhi->ramdump_buf) {
		vfree(mhi->ramdump_buf);
		mhi->ramdump_buf = NULL;
	}

	if (mhi->paging_dump_buf) {
		vfree(mhi->paging_dump_buf);
		mhi->paging_dump_buf = NULL;
	}

	if (reg->reg_buf) {
		vfree(reg->reg_buf);
		reg->reg_buf = NULL;
	}

	if (reg->reg_rddm_buf) {
		vfree(reg->reg_rddm_buf);
		reg->reg_rddm_buf = NULL;
	}
}

void ath11k_mhi_pm_rddm_worker(struct work_struct *work)
{
	struct ath11k_pci *ab_pci = container_of(work,
						 struct ath11k_pci,
						 rddm_worker);
	struct ath11k_base *ab = ab_pci->ab;

	mhi_download_rddm_image(ab_pci->mhi_ctrl, false);

	ath11k_coredump_fw_rddm_dump(ab_pci, ab_pci->mhi_ctrl);
	ath11k_coredump_fw_paging_dump(ab_pci, ab_pci->mhi_ctrl);
	ath11k_qmi_remote_dump(ab_pci->ab);
	ath11k_pci_register_dump(ab_pci);

	ath11k_coredump_submit(ab_pci);
	ath11k_coredump_buf_release(ab_pci);

	if (test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags))
		return;

	ath11k_info(ab, "start to reset for rddm\n");
	queue_work(ab->workqueue_aux, &ab->reset_work);
}