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