xref: /freebsd/sys/contrib/dev/iwlwifi/iwl-phy-db.c (revision d9836fb4b9380e2ed1c38455fb31a3832b452671)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright (C) 2005-2014, 2020-2021 Intel Corporation
4  * Copyright (C) 2016 Intel Deutschland GmbH
5  */
6 #include <linux/slab.h>
7 #include <linux/string.h>
8 #include <linux/export.h>
9 
10 #include "iwl-drv.h"
11 #include "iwl-phy-db.h"
12 #include "iwl-debug.h"
13 #include "iwl-op-mode.h"
14 #include "iwl-trans.h"
15 
16 struct iwl_phy_db_entry {
17 	u16	size;
18 	u8	*data;
19 };
20 
21 /**
22  * struct iwl_phy_db - stores phy configuration and calibration data.
23  *
24  * @cfg: phy configuration.
25  * @calib_nch: non channel specific calibration data.
26  * @n_group_papd: number of entries in papd channel group.
27  * @calib_ch_group_papd: calibration data related to papd channel group.
28  * @n_group_txp: number of entries in tx power channel group.
29  * @calib_ch_group_txp: calibration data related to tx power chanel group.
30  * @trans: transport layer
31  */
32 struct iwl_phy_db {
33 	struct iwl_phy_db_entry	cfg;
34 	struct iwl_phy_db_entry	calib_nch;
35 	int n_group_papd;
36 	struct iwl_phy_db_entry	*calib_ch_group_papd;
37 	int n_group_txp;
38 	struct iwl_phy_db_entry	*calib_ch_group_txp;
39 
40 	struct iwl_trans *trans;
41 };
42 
43 enum iwl_phy_db_section_type {
44 	IWL_PHY_DB_CFG = 1,
45 	IWL_PHY_DB_CALIB_NCH,
46 	IWL_PHY_DB_UNUSED,
47 	IWL_PHY_DB_CALIB_CHG_PAPD,
48 	IWL_PHY_DB_CALIB_CHG_TXP,
49 	IWL_PHY_DB_MAX
50 };
51 
52 #define PHY_DB_CMD 0x6c
53 
54 /* for parsing of tx power channel group data that comes from the firmware*/
55 struct iwl_phy_db_chg_txp {
56 	__le32 space;
57 	__le16 max_channel_idx;
58 } __packed;
59 
iwl_phy_db_init(struct iwl_trans * trans)60 struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
61 {
62 	struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
63 					    GFP_KERNEL);
64 
65 	if (!phy_db)
66 		return phy_db;
67 
68 	phy_db->trans = trans;
69 
70 	phy_db->n_group_txp = -1;
71 	phy_db->n_group_papd = -1;
72 
73 	/* TODO: add default values of the phy db. */
74 	return phy_db;
75 }
76 IWL_EXPORT_SYMBOL(iwl_phy_db_init);
77 
78 /*
79  * get phy db section: returns a pointer to a phy db section specified by
80  * type and channel group id.
81  */
82 static struct iwl_phy_db_entry *
iwl_phy_db_get_section(struct iwl_phy_db * phy_db,enum iwl_phy_db_section_type type,u16 chg_id)83 iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
84 		       enum iwl_phy_db_section_type type,
85 		       u16 chg_id)
86 {
87 	if (!phy_db || type >= IWL_PHY_DB_MAX)
88 		return NULL;
89 
90 	switch (type) {
91 	case IWL_PHY_DB_CFG:
92 		return &phy_db->cfg;
93 	case IWL_PHY_DB_CALIB_NCH:
94 		return &phy_db->calib_nch;
95 	case IWL_PHY_DB_CALIB_CHG_PAPD:
96 		if (chg_id >= phy_db->n_group_papd)
97 			return NULL;
98 		return &phy_db->calib_ch_group_papd[chg_id];
99 	case IWL_PHY_DB_CALIB_CHG_TXP:
100 		if (chg_id >= phy_db->n_group_txp)
101 			return NULL;
102 		return &phy_db->calib_ch_group_txp[chg_id];
103 	default:
104 		return NULL;
105 	}
106 	return NULL;
107 }
108 
iwl_phy_db_free_section(struct iwl_phy_db * phy_db,enum iwl_phy_db_section_type type,u16 chg_id)109 static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
110 				    enum iwl_phy_db_section_type type,
111 				    u16 chg_id)
112 {
113 	struct iwl_phy_db_entry *entry =
114 				iwl_phy_db_get_section(phy_db, type, chg_id);
115 	if (!entry)
116 		return;
117 
118 	kfree(entry->data);
119 	entry->data = NULL;
120 	entry->size = 0;
121 }
122 
iwl_phy_db_free(struct iwl_phy_db * phy_db)123 void iwl_phy_db_free(struct iwl_phy_db *phy_db)
124 {
125 	int i;
126 
127 	if (!phy_db)
128 		return;
129 
130 	iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
131 	iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
132 
133 	for (i = 0; i < phy_db->n_group_papd; i++)
134 		iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
135 	kfree(phy_db->calib_ch_group_papd);
136 
137 	for (i = 0; i < phy_db->n_group_txp; i++)
138 		iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
139 	kfree(phy_db->calib_ch_group_txp);
140 
141 	kfree(phy_db);
142 }
143 IWL_EXPORT_SYMBOL(iwl_phy_db_free);
144 
iwl_phy_db_set_section(struct iwl_phy_db * phy_db,struct iwl_rx_packet * pkt)145 int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
146 			   struct iwl_rx_packet *pkt)
147 {
148 	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
149 	struct iwl_calib_res_notif_phy_db *phy_db_notif =
150 			(struct iwl_calib_res_notif_phy_db *)pkt->data;
151 	enum iwl_phy_db_section_type type;
152 	u16 size;
153 	struct iwl_phy_db_entry *entry;
154 	u16 chg_id = 0;
155 
156 	if (pkt_len < sizeof(*phy_db_notif))
157 		return -EINVAL;
158 
159 	type = le16_to_cpu(phy_db_notif->type);
160 	size = le16_to_cpu(phy_db_notif->length);
161 
162 	if (pkt_len < sizeof(*phy_db_notif) + size)
163 		return -EINVAL;
164 
165 	if (!phy_db)
166 		return -EINVAL;
167 
168 	if (type == IWL_PHY_DB_CALIB_CHG_PAPD) {
169 		chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
170 		if (phy_db && !phy_db->calib_ch_group_papd) {
171 			/*
172 			 * Firmware sends the largest index first, so we can use
173 			 * it to know how much we should allocate.
174 			 */
175 			phy_db->calib_ch_group_papd = kcalloc(chg_id + 1,
176 							      sizeof(struct iwl_phy_db_entry),
177 							      GFP_ATOMIC);
178 			if (!phy_db->calib_ch_group_papd)
179 				return -ENOMEM;
180 			phy_db->n_group_papd = chg_id + 1;
181 		}
182 	} else if (type == IWL_PHY_DB_CALIB_CHG_TXP) {
183 		chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
184 		if (phy_db && !phy_db->calib_ch_group_txp) {
185 			/*
186 			 * Firmware sends the largest index first, so we can use
187 			 * it to know how much we should allocate.
188 			 */
189 			phy_db->calib_ch_group_txp = kcalloc(chg_id + 1,
190 							     sizeof(struct iwl_phy_db_entry),
191 							     GFP_ATOMIC);
192 			if (!phy_db->calib_ch_group_txp)
193 				return -ENOMEM;
194 			phy_db->n_group_txp = chg_id + 1;
195 		}
196 	}
197 
198 	entry = iwl_phy_db_get_section(phy_db, type, chg_id);
199 	if (!entry)
200 		return -EINVAL;
201 
202 	kfree(entry->data);
203 	entry->data = kmemdup(phy_db_notif->data, size, GFP_ATOMIC);
204 	if (!entry->data) {
205 		entry->size = 0;
206 		return -ENOMEM;
207 	}
208 
209 	entry->size = size;
210 
211 	IWL_DEBUG_INFO(phy_db->trans,
212 		       "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
213 		       __func__, __LINE__, type, size);
214 
215 	return 0;
216 }
217 IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
218 
is_valid_channel(u16 ch_id)219 static int is_valid_channel(u16 ch_id)
220 {
221 	if (ch_id <= 14 ||
222 	    (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
223 	    (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
224 	    (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
225 		return 1;
226 	return 0;
227 }
228 
ch_id_to_ch_index(u16 ch_id)229 static u8 ch_id_to_ch_index(u16 ch_id)
230 {
231 	if (WARN_ON(!is_valid_channel(ch_id)))
232 		return 0xff;
233 
234 	if (ch_id <= 14)
235 		return ch_id - 1;
236 	if (ch_id <= 64)
237 		return (ch_id + 20) / 4;
238 	if (ch_id <= 140)
239 		return (ch_id - 12) / 4;
240 	return (ch_id - 13) / 4;
241 }
242 
243 
channel_id_to_papd(u16 ch_id)244 static u16 channel_id_to_papd(u16 ch_id)
245 {
246 	if (WARN_ON(!is_valid_channel(ch_id)))
247 		return 0xff;
248 
249 	if (1 <= ch_id && ch_id <= 14)
250 		return 0;
251 	if (36 <= ch_id && ch_id <= 64)
252 		return 1;
253 	if (100 <= ch_id && ch_id <= 140)
254 		return 2;
255 	return 3;
256 }
257 
channel_id_to_txp(struct iwl_phy_db * phy_db,u16 ch_id)258 static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
259 {
260 	struct iwl_phy_db_chg_txp *txp_chg;
261 	int i;
262 	u8 ch_index = ch_id_to_ch_index(ch_id);
263 	if (ch_index == 0xff)
264 		return 0xff;
265 
266 	for (i = 0; i < phy_db->n_group_txp; i++) {
267 		txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
268 		if (!txp_chg)
269 			return 0xff;
270 		/*
271 		 * Looking for the first channel group that its max channel is
272 		 * higher then wanted channel.
273 		 */
274 		if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
275 			return i;
276 	}
277 	return 0xff;
278 }
279 static
iwl_phy_db_get_section_data(struct iwl_phy_db * phy_db,u32 type,u8 ** data,u16 * size,u16 ch_id)280 int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
281 				u32 type, u8 **data, u16 *size, u16 ch_id)
282 {
283 	struct iwl_phy_db_entry *entry;
284 	u16 ch_group_id = 0;
285 
286 	if (!phy_db)
287 		return -EINVAL;
288 
289 	/* find wanted channel group */
290 	if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
291 		ch_group_id = channel_id_to_papd(ch_id);
292 	else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
293 		ch_group_id = channel_id_to_txp(phy_db, ch_id);
294 
295 	entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
296 	if (!entry)
297 		return -EINVAL;
298 
299 	*data = entry->data;
300 	*size = entry->size;
301 
302 	IWL_DEBUG_INFO(phy_db->trans,
303 		       "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
304 		       __func__, __LINE__, type, *size);
305 
306 	return 0;
307 }
308 
iwl_send_phy_db_cmd(struct iwl_phy_db * phy_db,u16 type,u16 length,void * data)309 static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
310 			       u16 length, void *data)
311 {
312 	struct iwl_phy_db_cmd phy_db_cmd;
313 	struct iwl_host_cmd cmd = {
314 		.id = PHY_DB_CMD,
315 	};
316 
317 	IWL_DEBUG_INFO(phy_db->trans,
318 		       "Sending PHY-DB hcmd of type %d, of length %d\n",
319 		       type, length);
320 
321 	/* Set phy db cmd variables */
322 	phy_db_cmd.type = cpu_to_le16(type);
323 	phy_db_cmd.length = cpu_to_le16(length);
324 
325 	/* Set hcmd variables */
326 	cmd.data[0] = &phy_db_cmd;
327 	cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
328 	cmd.data[1] = data;
329 	cmd.len[1] = length;
330 	cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
331 
332 	return iwl_trans_send_cmd(phy_db->trans, &cmd);
333 }
334 
iwl_phy_db_send_all_channel_groups(struct iwl_phy_db * phy_db,enum iwl_phy_db_section_type type,u8 max_ch_groups)335 static int iwl_phy_db_send_all_channel_groups(
336 					struct iwl_phy_db *phy_db,
337 					enum iwl_phy_db_section_type type,
338 					u8 max_ch_groups)
339 {
340 	u16 i;
341 	int err;
342 	struct iwl_phy_db_entry *entry;
343 
344 	/* Send all the  channel specific groups to operational fw */
345 	for (i = 0; i < max_ch_groups; i++) {
346 		entry = iwl_phy_db_get_section(phy_db,
347 					       type,
348 					       i);
349 		if (!entry)
350 			return -EINVAL;
351 
352 		if (!entry->size)
353 			continue;
354 
355 		/* Send the requested PHY DB section */
356 		err = iwl_send_phy_db_cmd(phy_db,
357 					  type,
358 					  entry->size,
359 					  entry->data);
360 		if (err) {
361 			IWL_ERR(phy_db->trans,
362 				"Can't SEND phy_db section %d (%d), err %d\n",
363 				type, i, err);
364 			return err;
365 		}
366 
367 		IWL_DEBUG_INFO(phy_db->trans,
368 			       "Sent PHY_DB HCMD, type = %d num = %d\n",
369 			       type, i);
370 	}
371 
372 	return 0;
373 }
374 
iwl_send_phy_db_data(struct iwl_phy_db * phy_db)375 int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
376 {
377 	u8 *data = NULL;
378 	u16 size = 0;
379 	int err;
380 
381 	IWL_DEBUG_INFO(phy_db->trans,
382 		       "Sending phy db data and configuration to runtime image\n");
383 
384 	/* Send PHY DB CFG section */
385 	err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
386 					  &data, &size, 0);
387 	if (err) {
388 		IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
389 		return err;
390 	}
391 
392 	err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
393 	if (err) {
394 		IWL_ERR(phy_db->trans,
395 			"Cannot send HCMD of  Phy DB cfg section\n");
396 		return err;
397 	}
398 
399 	err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
400 					  &data, &size, 0);
401 	if (err) {
402 		IWL_ERR(phy_db->trans,
403 			"Cannot get Phy DB non specific channel section\n");
404 		return err;
405 	}
406 
407 	err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
408 	if (err) {
409 		IWL_ERR(phy_db->trans,
410 			"Cannot send HCMD of Phy DB non specific channel section\n");
411 		return err;
412 	}
413 
414 	/* Send all the TXP channel specific data */
415 	err = iwl_phy_db_send_all_channel_groups(phy_db,
416 						 IWL_PHY_DB_CALIB_CHG_PAPD,
417 						 phy_db->n_group_papd);
418 	if (err) {
419 		IWL_ERR(phy_db->trans,
420 			"Cannot send channel specific PAPD groups\n");
421 		return err;
422 	}
423 
424 	/* Send all the TXP channel specific data */
425 	err = iwl_phy_db_send_all_channel_groups(phy_db,
426 						 IWL_PHY_DB_CALIB_CHG_TXP,
427 						 phy_db->n_group_txp);
428 	if (err) {
429 		IWL_ERR(phy_db->trans,
430 			"Cannot send channel specific TX power groups\n");
431 		return err;
432 	}
433 
434 	IWL_DEBUG_INFO(phy_db->trans,
435 		       "Finished sending phy db non channel data\n");
436 	return 0;
437 }
438 IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);
439