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