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