xref: /linux/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c (revision 6fdcba32711044c35c0e1b094cbd8f3f0b4472c9)
1 /******************************************************************************
2  *
3  * This file is provided under a dual BSD/GPLv2 license.  When using or
4  * redistributing this file, you may do so under either license.
5  *
6  * GPL LICENSE SUMMARY
7  *
8  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
9  * Copyright(c) 2016 Intel Deutschland GmbH
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of version 2 of the GNU General Public License as
13  * published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * The full GNU General Public License is included in this distribution
21  * in the file called COPYING.
22  *
23  * Contact Information:
24  *  Intel Linux Wireless <linuxwifi@intel.com>
25  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
26  *
27  * BSD LICENSE
28  *
29  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
30  * All rights reserved.
31  *
32  * Redistribution and use in source and binary forms, with or without
33  * modification, are permitted provided that the following conditions
34  * are met:
35  *
36  *  * Redistributions of source code must retain the above copyright
37  *    notice, this list of conditions and the following disclaimer.
38  *  * Redistributions in binary form must reproduce the above copyright
39  *    notice, this list of conditions and the following disclaimer in
40  *    the documentation and/or other materials provided with the
41  *    distribution.
42  *  * Neither the name Intel Corporation nor the names of its
43  *    contributors may be used to endorse or promote products derived
44  *    from this software without specific prior written permission.
45  *
46  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
47  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
48  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
49  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
50  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
51  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
52  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
56  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57  *
58  *****************************************************************************/
59 
60 #include <linux/slab.h>
61 #include <linux/string.h>
62 #include <linux/export.h>
63 
64 #include "iwl-drv.h"
65 #include "iwl-phy-db.h"
66 #include "iwl-debug.h"
67 #include "iwl-op-mode.h"
68 #include "iwl-trans.h"
69 
70 #define CHANNEL_NUM_SIZE	4	/* num of channels in calib_ch size */
71 
72 struct iwl_phy_db_entry {
73 	u16	size;
74 	u8	*data;
75 };
76 
77 /**
78  * struct iwl_phy_db - stores phy configuration and calibration data.
79  *
80  * @cfg: phy configuration.
81  * @calib_nch: non channel specific calibration data.
82  * @calib_ch: channel specific calibration data.
83  * @n_group_papd: number of entries in papd channel group.
84  * @calib_ch_group_papd: calibration data related to papd channel group.
85  * @n_group_txp: number of entries in tx power channel group.
86  * @calib_ch_group_txp: calibration data related to tx power chanel group.
87  */
88 struct iwl_phy_db {
89 	struct iwl_phy_db_entry	cfg;
90 	struct iwl_phy_db_entry	calib_nch;
91 	int n_group_papd;
92 	struct iwl_phy_db_entry	*calib_ch_group_papd;
93 	int n_group_txp;
94 	struct iwl_phy_db_entry	*calib_ch_group_txp;
95 
96 	struct iwl_trans *trans;
97 };
98 
99 enum iwl_phy_db_section_type {
100 	IWL_PHY_DB_CFG = 1,
101 	IWL_PHY_DB_CALIB_NCH,
102 	IWL_PHY_DB_UNUSED,
103 	IWL_PHY_DB_CALIB_CHG_PAPD,
104 	IWL_PHY_DB_CALIB_CHG_TXP,
105 	IWL_PHY_DB_MAX
106 };
107 
108 #define PHY_DB_CMD 0x6c
109 
110 /* for parsing of tx power channel group data that comes from the firmware*/
111 struct iwl_phy_db_chg_txp {
112 	__le32 space;
113 	__le16 max_channel_idx;
114 } __packed;
115 
116 struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
117 {
118 	struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
119 					    GFP_KERNEL);
120 
121 	if (!phy_db)
122 		return phy_db;
123 
124 	phy_db->trans = trans;
125 
126 	phy_db->n_group_txp = -1;
127 	phy_db->n_group_papd = -1;
128 
129 	/* TODO: add default values of the phy db. */
130 	return phy_db;
131 }
132 IWL_EXPORT_SYMBOL(iwl_phy_db_init);
133 
134 /*
135  * get phy db section: returns a pointer to a phy db section specified by
136  * type and channel group id.
137  */
138 static struct iwl_phy_db_entry *
139 iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
140 		       enum iwl_phy_db_section_type type,
141 		       u16 chg_id)
142 {
143 	if (!phy_db || type >= IWL_PHY_DB_MAX)
144 		return NULL;
145 
146 	switch (type) {
147 	case IWL_PHY_DB_CFG:
148 		return &phy_db->cfg;
149 	case IWL_PHY_DB_CALIB_NCH:
150 		return &phy_db->calib_nch;
151 	case IWL_PHY_DB_CALIB_CHG_PAPD:
152 		if (chg_id >= phy_db->n_group_papd)
153 			return NULL;
154 		return &phy_db->calib_ch_group_papd[chg_id];
155 	case IWL_PHY_DB_CALIB_CHG_TXP:
156 		if (chg_id >= phy_db->n_group_txp)
157 			return NULL;
158 		return &phy_db->calib_ch_group_txp[chg_id];
159 	default:
160 		return NULL;
161 	}
162 	return NULL;
163 }
164 
165 static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
166 				    enum iwl_phy_db_section_type type,
167 				    u16 chg_id)
168 {
169 	struct iwl_phy_db_entry *entry =
170 				iwl_phy_db_get_section(phy_db, type, chg_id);
171 	if (!entry)
172 		return;
173 
174 	kfree(entry->data);
175 	entry->data = NULL;
176 	entry->size = 0;
177 }
178 
179 void iwl_phy_db_free(struct iwl_phy_db *phy_db)
180 {
181 	int i;
182 
183 	if (!phy_db)
184 		return;
185 
186 	iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
187 	iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
188 
189 	for (i = 0; i < phy_db->n_group_papd; i++)
190 		iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
191 	kfree(phy_db->calib_ch_group_papd);
192 
193 	for (i = 0; i < phy_db->n_group_txp; i++)
194 		iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
195 	kfree(phy_db->calib_ch_group_txp);
196 
197 	kfree(phy_db);
198 }
199 IWL_EXPORT_SYMBOL(iwl_phy_db_free);
200 
201 int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
202 			   struct iwl_rx_packet *pkt)
203 {
204 	struct iwl_calib_res_notif_phy_db *phy_db_notif =
205 			(struct iwl_calib_res_notif_phy_db *)pkt->data;
206 	enum iwl_phy_db_section_type type = le16_to_cpu(phy_db_notif->type);
207 	u16 size  = le16_to_cpu(phy_db_notif->length);
208 	struct iwl_phy_db_entry *entry;
209 	u16 chg_id = 0;
210 
211 	if (!phy_db)
212 		return -EINVAL;
213 
214 	if (type == IWL_PHY_DB_CALIB_CHG_PAPD) {
215 		chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
216 		if (phy_db && !phy_db->calib_ch_group_papd) {
217 			/*
218 			 * Firmware sends the largest index first, so we can use
219 			 * it to know how much we should allocate.
220 			 */
221 			phy_db->calib_ch_group_papd = kcalloc(chg_id + 1,
222 							      sizeof(struct iwl_phy_db_entry),
223 							      GFP_ATOMIC);
224 			if (!phy_db->calib_ch_group_papd)
225 				return -ENOMEM;
226 			phy_db->n_group_papd = chg_id + 1;
227 		}
228 	} else if (type == IWL_PHY_DB_CALIB_CHG_TXP) {
229 		chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
230 		if (phy_db && !phy_db->calib_ch_group_txp) {
231 			/*
232 			 * Firmware sends the largest index first, so we can use
233 			 * it to know how much we should allocate.
234 			 */
235 			phy_db->calib_ch_group_txp = kcalloc(chg_id + 1,
236 							     sizeof(struct iwl_phy_db_entry),
237 							     GFP_ATOMIC);
238 			if (!phy_db->calib_ch_group_txp)
239 				return -ENOMEM;
240 			phy_db->n_group_txp = chg_id + 1;
241 		}
242 	}
243 
244 	entry = iwl_phy_db_get_section(phy_db, type, chg_id);
245 	if (!entry)
246 		return -EINVAL;
247 
248 	kfree(entry->data);
249 	entry->data = kmemdup(phy_db_notif->data, size, GFP_ATOMIC);
250 	if (!entry->data) {
251 		entry->size = 0;
252 		return -ENOMEM;
253 	}
254 
255 	entry->size = size;
256 
257 	IWL_DEBUG_INFO(phy_db->trans,
258 		       "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
259 		       __func__, __LINE__, type, size);
260 
261 	return 0;
262 }
263 IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
264 
265 static int is_valid_channel(u16 ch_id)
266 {
267 	if (ch_id <= 14 ||
268 	    (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
269 	    (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
270 	    (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
271 		return 1;
272 	return 0;
273 }
274 
275 static u8 ch_id_to_ch_index(u16 ch_id)
276 {
277 	if (WARN_ON(!is_valid_channel(ch_id)))
278 		return 0xff;
279 
280 	if (ch_id <= 14)
281 		return ch_id - 1;
282 	if (ch_id <= 64)
283 		return (ch_id + 20) / 4;
284 	if (ch_id <= 140)
285 		return (ch_id - 12) / 4;
286 	return (ch_id - 13) / 4;
287 }
288 
289 
290 static u16 channel_id_to_papd(u16 ch_id)
291 {
292 	if (WARN_ON(!is_valid_channel(ch_id)))
293 		return 0xff;
294 
295 	if (1 <= ch_id && ch_id <= 14)
296 		return 0;
297 	if (36 <= ch_id && ch_id <= 64)
298 		return 1;
299 	if (100 <= ch_id && ch_id <= 140)
300 		return 2;
301 	return 3;
302 }
303 
304 static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
305 {
306 	struct iwl_phy_db_chg_txp *txp_chg;
307 	int i;
308 	u8 ch_index = ch_id_to_ch_index(ch_id);
309 	if (ch_index == 0xff)
310 		return 0xff;
311 
312 	for (i = 0; i < phy_db->n_group_txp; i++) {
313 		txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
314 		if (!txp_chg)
315 			return 0xff;
316 		/*
317 		 * Looking for the first channel group that its max channel is
318 		 * higher then wanted channel.
319 		 */
320 		if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
321 			return i;
322 	}
323 	return 0xff;
324 }
325 static
326 int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
327 				u32 type, u8 **data, u16 *size, u16 ch_id)
328 {
329 	struct iwl_phy_db_entry *entry;
330 	u16 ch_group_id = 0;
331 
332 	if (!phy_db)
333 		return -EINVAL;
334 
335 	/* find wanted channel group */
336 	if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
337 		ch_group_id = channel_id_to_papd(ch_id);
338 	else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
339 		ch_group_id = channel_id_to_txp(phy_db, ch_id);
340 
341 	entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
342 	if (!entry)
343 		return -EINVAL;
344 
345 	*data = entry->data;
346 	*size = entry->size;
347 
348 	IWL_DEBUG_INFO(phy_db->trans,
349 		       "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
350 		       __func__, __LINE__, type, *size);
351 
352 	return 0;
353 }
354 
355 static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
356 			       u16 length, void *data)
357 {
358 	struct iwl_phy_db_cmd phy_db_cmd;
359 	struct iwl_host_cmd cmd = {
360 		.id = PHY_DB_CMD,
361 	};
362 
363 	IWL_DEBUG_INFO(phy_db->trans,
364 		       "Sending PHY-DB hcmd of type %d, of length %d\n",
365 		       type, length);
366 
367 	/* Set phy db cmd variables */
368 	phy_db_cmd.type = cpu_to_le16(type);
369 	phy_db_cmd.length = cpu_to_le16(length);
370 
371 	/* Set hcmd variables */
372 	cmd.data[0] = &phy_db_cmd;
373 	cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
374 	cmd.data[1] = data;
375 	cmd.len[1] = length;
376 	cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
377 
378 	return iwl_trans_send_cmd(phy_db->trans, &cmd);
379 }
380 
381 static int iwl_phy_db_send_all_channel_groups(
382 					struct iwl_phy_db *phy_db,
383 					enum iwl_phy_db_section_type type,
384 					u8 max_ch_groups)
385 {
386 	u16 i;
387 	int err;
388 	struct iwl_phy_db_entry *entry;
389 
390 	/* Send all the  channel specific groups to operational fw */
391 	for (i = 0; i < max_ch_groups; i++) {
392 		entry = iwl_phy_db_get_section(phy_db,
393 					       type,
394 					       i);
395 		if (!entry)
396 			return -EINVAL;
397 
398 		if (!entry->size)
399 			continue;
400 
401 		/* Send the requested PHY DB section */
402 		err = iwl_send_phy_db_cmd(phy_db,
403 					  type,
404 					  entry->size,
405 					  entry->data);
406 		if (err) {
407 			IWL_ERR(phy_db->trans,
408 				"Can't SEND phy_db section %d (%d), err %d\n",
409 				type, i, err);
410 			return err;
411 		}
412 
413 		IWL_DEBUG_INFO(phy_db->trans,
414 			       "Sent PHY_DB HCMD, type = %d num = %d\n",
415 			       type, i);
416 	}
417 
418 	return 0;
419 }
420 
421 int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
422 {
423 	u8 *data = NULL;
424 	u16 size = 0;
425 	int err;
426 
427 	IWL_DEBUG_INFO(phy_db->trans,
428 		       "Sending phy db data and configuration to runtime image\n");
429 
430 	/* Send PHY DB CFG section */
431 	err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
432 					  &data, &size, 0);
433 	if (err) {
434 		IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
435 		return err;
436 	}
437 
438 	err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
439 	if (err) {
440 		IWL_ERR(phy_db->trans,
441 			"Cannot send HCMD of  Phy DB cfg section\n");
442 		return err;
443 	}
444 
445 	err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
446 					  &data, &size, 0);
447 	if (err) {
448 		IWL_ERR(phy_db->trans,
449 			"Cannot get Phy DB non specific channel section\n");
450 		return err;
451 	}
452 
453 	err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
454 	if (err) {
455 		IWL_ERR(phy_db->trans,
456 			"Cannot send HCMD of Phy DB non specific channel section\n");
457 		return err;
458 	}
459 
460 	/* Send all the TXP channel specific data */
461 	err = iwl_phy_db_send_all_channel_groups(phy_db,
462 						 IWL_PHY_DB_CALIB_CHG_PAPD,
463 						 phy_db->n_group_papd);
464 	if (err) {
465 		IWL_ERR(phy_db->trans,
466 			"Cannot send channel specific PAPD groups\n");
467 		return err;
468 	}
469 
470 	/* Send all the TXP channel specific data */
471 	err = iwl_phy_db_send_all_channel_groups(phy_db,
472 						 IWL_PHY_DB_CALIB_CHG_TXP,
473 						 phy_db->n_group_txp);
474 	if (err) {
475 		IWL_ERR(phy_db->trans,
476 			"Cannot send channel specific TX power groups\n");
477 		return err;
478 	}
479 
480 	IWL_DEBUG_INFO(phy_db->trans,
481 		       "Finished sending phy db non channel data\n");
482 	return 0;
483 }
484 IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);
485