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