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