124a8d46aSMatthew Dillon /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */
224a8d46aSMatthew Dillon
324a8d46aSMatthew Dillon /*
424a8d46aSMatthew Dillon * Copyright (c) 2014 genua mbh <info@genua.de>
524a8d46aSMatthew Dillon * Copyright (c) 2014 Fixup Software Ltd.
624a8d46aSMatthew Dillon *
724a8d46aSMatthew Dillon * Permission to use, copy, modify, and distribute this software for any
824a8d46aSMatthew Dillon * purpose with or without fee is hereby granted, provided that the above
924a8d46aSMatthew Dillon * copyright notice and this permission notice appear in all copies.
1024a8d46aSMatthew Dillon *
1124a8d46aSMatthew Dillon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1224a8d46aSMatthew Dillon * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1324a8d46aSMatthew Dillon * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1424a8d46aSMatthew Dillon * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1524a8d46aSMatthew Dillon * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1624a8d46aSMatthew Dillon * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1724a8d46aSMatthew Dillon * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1824a8d46aSMatthew Dillon */
1924a8d46aSMatthew Dillon
2024a8d46aSMatthew Dillon /*-
2124a8d46aSMatthew Dillon * Based on BSD-licensed source modules in the Linux iwlwifi driver,
2224a8d46aSMatthew Dillon * which were used as the reference documentation for this implementation.
2324a8d46aSMatthew Dillon *
2424a8d46aSMatthew Dillon * Driver version we are currently based off of is
2524a8d46aSMatthew Dillon * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
2624a8d46aSMatthew Dillon *
2724a8d46aSMatthew Dillon ***********************************************************************
2824a8d46aSMatthew Dillon *
2924a8d46aSMatthew Dillon * This file is provided under a dual BSD/GPLv2 license. When using or
3024a8d46aSMatthew Dillon * redistributing this file, you may do so under either license.
3124a8d46aSMatthew Dillon *
3224a8d46aSMatthew Dillon * GPL LICENSE SUMMARY
3324a8d46aSMatthew Dillon *
3424a8d46aSMatthew Dillon * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
3524a8d46aSMatthew Dillon *
3624a8d46aSMatthew Dillon * This program is free software; you can redistribute it and/or modify
3724a8d46aSMatthew Dillon * it under the terms of version 2 of the GNU General Public License as
3824a8d46aSMatthew Dillon * published by the Free Software Foundation.
3924a8d46aSMatthew Dillon *
4024a8d46aSMatthew Dillon * This program is distributed in the hope that it will be useful, but
4124a8d46aSMatthew Dillon * WITHOUT ANY WARRANTY; without even the implied warranty of
4224a8d46aSMatthew Dillon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4324a8d46aSMatthew Dillon * General Public License for more details.
4424a8d46aSMatthew Dillon *
4524a8d46aSMatthew Dillon * You should have received a copy of the GNU General Public License
4624a8d46aSMatthew Dillon * along with this program; if not, write to the Free Software
4724a8d46aSMatthew Dillon * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
4824a8d46aSMatthew Dillon * USA
4924a8d46aSMatthew Dillon *
5024a8d46aSMatthew Dillon * The full GNU General Public License is included in this distribution
5124a8d46aSMatthew Dillon * in the file called COPYING.
5224a8d46aSMatthew Dillon *
5324a8d46aSMatthew Dillon * Contact Information:
5424a8d46aSMatthew Dillon * Intel Linux Wireless <ilw@linux.intel.com>
5524a8d46aSMatthew Dillon * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
5624a8d46aSMatthew Dillon *
5724a8d46aSMatthew Dillon *
5824a8d46aSMatthew Dillon * BSD LICENSE
5924a8d46aSMatthew Dillon *
6024a8d46aSMatthew Dillon * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
6124a8d46aSMatthew Dillon * All rights reserved.
6224a8d46aSMatthew Dillon *
6324a8d46aSMatthew Dillon * Redistribution and use in source and binary forms, with or without
6424a8d46aSMatthew Dillon * modification, are permitted provided that the following conditions
6524a8d46aSMatthew Dillon * are met:
6624a8d46aSMatthew Dillon *
6724a8d46aSMatthew Dillon * * Redistributions of source code must retain the above copyright
6824a8d46aSMatthew Dillon * notice, this list of conditions and the following disclaimer.
6924a8d46aSMatthew Dillon * * Redistributions in binary form must reproduce the above copyright
7024a8d46aSMatthew Dillon * notice, this list of conditions and the following disclaimer in
7124a8d46aSMatthew Dillon * the documentation and/or other materials provided with the
7224a8d46aSMatthew Dillon * distribution.
7324a8d46aSMatthew Dillon * * Neither the name Intel Corporation nor the names of its
7424a8d46aSMatthew Dillon * contributors may be used to endorse or promote products derived
7524a8d46aSMatthew Dillon * from this software without specific prior written permission.
7624a8d46aSMatthew Dillon *
7724a8d46aSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
7824a8d46aSMatthew Dillon * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7924a8d46aSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8024a8d46aSMatthew Dillon * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8124a8d46aSMatthew Dillon * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8224a8d46aSMatthew Dillon * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
8324a8d46aSMatthew Dillon * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
8424a8d46aSMatthew Dillon * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
8524a8d46aSMatthew Dillon * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
8624a8d46aSMatthew Dillon * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
8724a8d46aSMatthew Dillon * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8824a8d46aSMatthew Dillon */
8924a8d46aSMatthew Dillon
9024a8d46aSMatthew Dillon /*-
9124a8d46aSMatthew Dillon * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
9224a8d46aSMatthew Dillon *
9324a8d46aSMatthew Dillon * Permission to use, copy, modify, and distribute this software for any
9424a8d46aSMatthew Dillon * purpose with or without fee is hereby granted, provided that the above
9524a8d46aSMatthew Dillon * copyright notice and this permission notice appear in all copies.
9624a8d46aSMatthew Dillon *
9724a8d46aSMatthew Dillon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9824a8d46aSMatthew Dillon * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9924a8d46aSMatthew Dillon * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10024a8d46aSMatthew Dillon * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
10124a8d46aSMatthew Dillon * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
10224a8d46aSMatthew Dillon * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
10324a8d46aSMatthew Dillon * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
10424a8d46aSMatthew Dillon */
10524a8d46aSMatthew Dillon #include <sys/param.h>
10624a8d46aSMatthew Dillon #include <sys/bus.h>
1076acbba79SMatthew Dillon #include <sys/conf.h>
10824a8d46aSMatthew Dillon #include <sys/endian.h>
10924a8d46aSMatthew Dillon #include <sys/firmware.h>
11024a8d46aSMatthew Dillon #include <sys/kernel.h>
11124a8d46aSMatthew Dillon #include <sys/malloc.h>
11224a8d46aSMatthew Dillon #include <sys/mbuf.h>
11324a8d46aSMatthew Dillon #include <sys/rman.h>
11424a8d46aSMatthew Dillon #include <sys/sysctl.h>
11524a8d46aSMatthew Dillon #include <sys/linker.h>
11624a8d46aSMatthew Dillon
11724a8d46aSMatthew Dillon #include <machine/endian.h>
11824a8d46aSMatthew Dillon
11945bc40b1SMatthew Dillon #include <bus/pci/pcivar.h>
12045bc40b1SMatthew Dillon #include <bus/pci/pcireg.h>
12124a8d46aSMatthew Dillon
12224a8d46aSMatthew Dillon #include <net/bpf.h>
12324a8d46aSMatthew Dillon
12424a8d46aSMatthew Dillon #include <net/if.h>
12524a8d46aSMatthew Dillon #include <net/if_var.h>
12624a8d46aSMatthew Dillon #include <net/if_arp.h>
12724a8d46aSMatthew Dillon #include <net/if_dl.h>
12824a8d46aSMatthew Dillon #include <net/if_media.h>
12924a8d46aSMatthew Dillon #include <net/if_types.h>
13024a8d46aSMatthew Dillon
13124a8d46aSMatthew Dillon #include <netinet/in.h>
13224a8d46aSMatthew Dillon #include <netinet/in_systm.h>
13324a8d46aSMatthew Dillon #include <netinet/if_ether.h>
13424a8d46aSMatthew Dillon #include <netinet/ip.h>
13524a8d46aSMatthew Dillon
13645bc40b1SMatthew Dillon #include <netproto/802_11/ieee80211_var.h>
13745bc40b1SMatthew Dillon #include <netproto/802_11/ieee80211_regdomain.h>
13845bc40b1SMatthew Dillon #include <netproto/802_11/ieee80211_ratectl.h>
13945bc40b1SMatthew Dillon #include <netproto/802_11/ieee80211_radiotap.h>
14024a8d46aSMatthew Dillon
14145bc40b1SMatthew Dillon #include "if_iwmreg.h"
14245bc40b1SMatthew Dillon #include "if_iwmvar.h"
14345bc40b1SMatthew Dillon #include "if_iwm_debug.h"
14445bc40b1SMatthew Dillon #include "if_iwm_util.h"
14545bc40b1SMatthew Dillon #include "if_iwm_phy_db.h"
14624a8d46aSMatthew Dillon
147c1019b6bSImre Vadász #define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */
148c1019b6bSImre Vadász
149c1019b6bSImre Vadász struct iwm_phy_db_entry {
150c1019b6bSImre Vadász uint16_t size;
151c1019b6bSImre Vadász uint8_t *data;
152c1019b6bSImre Vadász };
153c1019b6bSImre Vadász
154c1019b6bSImre Vadász /**
155c1019b6bSImre Vadász * struct iwm_phy_db - stores phy configuration and calibration data.
156c1019b6bSImre Vadász *
157c1019b6bSImre Vadász * @cfg: phy configuration.
158c1019b6bSImre Vadász * @calib_nch: non channel specific calibration data.
159c1019b6bSImre Vadász * @calib_ch: channel specific calibration data.
160c1019b6bSImre Vadász * @n_group_papd: number of entries in papd channel group.
161c1019b6bSImre Vadász * @calib_ch_group_papd: calibration data related to papd channel group.
162c1019b6bSImre Vadász * @n_group_txp: number of entries in tx power channel group.
163c1019b6bSImre Vadász * @calib_ch_group_txp: calibration data related to tx power chanel group.
164c1019b6bSImre Vadász */
165c1019b6bSImre Vadász struct iwm_phy_db {
166c1019b6bSImre Vadász struct iwm_phy_db_entry cfg;
167c1019b6bSImre Vadász struct iwm_phy_db_entry calib_nch;
168c1019b6bSImre Vadász int n_group_papd;
169c1019b6bSImre Vadász struct iwm_phy_db_entry *calib_ch_group_papd;
170c1019b6bSImre Vadász int n_group_txp;
171c1019b6bSImre Vadász struct iwm_phy_db_entry *calib_ch_group_txp;
172c1019b6bSImre Vadász
173c1019b6bSImre Vadász struct iwm_softc *sc;
174c1019b6bSImre Vadász };
175c1019b6bSImre Vadász
176c1019b6bSImre Vadász struct iwm_phy_db *
iwm_phy_db_init(struct iwm_softc * sc)177c1019b6bSImre Vadász iwm_phy_db_init(struct iwm_softc *sc)
178c1019b6bSImre Vadász {
179c1019b6bSImre Vadász struct iwm_phy_db *phy_db = kmalloc(sizeof(struct iwm_phy_db),
180c1019b6bSImre Vadász M_DEVBUF, M_WAITOK | M_ZERO);
181c1019b6bSImre Vadász
182c1019b6bSImre Vadász if (!phy_db)
183c1019b6bSImre Vadász return phy_db;
184c1019b6bSImre Vadász
185c1019b6bSImre Vadász phy_db->sc = sc;
186c1019b6bSImre Vadász
187c1019b6bSImre Vadász phy_db->n_group_txp = -1;
188c1019b6bSImre Vadász phy_db->n_group_papd = -1;
189c1019b6bSImre Vadász
190c1019b6bSImre Vadász /* TODO: add default values of the phy db. */
191c1019b6bSImre Vadász return phy_db;
192c1019b6bSImre Vadász }
193c1019b6bSImre Vadász
19424a8d46aSMatthew Dillon /*
19524a8d46aSMatthew Dillon * get phy db section: returns a pointer to a phy db section specified by
19624a8d46aSMatthew Dillon * type and channel group id.
19724a8d46aSMatthew Dillon */
19824a8d46aSMatthew Dillon static struct iwm_phy_db_entry *
iwm_phy_db_get_section(struct iwm_phy_db * phy_db,enum iwm_phy_db_section_type type,uint16_t chg_id)199c1019b6bSImre Vadász iwm_phy_db_get_section(struct iwm_phy_db *phy_db,
200c1019b6bSImre Vadász enum iwm_phy_db_section_type type,
201c1019b6bSImre Vadász uint16_t chg_id)
20224a8d46aSMatthew Dillon {
203c1019b6bSImre Vadász if (!phy_db || type >= IWM_PHY_DB_MAX)
20424a8d46aSMatthew Dillon return NULL;
20524a8d46aSMatthew Dillon
20624a8d46aSMatthew Dillon switch (type) {
20724a8d46aSMatthew Dillon case IWM_PHY_DB_CFG:
20824a8d46aSMatthew Dillon return &phy_db->cfg;
20924a8d46aSMatthew Dillon case IWM_PHY_DB_CALIB_NCH:
21024a8d46aSMatthew Dillon return &phy_db->calib_nch;
21124a8d46aSMatthew Dillon case IWM_PHY_DB_CALIB_CHG_PAPD:
212c1019b6bSImre Vadász if (chg_id >= phy_db->n_group_papd)
21324a8d46aSMatthew Dillon return NULL;
21424a8d46aSMatthew Dillon return &phy_db->calib_ch_group_papd[chg_id];
21524a8d46aSMatthew Dillon case IWM_PHY_DB_CALIB_CHG_TXP:
216c1019b6bSImre Vadász if (chg_id >= phy_db->n_group_txp)
21724a8d46aSMatthew Dillon return NULL;
21824a8d46aSMatthew Dillon return &phy_db->calib_ch_group_txp[chg_id];
21924a8d46aSMatthew Dillon default:
22024a8d46aSMatthew Dillon return NULL;
22124a8d46aSMatthew Dillon }
22224a8d46aSMatthew Dillon return NULL;
22324a8d46aSMatthew Dillon }
22424a8d46aSMatthew Dillon
225c1019b6bSImre Vadász static void
iwm_phy_db_free_section(struct iwm_phy_db * phy_db,enum iwm_phy_db_section_type type,uint16_t chg_id)226c1019b6bSImre Vadász iwm_phy_db_free_section(struct iwm_phy_db *phy_db,
227c1019b6bSImre Vadász enum iwm_phy_db_section_type type, uint16_t chg_id)
228c1019b6bSImre Vadász {
229c1019b6bSImre Vadász struct iwm_phy_db_entry *entry =
230c1019b6bSImre Vadász iwm_phy_db_get_section(phy_db, type, chg_id);
231c1019b6bSImre Vadász if (!entry)
232c1019b6bSImre Vadász return;
233c1019b6bSImre Vadász
234c1019b6bSImre Vadász if (entry->data != NULL)
235c1019b6bSImre Vadász kfree(entry->data, M_DEVBUF);
236c1019b6bSImre Vadász entry->data = NULL;
237c1019b6bSImre Vadász entry->size = 0;
238c1019b6bSImre Vadász }
239c1019b6bSImre Vadász
240c1019b6bSImre Vadász void
iwm_phy_db_free(struct iwm_phy_db * phy_db)241c1019b6bSImre Vadász iwm_phy_db_free(struct iwm_phy_db *phy_db)
242c1019b6bSImre Vadász {
243c1019b6bSImre Vadász int i;
244c1019b6bSImre Vadász
245c1019b6bSImre Vadász if (!phy_db)
246c1019b6bSImre Vadász return;
247c1019b6bSImre Vadász
248c1019b6bSImre Vadász iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CFG, 0);
249c1019b6bSImre Vadász iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_NCH, 0);
250c1019b6bSImre Vadász
251c1019b6bSImre Vadász for (i = 0; i < phy_db->n_group_papd; i++)
252c1019b6bSImre Vadász iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_CHG_PAPD, i);
253c1019b6bSImre Vadász if (phy_db->calib_ch_group_papd != NULL)
254c1019b6bSImre Vadász kfree(phy_db->calib_ch_group_papd, M_DEVBUF);
255c1019b6bSImre Vadász
256c1019b6bSImre Vadász for (i = 0; i < phy_db->n_group_txp; i++)
257c1019b6bSImre Vadász iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_CHG_TXP, i);
258c1019b6bSImre Vadász if (phy_db->calib_ch_group_txp != NULL)
259c1019b6bSImre Vadász kfree(phy_db->calib_ch_group_txp, M_DEVBUF);
260c1019b6bSImre Vadász
261c1019b6bSImre Vadász kfree(phy_db, M_DEVBUF);
262c1019b6bSImre Vadász }
263c1019b6bSImre Vadász
26424a8d46aSMatthew Dillon int
iwm_phy_db_set_section(struct iwm_phy_db * phy_db,struct iwm_rx_packet * pkt)265c1019b6bSImre Vadász iwm_phy_db_set_section(struct iwm_phy_db *phy_db,
266de7995a5SImre Vadász struct iwm_rx_packet *pkt)
26724a8d46aSMatthew Dillon {
268de7995a5SImre Vadász struct iwm_calib_res_notif_phy_db *phy_db_notif =
269de7995a5SImre Vadász (struct iwm_calib_res_notif_phy_db *)pkt->data;
27024a8d46aSMatthew Dillon enum iwm_phy_db_section_type type = le16toh(phy_db_notif->type);
27124a8d46aSMatthew Dillon uint16_t size = le16toh(phy_db_notif->length);
27224a8d46aSMatthew Dillon struct iwm_phy_db_entry *entry;
27324a8d46aSMatthew Dillon uint16_t chg_id = 0;
27424a8d46aSMatthew Dillon
275c1019b6bSImre Vadász if (!phy_db)
276c1019b6bSImre Vadász return EINVAL;
27724a8d46aSMatthew Dillon
278c1019b6bSImre Vadász if (type == IWM_PHY_DB_CALIB_CHG_PAPD) {
279c1019b6bSImre Vadász chg_id = le16toh(*(uint16_t *)phy_db_notif->data);
280c1019b6bSImre Vadász if (phy_db && !phy_db->calib_ch_group_papd) {
281c1019b6bSImre Vadász /*
282c1019b6bSImre Vadász * Firmware sends the largest index first, so we can use
283c1019b6bSImre Vadász * it to know how much we should allocate.
284c1019b6bSImre Vadász */
285c1019b6bSImre Vadász phy_db->calib_ch_group_papd = kmalloc(
286c1019b6bSImre Vadász (chg_id + 1) * sizeof(struct iwm_phy_db_entry),
287c1019b6bSImre Vadász M_DEVBUF, M_WAITOK | M_ZERO);
288c1019b6bSImre Vadász if (!phy_db->calib_ch_group_papd)
289c1019b6bSImre Vadász return ENOMEM;
290c1019b6bSImre Vadász phy_db->n_group_papd = chg_id + 1;
291c1019b6bSImre Vadász }
292c1019b6bSImre Vadász } else if (type == IWM_PHY_DB_CALIB_CHG_TXP) {
293c1019b6bSImre Vadász chg_id = le16toh(*(uint16_t *)phy_db_notif->data);
294c1019b6bSImre Vadász if (phy_db && !phy_db->calib_ch_group_txp) {
295c1019b6bSImre Vadász /*
296c1019b6bSImre Vadász * Firmware sends the largest index first, so we can use
297c1019b6bSImre Vadász * it to know how much we should allocate.
298c1019b6bSImre Vadász */
299c1019b6bSImre Vadász phy_db->calib_ch_group_txp = kmalloc(
300c1019b6bSImre Vadász (chg_id + 1) * sizeof(struct iwm_phy_db_entry),
301c1019b6bSImre Vadász M_DEVBUF, M_WAITOK | M_ZERO);
302c1019b6bSImre Vadász if (!phy_db->calib_ch_group_txp)
303c1019b6bSImre Vadász return ENOMEM;
304c1019b6bSImre Vadász phy_db->n_group_txp = chg_id + 1;
305c1019b6bSImre Vadász }
306c1019b6bSImre Vadász }
307c1019b6bSImre Vadász
308c1019b6bSImre Vadász entry = iwm_phy_db_get_section(phy_db, type, chg_id);
30924a8d46aSMatthew Dillon if (!entry)
31024a8d46aSMatthew Dillon return EINVAL;
31124a8d46aSMatthew Dillon
312c1019b6bSImre Vadász if (entry->data != NULL)
31345bc40b1SMatthew Dillon kfree(entry->data, M_DEVBUF);
314c1019b6bSImre Vadász entry->data = kmalloc(size, M_DEVBUF, M_WAITOK);
31524a8d46aSMatthew Dillon if (!entry->data) {
31624a8d46aSMatthew Dillon entry->size = 0;
31724a8d46aSMatthew Dillon return ENOMEM;
31824a8d46aSMatthew Dillon }
31924a8d46aSMatthew Dillon memcpy(entry->data, phy_db_notif->data, size);
320c1019b6bSImre Vadász
32124a8d46aSMatthew Dillon entry->size = size;
32224a8d46aSMatthew Dillon
323c1019b6bSImre Vadász IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET,
324c1019b6bSImre Vadász "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
325c1019b6bSImre Vadász __func__, __LINE__, type, size);
32624a8d46aSMatthew Dillon
32724a8d46aSMatthew Dillon return 0;
32824a8d46aSMatthew Dillon }
32924a8d46aSMatthew Dillon
33024a8d46aSMatthew Dillon static int
is_valid_channel(uint16_t ch_id)331c1019b6bSImre Vadász is_valid_channel(uint16_t ch_id)
33224a8d46aSMatthew Dillon {
33324a8d46aSMatthew Dillon if (ch_id <= 14 ||
33424a8d46aSMatthew Dillon (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
33524a8d46aSMatthew Dillon (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
33624a8d46aSMatthew Dillon (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
33724a8d46aSMatthew Dillon return 1;
33824a8d46aSMatthew Dillon return 0;
33924a8d46aSMatthew Dillon }
34024a8d46aSMatthew Dillon
34124a8d46aSMatthew Dillon static uint8_t
ch_id_to_ch_index(uint16_t ch_id)342c1019b6bSImre Vadász ch_id_to_ch_index(uint16_t ch_id)
34324a8d46aSMatthew Dillon {
344c1019b6bSImre Vadász if (!is_valid_channel(ch_id))
34524a8d46aSMatthew Dillon return 0xff;
34624a8d46aSMatthew Dillon
34724a8d46aSMatthew Dillon if (ch_id <= 14)
34824a8d46aSMatthew Dillon return ch_id - 1;
34924a8d46aSMatthew Dillon if (ch_id <= 64)
35024a8d46aSMatthew Dillon return (ch_id + 20) / 4;
35124a8d46aSMatthew Dillon if (ch_id <= 140)
35224a8d46aSMatthew Dillon return (ch_id - 12) / 4;
35324a8d46aSMatthew Dillon return (ch_id - 13) / 4;
35424a8d46aSMatthew Dillon }
35524a8d46aSMatthew Dillon
35624a8d46aSMatthew Dillon
35724a8d46aSMatthew Dillon static uint16_t
channel_id_to_papd(uint16_t ch_id)358c1019b6bSImre Vadász channel_id_to_papd(uint16_t ch_id)
35924a8d46aSMatthew Dillon {
360c1019b6bSImre Vadász if (!is_valid_channel(ch_id))
36124a8d46aSMatthew Dillon return 0xff;
36224a8d46aSMatthew Dillon
36324a8d46aSMatthew Dillon if (1 <= ch_id && ch_id <= 14)
36424a8d46aSMatthew Dillon return 0;
36524a8d46aSMatthew Dillon if (36 <= ch_id && ch_id <= 64)
36624a8d46aSMatthew Dillon return 1;
36724a8d46aSMatthew Dillon if (100 <= ch_id && ch_id <= 140)
36824a8d46aSMatthew Dillon return 2;
36924a8d46aSMatthew Dillon return 3;
37024a8d46aSMatthew Dillon }
37124a8d46aSMatthew Dillon
37224a8d46aSMatthew Dillon static uint16_t
channel_id_to_txp(struct iwm_phy_db * phy_db,uint16_t ch_id)373c1019b6bSImre Vadász channel_id_to_txp(struct iwm_phy_db *phy_db, uint16_t ch_id)
37424a8d46aSMatthew Dillon {
37524a8d46aSMatthew Dillon struct iwm_phy_db_chg_txp *txp_chg;
37624a8d46aSMatthew Dillon int i;
377c1019b6bSImre Vadász uint8_t ch_index = ch_id_to_ch_index(ch_id);
37824a8d46aSMatthew Dillon if (ch_index == 0xff)
37924a8d46aSMatthew Dillon return 0xff;
38024a8d46aSMatthew Dillon
381c1019b6bSImre Vadász for (i = 0; i < phy_db->n_group_txp; i++) {
38224a8d46aSMatthew Dillon txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
38324a8d46aSMatthew Dillon if (!txp_chg)
38424a8d46aSMatthew Dillon return 0xff;
38524a8d46aSMatthew Dillon /*
38624a8d46aSMatthew Dillon * Looking for the first channel group that its max channel is
38724a8d46aSMatthew Dillon * higher then wanted channel.
38824a8d46aSMatthew Dillon */
38924a8d46aSMatthew Dillon if (le16toh(txp_chg->max_channel_idx) >= ch_index)
39024a8d46aSMatthew Dillon return i;
39124a8d46aSMatthew Dillon }
39224a8d46aSMatthew Dillon return 0xff;
39324a8d46aSMatthew Dillon }
39424a8d46aSMatthew Dillon
39524a8d46aSMatthew Dillon static int
iwm_phy_db_get_section_data(struct iwm_phy_db * phy_db,uint32_t type,uint8_t ** data,uint16_t * size,uint16_t ch_id)396c1019b6bSImre Vadász iwm_phy_db_get_section_data(struct iwm_phy_db *phy_db,
397c1019b6bSImre Vadász uint32_t type, uint8_t **data, uint16_t *size,
398c1019b6bSImre Vadász uint16_t ch_id)
39924a8d46aSMatthew Dillon {
40024a8d46aSMatthew Dillon struct iwm_phy_db_entry *entry;
40124a8d46aSMatthew Dillon uint16_t ch_group_id = 0;
40224a8d46aSMatthew Dillon
403c1019b6bSImre Vadász if (!phy_db)
404c1019b6bSImre Vadász return EINVAL;
405c1019b6bSImre Vadász
40624a8d46aSMatthew Dillon /* find wanted channel group */
40724a8d46aSMatthew Dillon if (type == IWM_PHY_DB_CALIB_CHG_PAPD)
408c1019b6bSImre Vadász ch_group_id = channel_id_to_papd(ch_id);
40924a8d46aSMatthew Dillon else if (type == IWM_PHY_DB_CALIB_CHG_TXP)
410c1019b6bSImre Vadász ch_group_id = channel_id_to_txp(phy_db, ch_id);
41124a8d46aSMatthew Dillon
412c1019b6bSImre Vadász entry = iwm_phy_db_get_section(phy_db, type, ch_group_id);
41324a8d46aSMatthew Dillon if (!entry)
41424a8d46aSMatthew Dillon return EINVAL;
41524a8d46aSMatthew Dillon
41624a8d46aSMatthew Dillon *data = entry->data;
41724a8d46aSMatthew Dillon *size = entry->size;
41824a8d46aSMatthew Dillon
419c1019b6bSImre Vadász IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET,
42024a8d46aSMatthew Dillon "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
42124a8d46aSMatthew Dillon __func__, __LINE__, type, *size);
42224a8d46aSMatthew Dillon
42324a8d46aSMatthew Dillon return 0;
42424a8d46aSMatthew Dillon }
42524a8d46aSMatthew Dillon
42624a8d46aSMatthew Dillon static int
iwm_send_phy_db_cmd(struct iwm_phy_db * phy_db,uint16_t type,uint16_t length,void * data)427c1019b6bSImre Vadász iwm_send_phy_db_cmd(struct iwm_phy_db *phy_db, uint16_t type,
42824a8d46aSMatthew Dillon uint16_t length, void *data)
42924a8d46aSMatthew Dillon {
43024a8d46aSMatthew Dillon struct iwm_phy_db_cmd phy_db_cmd;
43124a8d46aSMatthew Dillon struct iwm_host_cmd cmd = {
432*4cbc7cf9SMichael Neumann .id = IWM_PHY_DB_CMD,
43324a8d46aSMatthew Dillon };
43424a8d46aSMatthew Dillon
435c1019b6bSImre Vadász IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET,
43624a8d46aSMatthew Dillon "Sending PHY-DB hcmd of type %d, of length %d\n",
43724a8d46aSMatthew Dillon type, length);
43824a8d46aSMatthew Dillon
43924a8d46aSMatthew Dillon /* Set phy db cmd variables */
440c1019b6bSImre Vadász phy_db_cmd.type = htole16(type);
441c1019b6bSImre Vadász phy_db_cmd.length = htole16(length);
44224a8d46aSMatthew Dillon
44324a8d46aSMatthew Dillon /* Set hcmd variables */
44424a8d46aSMatthew Dillon cmd.data[0] = &phy_db_cmd;
44524a8d46aSMatthew Dillon cmd.len[0] = sizeof(struct iwm_phy_db_cmd);
44624a8d46aSMatthew Dillon cmd.data[1] = data;
44724a8d46aSMatthew Dillon cmd.len[1] = length;
448c1019b6bSImre Vadász #ifdef notyet
449c1019b6bSImre Vadász cmd.dataflags[1] = IWM_HCMD_DFL_NOCOPY;
450c1019b6bSImre Vadász #endif
45124a8d46aSMatthew Dillon
452c1019b6bSImre Vadász return iwm_send_cmd(phy_db->sc, &cmd);
45324a8d46aSMatthew Dillon }
45424a8d46aSMatthew Dillon
45524a8d46aSMatthew Dillon static int
iwm_phy_db_send_all_channel_groups(struct iwm_phy_db * phy_db,enum iwm_phy_db_section_type type,uint8_t max_ch_groups)456c1019b6bSImre Vadász iwm_phy_db_send_all_channel_groups(struct iwm_phy_db *phy_db,
457c1019b6bSImre Vadász enum iwm_phy_db_section_type type,
458c1019b6bSImre Vadász uint8_t max_ch_groups)
45924a8d46aSMatthew Dillon {
46024a8d46aSMatthew Dillon uint16_t i;
46124a8d46aSMatthew Dillon int err;
46224a8d46aSMatthew Dillon struct iwm_phy_db_entry *entry;
46324a8d46aSMatthew Dillon
464c1019b6bSImre Vadász /* Send all the channel specific groups to operational fw */
46524a8d46aSMatthew Dillon for (i = 0; i < max_ch_groups; i++) {
466c1019b6bSImre Vadász entry = iwm_phy_db_get_section(phy_db,
467c1019b6bSImre Vadász type,
468c1019b6bSImre Vadász i);
46924a8d46aSMatthew Dillon if (!entry)
47024a8d46aSMatthew Dillon return EINVAL;
47124a8d46aSMatthew Dillon
47224a8d46aSMatthew Dillon if (!entry->size)
47324a8d46aSMatthew Dillon continue;
47424a8d46aSMatthew Dillon
47524a8d46aSMatthew Dillon /* Send the requested PHY DB section */
476c1019b6bSImre Vadász err = iwm_send_phy_db_cmd(phy_db,
477c1019b6bSImre Vadász type,
478c1019b6bSImre Vadász entry->size,
479c1019b6bSImre Vadász entry->data);
48024a8d46aSMatthew Dillon if (err) {
481c1019b6bSImre Vadász device_printf(phy_db->sc->sc_dev,
482c1019b6bSImre Vadász "Can't SEND phy_db section %d (%d), err %d\n",
483c1019b6bSImre Vadász type, i, err);
48424a8d46aSMatthew Dillon return err;
48524a8d46aSMatthew Dillon }
48624a8d46aSMatthew Dillon
487c1019b6bSImre Vadász IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD,
48824a8d46aSMatthew Dillon "Sent PHY_DB HCMD, type = %d num = %d\n", type, i);
48924a8d46aSMatthew Dillon }
49024a8d46aSMatthew Dillon
49124a8d46aSMatthew Dillon return 0;
49224a8d46aSMatthew Dillon }
49324a8d46aSMatthew Dillon
49424a8d46aSMatthew Dillon int
iwm_send_phy_db_data(struct iwm_phy_db * phy_db)495c1019b6bSImre Vadász iwm_send_phy_db_data(struct iwm_phy_db *phy_db)
49624a8d46aSMatthew Dillon {
49724a8d46aSMatthew Dillon uint8_t *data = NULL;
49824a8d46aSMatthew Dillon uint16_t size = 0;
49924a8d46aSMatthew Dillon int err;
50024a8d46aSMatthew Dillon
501c1019b6bSImre Vadász IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET,
50224a8d46aSMatthew Dillon "%s: Sending phy db data and configuration to runtime image\n",
50324a8d46aSMatthew Dillon __func__);
50424a8d46aSMatthew Dillon
50524a8d46aSMatthew Dillon /* Send PHY DB CFG section */
506c1019b6bSImre Vadász err = iwm_phy_db_get_section_data(phy_db, IWM_PHY_DB_CFG,
507c1019b6bSImre Vadász &data, &size, 0);
50824a8d46aSMatthew Dillon if (err) {
509c1019b6bSImre Vadász device_printf(phy_db->sc->sc_dev,
51024a8d46aSMatthew Dillon "%s: Cannot get Phy DB cfg section, %d\n",
51124a8d46aSMatthew Dillon __func__, err);
51224a8d46aSMatthew Dillon return err;
51324a8d46aSMatthew Dillon }
51424a8d46aSMatthew Dillon
515c1019b6bSImre Vadász err = iwm_send_phy_db_cmd(phy_db, IWM_PHY_DB_CFG, size, data);
51624a8d46aSMatthew Dillon if (err) {
517c1019b6bSImre Vadász device_printf(phy_db->sc->sc_dev,
51824a8d46aSMatthew Dillon "%s: Cannot send HCMD of Phy DB cfg section, %d\n",
51924a8d46aSMatthew Dillon __func__, err);
52024a8d46aSMatthew Dillon return err;
52124a8d46aSMatthew Dillon }
52224a8d46aSMatthew Dillon
523c1019b6bSImre Vadász err = iwm_phy_db_get_section_data(phy_db, IWM_PHY_DB_CALIB_NCH,
52424a8d46aSMatthew Dillon &data, &size, 0);
52524a8d46aSMatthew Dillon if (err) {
526c1019b6bSImre Vadász device_printf(phy_db->sc->sc_dev,
52724a8d46aSMatthew Dillon "%s: Cannot get Phy DB non specific channel section, "
52824a8d46aSMatthew Dillon "%d\n", __func__, err);
52924a8d46aSMatthew Dillon return err;
53024a8d46aSMatthew Dillon }
53124a8d46aSMatthew Dillon
532c1019b6bSImre Vadász err = iwm_send_phy_db_cmd(phy_db, IWM_PHY_DB_CALIB_NCH, size, data);
53324a8d46aSMatthew Dillon if (err) {
534c1019b6bSImre Vadász device_printf(phy_db->sc->sc_dev,
53524a8d46aSMatthew Dillon "%s: Cannot send HCMD of Phy DB non specific channel "
53624a8d46aSMatthew Dillon "sect, %d\n", __func__, err);
53724a8d46aSMatthew Dillon return err;
53824a8d46aSMatthew Dillon }
53924a8d46aSMatthew Dillon
54024a8d46aSMatthew Dillon /* Send all the TXP channel specific data */
541c1019b6bSImre Vadász err = iwm_phy_db_send_all_channel_groups(phy_db,
542c1019b6bSImre Vadász IWM_PHY_DB_CALIB_CHG_PAPD, phy_db->n_group_papd);
54324a8d46aSMatthew Dillon if (err) {
544c1019b6bSImre Vadász device_printf(phy_db->sc->sc_dev,
54524a8d46aSMatthew Dillon "%s: Cannot send channel specific PAPD groups, %d\n",
54624a8d46aSMatthew Dillon __func__, err);
54724a8d46aSMatthew Dillon return err;
54824a8d46aSMatthew Dillon }
54924a8d46aSMatthew Dillon
55024a8d46aSMatthew Dillon /* Send all the TXP channel specific data */
551c1019b6bSImre Vadász err = iwm_phy_db_send_all_channel_groups(phy_db,
552c1019b6bSImre Vadász IWM_PHY_DB_CALIB_CHG_TXP, phy_db->n_group_txp);
55324a8d46aSMatthew Dillon if (err) {
554c1019b6bSImre Vadász device_printf(phy_db->sc->sc_dev,
55524a8d46aSMatthew Dillon "%s: Cannot send channel specific TX power groups, "
55624a8d46aSMatthew Dillon "%d\n", __func__, err);
55724a8d46aSMatthew Dillon return err;
55824a8d46aSMatthew Dillon }
55924a8d46aSMatthew Dillon
560c1019b6bSImre Vadász IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET,
56124a8d46aSMatthew Dillon "%s: Finished sending phy db non channel data\n",
56224a8d46aSMatthew Dillon __func__);
56324a8d46aSMatthew Dillon return 0;
56424a8d46aSMatthew Dillon }
565