xref: /linux/drivers/net/wireless/ath/ath12k/fw.c (revision 24168c5e6dfbdd5b414f048f47f75d64533296ca)
1 // SPDX-License-Identifier: BSD-3-Clause-Clear
2 /*
3  * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  */
5 
6 #include "core.h"
7 
8 #include "debug.h"
9 
10 static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab,
11 					    const char *name)
12 {
13 	size_t magic_len, len, ie_len;
14 	int ie_id, i, index, bit, ret;
15 	struct ath12k_fw_ie *hdr;
16 	const u8 *data;
17 	__le32 *timestamp;
18 
19 	ab->fw.fw = ath12k_core_firmware_request(ab, name);
20 	if (IS_ERR(ab->fw.fw)) {
21 		ret = PTR_ERR(ab->fw.fw);
22 		ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to load %s: %d\n", name, ret);
23 		ab->fw.fw = NULL;
24 		return ret;
25 	}
26 
27 	data = ab->fw.fw->data;
28 	len = ab->fw.fw->size;
29 
30 	/* magic also includes the null byte, check that as well */
31 	magic_len = strlen(ATH12K_FIRMWARE_MAGIC) + 1;
32 
33 	if (len < magic_len) {
34 		ath12k_err(ab, "firmware image too small to contain magic: %zu\n",
35 			   len);
36 		ret = -EINVAL;
37 		goto err;
38 	}
39 
40 	if (memcmp(data, ATH12K_FIRMWARE_MAGIC, magic_len) != 0) {
41 		ath12k_err(ab, "Invalid firmware magic\n");
42 		ret = -EINVAL;
43 		goto err;
44 	}
45 
46 	/* jump over the padding */
47 	magic_len = ALIGN(magic_len, 4);
48 
49 	/* make sure there's space for padding */
50 	if (magic_len > len) {
51 		ath12k_err(ab, "No space for padding after magic\n");
52 		ret = -EINVAL;
53 		goto err;
54 	}
55 
56 	len -= magic_len;
57 	data += magic_len;
58 
59 	/* loop elements */
60 	while (len > sizeof(struct ath12k_fw_ie)) {
61 		hdr = (struct ath12k_fw_ie *)data;
62 
63 		ie_id = le32_to_cpu(hdr->id);
64 		ie_len = le32_to_cpu(hdr->len);
65 
66 		len -= sizeof(*hdr);
67 		data += sizeof(*hdr);
68 
69 		if (len < ie_len) {
70 			ath12k_err(ab, "Invalid length for FW IE %d (%zu < %zu)\n",
71 				   ie_id, len, ie_len);
72 			ret = -EINVAL;
73 			goto err;
74 		}
75 
76 		switch (ie_id) {
77 		case ATH12K_FW_IE_TIMESTAMP:
78 			if (ie_len != sizeof(u32))
79 				break;
80 
81 			timestamp = (__le32 *)data;
82 
83 			ath12k_dbg(ab, ATH12K_DBG_BOOT, "found fw timestamp %d\n",
84 				   le32_to_cpup(timestamp));
85 			break;
86 		case ATH12K_FW_IE_FEATURES:
87 			ath12k_dbg(ab, ATH12K_DBG_BOOT,
88 				   "found firmware features ie (%zd B)\n",
89 				   ie_len);
90 
91 			for (i = 0; i < ATH12K_FW_FEATURE_COUNT; i++) {
92 				index = i / 8;
93 				bit = i % 8;
94 
95 				if (index == ie_len)
96 					break;
97 
98 				if (data[index] & (1 << bit))
99 					__set_bit(i, ab->fw.fw_features);
100 			}
101 
102 			ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "features", "",
103 					ab->fw.fw_features,
104 					sizeof(ab->fw.fw_features));
105 			break;
106 		case ATH12K_FW_IE_AMSS_IMAGE:
107 			ath12k_dbg(ab, ATH12K_DBG_BOOT,
108 				   "found fw image ie (%zd B)\n",
109 				   ie_len);
110 
111 			ab->fw.amss_data = data;
112 			ab->fw.amss_len = ie_len;
113 			break;
114 		case ATH12K_FW_IE_M3_IMAGE:
115 			ath12k_dbg(ab, ATH12K_DBG_BOOT,
116 				   "found m3 image ie (%zd B)\n",
117 				   ie_len);
118 
119 			ab->fw.m3_data = data;
120 			ab->fw.m3_len = ie_len;
121 			break;
122 		case ATH12K_FW_IE_AMSS_DUALMAC_IMAGE:
123 			ath12k_dbg(ab, ATH12K_DBG_BOOT,
124 				   "found dualmac fw image ie (%zd B)\n",
125 				   ie_len);
126 			ab->fw.amss_dualmac_data = data;
127 			ab->fw.amss_dualmac_len = ie_len;
128 			break;
129 		default:
130 			ath12k_warn(ab, "Unknown FW IE: %u\n", ie_id);
131 			break;
132 		}
133 
134 		/* jump over the padding */
135 		ie_len = ALIGN(ie_len, 4);
136 
137 		/* make sure there's space for padding */
138 		if (ie_len > len)
139 			break;
140 
141 		len -= ie_len;
142 		data += ie_len;
143 	}
144 
145 	return 0;
146 
147 err:
148 	release_firmware(ab->fw.fw);
149 	ab->fw.fw = NULL;
150 	return ret;
151 }
152 
153 void ath12k_fw_map(struct ath12k_base *ab)
154 {
155 	int ret;
156 
157 	ret = ath12k_fw_request_firmware_api_n(ab, ATH12K_FW_API2_FILE);
158 	if (ret == 0)
159 		ab->fw.api_version = 2;
160 	else
161 		ab->fw.api_version = 1;
162 
163 	ath12k_dbg(ab, ATH12K_DBG_BOOT, "using fw api %d\n",
164 		   ab->fw.api_version);
165 }
166 
167 void ath12k_fw_unmap(struct ath12k_base *ab)
168 {
169 	release_firmware(ab->fw.fw);
170 	memset(&ab->fw, 0, sizeof(ab->fw));
171 }
172