1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <console/console.h>
5 #include <drivers/vpd/vpd.h>
14 #define LEGACY_BYTES_PER_GEO_OFFSET 6
15 #define LEGACY_BYTES_PER_SAR_LIMIT 10
16 #define LEGACY_NUM_SAR_LIMITS 4
17 #define LEGACY_SAR_BIN_SIZE 81
18 #define LEGACY_SAR_WGDS_BIN_SIZE 119
19 #define LEGACY_SAR_NUM_WGDS_GROUPS 3
21 static uint8_t *wifi_hextostr(const char *sar_str
, size_t str_len
, size_t *sar_bin_len
,
22 bool legacy_hex_format
)
24 uint8_t *sar_bin
= NULL
;
27 if (!legacy_hex_format
) {
28 sar_bin
= malloc(str_len
);
30 printk(BIOS_ERR
, "Failed to allocate space for SAR binary!\n");
34 memcpy(sar_bin
, sar_str
, str_len
);
35 *sar_bin_len
= str_len
;
37 bin_len
= ((str_len
- 1) / 2);
38 sar_bin
= malloc(bin_len
);
40 printk(BIOS_ERR
, "Failed to allocate space for SAR binary!\n");
44 if (hexstrtobin(sar_str
, (uint8_t *)sar_bin
, bin_len
) != bin_len
) {
45 printk(BIOS_ERR
, "sar_limits contains non-hex value!\n");
50 *sar_bin_len
= bin_len
;
56 static int sar_table_size(const struct sar_profile
*sar
)
61 return (sizeof(struct sar_profile
) + ((1 + sar
->dsar_set_count
) * sar
->chains_count
*
62 sar
->subbands_count
));
65 static int wgds_table_size(const struct geo_profile
*geo
)
70 return sizeof(struct geo_profile
) + (geo
->chains_count
* geo
->bands_count
);
73 static int gain_table_size(const struct gain_profile
*gain
)
78 return sizeof(struct gain_profile
) + (gain
->chains_count
* gain
->bands_count
);
81 static int sar_avg_table_size(const struct avg_profile
*sar_avg
)
86 return sizeof(struct avg_profile
);
89 static int dsm_table_size(const struct dsm_profile
*dsm
)
94 return sizeof(struct dsm_profile
);
97 static int bsar_table_size(const struct bsar_profile
*bsar
)
102 return sizeof(struct bsar_profile
);
105 static int wbem_table_size(const struct wbem_profile
*wbem
)
110 return sizeof(struct wbem_profile
);
113 static bool valid_legacy_length(size_t bin_len
)
115 if (bin_len
== LEGACY_SAR_WGDS_BIN_SIZE
)
118 if (bin_len
== LEGACY_SAR_BIN_SIZE
&& !CONFIG(GEO_SAR_ENABLE
))
124 static int sar_header_size(void)
126 return (MAX_PROFILE_COUNT
* sizeof(uint16_t)) + sizeof(struct sar_header
);
129 static int fill_wifi_sar_limits(union wifi_sar_limits
*sar_limits
, const uint8_t *sar_bin
,
132 struct sar_header
*header
;
133 size_t i
= 0, expected_sar_bin_size
;
134 size_t header_size
= sar_header_size();
136 if (sar_bin_size
< header_size
) {
137 printk(BIOS_ERR
, "Invalid SAR format!\n");
141 header
= (struct sar_header
*)sar_bin
;
143 if (header
->version
!= SAR_FILE_REVISION
) {
144 printk(BIOS_ERR
, "Invalid SAR file version: %d!\n", header
->version
);
148 for (i
= 0; i
< MAX_PROFILE_COUNT
; i
++) {
149 if (header
->offsets
[i
] > sar_bin_size
) {
150 printk(BIOS_ERR
, "Offset is outside the file size!\n");
154 if (header
->offsets
[i
])
155 sar_limits
->profile
[i
] = (void *) (sar_bin
+ header
->offsets
[i
]);
158 expected_sar_bin_size
= header_size
;
159 expected_sar_bin_size
+= sar_table_size(sar_limits
->sar
);
160 expected_sar_bin_size
+= wgds_table_size(sar_limits
->wgds
);
161 expected_sar_bin_size
+= gain_table_size(sar_limits
->ppag
);
162 expected_sar_bin_size
+= sar_avg_table_size(sar_limits
->wtas
);
163 expected_sar_bin_size
+= dsm_table_size(sar_limits
->dsm
);
164 expected_sar_bin_size
+= bsar_table_size(sar_limits
->bsar
);
165 expected_sar_bin_size
+= wbem_table_size(sar_limits
->wbem
);
167 if (sar_bin_size
!= expected_sar_bin_size
) {
168 printk(BIOS_ERR
, "Invalid SAR size, expected: %zu, obtained: %zu\n",
169 expected_sar_bin_size
, sar_bin_size
);
176 static int fill_wifi_sar_limits_legacy(union wifi_sar_limits
*sar_limits
,
177 const uint8_t *sar_bin
, size_t sar_bin_size
)
179 uint8_t *new_sar_bin
;
180 size_t size
= sar_bin_size
+ sizeof(struct sar_profile
);
182 if (CONFIG(GEO_SAR_ENABLE
))
183 size
+= sizeof(struct geo_profile
);
185 new_sar_bin
= malloc(size
);
187 printk(BIOS_ERR
, "Failed to allocate space for SAR binary!\n");
191 sar_limits
->sar
= (struct sar_profile
*) new_sar_bin
;
192 sar_limits
->sar
->revision
= 0;
193 sar_limits
->sar
->dsar_set_count
= CONFIG_DSAR_SET_NUM
;
194 sar_limits
->sar
->chains_count
= SAR_REV0_CHAINS_COUNT
;
195 sar_limits
->sar
->subbands_count
= SAR_REV0_SUBBANDS_COUNT
;
196 memcpy(&sar_limits
->sar
->sar_table
, sar_bin
,
197 LEGACY_BYTES_PER_SAR_LIMIT
* LEGACY_NUM_SAR_LIMITS
);
199 if (!CONFIG(GEO_SAR_ENABLE
))
202 sar_limits
->wgds
= (struct geo_profile
*)(new_sar_bin
+
203 sar_table_size(sar_limits
->sar
));
204 sar_limits
->wgds
->revision
= 0;
205 sar_limits
->wgds
->chains_count
= LEGACY_SAR_NUM_WGDS_GROUPS
;
206 sar_limits
->wgds
->bands_count
= LEGACY_BYTES_PER_GEO_OFFSET
;
207 memcpy(&sar_limits
->wgds
->wgds_table
,
208 sar_bin
+ LEGACY_BYTES_PER_SAR_LIMIT
* LEGACY_NUM_SAR_LIMITS
+ REVISION_SIZE
,
209 LEGACY_BYTES_PER_GEO_OFFSET
* LEGACY_SAR_NUM_WGDS_GROUPS
);
215 * Retrieve WiFi SAR limits data from CBFS and decode it
216 * Legacy WiFi SAR data is expected in the format: [<WRDD><EWRD>][WGDS]
218 * [<WRDD><EWRD>] = NUM_SAR_LIMITS * BYTES_PER_SAR_LIMIT bytes.
219 * [WGDS]=[WGDS_REVISION][WGDS_DATA]
221 * Current SAR configuration data is expected in the format:
226 * [SAR_REVISION,DSAR_SET_COUNT,CHAINS_COUNT,SUBBANDS_COUNT <WRDD>[EWRD]]
227 * [WGDS_REVISION,CHAINS_COUNT,SUBBANDS_COUNT<WGDS_DATA>]
228 * [PPAG_REVISION,MODE,CHAINS_COUNT,SUBBANDS_COUNT<PPAG_DATA>]
229 * [WTAS_REVISION, WTAS_DATA]
230 * [DSM_RETURN_VALUES]
231 * [BSAR_REVISION,IPML,LB,BR,EDR2,EDR3,LE,LE2,LE_LR]
232 * [WBEM_REVISION, WBEM_DATA]
234 * The configuration data will always have the revision added in the file for each of the
235 * block, based on the revision number and validity, size of the specific block will be
238 * [WGDS_DATA] = [GROUP#0][GROUP#1][GROUP#2]
241 * Supported by Revision 0, 1 and 2
242 * [2.4Ghz - Max Allowed][2.4Ghz - Chain A Offset][2.4Ghz - Chain B Offset]
243 * [5Ghz - Max Allowed][5Ghz - Chain A Offset][5Ghz - Chain B Offset]
244 * Supported by Revision 1 and 2
245 * [6Ghz - Max Allowed][6Ghz - Chain A Offset][6Ghz - Chain B Offset]
247 * [GROUP#0] is for FCC
248 * [GROUP#1] is for Europe/Japan
249 * [GROUP#2] is for ROW
251 * [PPAG_DATA] = [ANT_gain Table Chain A] [ANT_gain Table Chain A]
254 * Supported by Revision 0, 1 and 2
255 * [Antenna gain used for 2400MHz frequency]
256 * [Antenna gain used for 5150-5350MHz frequency]
257 * [Antenna gain used for 5350-5470MHz frequency]
258 * [Antenna gain used for 5470-5725MHz frequency]
259 * [Antenna gain used for 5725-5945MHz frequency]
260 * Supported by Revision 1 and 2
261 * [Antenna gain used for 5945-6165MHz frequency]
262 * [Antenna gain used for 6165-6405MHz frequency]
263 * [Antenna gain used for 6405-6525MHz frequency]
264 * [Antenna gain used for 6525-6705MHz frequency]
265 * [Antenna gain used for 6705-6865MHz frequency]
266 * [Antenna gain used for 6865-7105MHz frequency]
269 * [Enable/disable the TAS feature]
270 * [Number of blocked countries that are not approved by the OEM to support this feature]
271 * [deny_list_entry_<1-16>: ISO country code to block]
273 * [Enable or disable 320MHZ Bandwidth for Japan, SouthKorea]
275 int get_wifi_sar_limits(union wifi_sar_limits
*sar_limits
)
277 const char *filename
;
278 size_t sar_bin_len
, sar_str_len
;
282 bool legacy_hex_format
= false;
284 filename
= get_wifi_sar_cbfs_filename();
285 if (filename
== NULL
) {
286 printk(BIOS_ERR
, "Filename missing for CBFS SAR file!\n");
290 sar_str
= cbfs_map(filename
, &sar_str_len
);
292 printk(BIOS_ERR
, "Failed to get the %s file size!\n", filename
);
296 if (strncmp(sar_str
, SAR_STR_PREFIX
, SAR_STR_PREFIX_SIZE
) == 0) {
297 legacy_hex_format
= false;
298 } else if (valid_legacy_length(sar_str_len
)) {
299 legacy_hex_format
= true;
301 printk(BIOS_ERR
, "Invalid SAR format!\n");
305 sar_bin
= wifi_hextostr(sar_str
, sar_str_len
, &sar_bin_len
, legacy_hex_format
);
306 if (sar_bin
== NULL
) {
307 printk(BIOS_ERR
, "Failed to parse SAR file %s\n", filename
);
311 memset(sar_limits
, 0, sizeof(*sar_limits
));
312 if (legacy_hex_format
) {
313 ret
= fill_wifi_sar_limits_legacy(sar_limits
, sar_bin
, sar_bin_len
);
316 ret
= fill_wifi_sar_limits(sar_limits
, sar_bin
, sar_bin_len
);
327 const char *get_wifi_sar_cbfs_filename(void)
329 return WIFI_SAR_CBFS_DEFAULT_FILENAME
;
332 char *get_wifi_sar_fw_config_filename(const struct fw_config_field
*field
)
334 uint64_t sar_id
= fw_config_get_field(field
);
335 if (sar_id
== UNDEFINED_FW_CONFIG
) {
336 printk(BIOS_WARNING
, "fw_config unprovisioned, set sar filename to NULL\n");
339 static char filename
[20];
340 printk(BIOS_INFO
, "Use wifi_sar_%lld.hex.\n", sar_id
);
341 if (snprintf(filename
, sizeof(filename
), "wifi_sar_%lld.hex", sar_id
) < 0) {
342 printk(BIOS_ERR
, "Error occurred with snprintf, set sar filename to NULL\n");