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