Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /*
0003  * Copyright (C) 2005-2014, 2020-2021 Intel Corporation
0004  * Copyright (C) 2016 Intel Deutschland GmbH
0005  */
0006 #include <linux/slab.h>
0007 #include <linux/string.h>
0008 #include <linux/export.h>
0009 
0010 #include "iwl-drv.h"
0011 #include "iwl-phy-db.h"
0012 #include "iwl-debug.h"
0013 #include "iwl-op-mode.h"
0014 #include "iwl-trans.h"
0015 
0016 struct iwl_phy_db_entry {
0017     u16 size;
0018     u8  *data;
0019 };
0020 
0021 /**
0022  * struct iwl_phy_db - stores phy configuration and calibration data.
0023  *
0024  * @cfg: phy configuration.
0025  * @calib_nch: non channel specific calibration data.
0026  * @n_group_papd: number of entries in papd channel group.
0027  * @calib_ch_group_papd: calibration data related to papd channel group.
0028  * @n_group_txp: number of entries in tx power channel group.
0029  * @calib_ch_group_txp: calibration data related to tx power chanel group.
0030  * @trans: transport layer
0031  */
0032 struct iwl_phy_db {
0033     struct iwl_phy_db_entry cfg;
0034     struct iwl_phy_db_entry calib_nch;
0035     int n_group_papd;
0036     struct iwl_phy_db_entry *calib_ch_group_papd;
0037     int n_group_txp;
0038     struct iwl_phy_db_entry *calib_ch_group_txp;
0039 
0040     struct iwl_trans *trans;
0041 };
0042 
0043 enum iwl_phy_db_section_type {
0044     IWL_PHY_DB_CFG = 1,
0045     IWL_PHY_DB_CALIB_NCH,
0046     IWL_PHY_DB_UNUSED,
0047     IWL_PHY_DB_CALIB_CHG_PAPD,
0048     IWL_PHY_DB_CALIB_CHG_TXP,
0049     IWL_PHY_DB_MAX
0050 };
0051 
0052 #define PHY_DB_CMD 0x6c
0053 
0054 /* for parsing of tx power channel group data that comes from the firmware*/
0055 struct iwl_phy_db_chg_txp {
0056     __le32 space;
0057     __le16 max_channel_idx;
0058 } __packed;
0059 
0060 struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
0061 {
0062     struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
0063                         GFP_KERNEL);
0064 
0065     if (!phy_db)
0066         return phy_db;
0067 
0068     phy_db->trans = trans;
0069 
0070     phy_db->n_group_txp = -1;
0071     phy_db->n_group_papd = -1;
0072 
0073     /* TODO: add default values of the phy db. */
0074     return phy_db;
0075 }
0076 IWL_EXPORT_SYMBOL(iwl_phy_db_init);
0077 
0078 /*
0079  * get phy db section: returns a pointer to a phy db section specified by
0080  * type and channel group id.
0081  */
0082 static struct iwl_phy_db_entry *
0083 iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
0084                enum iwl_phy_db_section_type type,
0085                u16 chg_id)
0086 {
0087     if (!phy_db || type >= IWL_PHY_DB_MAX)
0088         return NULL;
0089 
0090     switch (type) {
0091     case IWL_PHY_DB_CFG:
0092         return &phy_db->cfg;
0093     case IWL_PHY_DB_CALIB_NCH:
0094         return &phy_db->calib_nch;
0095     case IWL_PHY_DB_CALIB_CHG_PAPD:
0096         if (chg_id >= phy_db->n_group_papd)
0097             return NULL;
0098         return &phy_db->calib_ch_group_papd[chg_id];
0099     case IWL_PHY_DB_CALIB_CHG_TXP:
0100         if (chg_id >= phy_db->n_group_txp)
0101             return NULL;
0102         return &phy_db->calib_ch_group_txp[chg_id];
0103     default:
0104         return NULL;
0105     }
0106     return NULL;
0107 }
0108 
0109 static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
0110                     enum iwl_phy_db_section_type type,
0111                     u16 chg_id)
0112 {
0113     struct iwl_phy_db_entry *entry =
0114                 iwl_phy_db_get_section(phy_db, type, chg_id);
0115     if (!entry)
0116         return;
0117 
0118     kfree(entry->data);
0119     entry->data = NULL;
0120     entry->size = 0;
0121 }
0122 
0123 void iwl_phy_db_free(struct iwl_phy_db *phy_db)
0124 {
0125     int i;
0126 
0127     if (!phy_db)
0128         return;
0129 
0130     iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
0131     iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
0132 
0133     for (i = 0; i < phy_db->n_group_papd; i++)
0134         iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
0135     kfree(phy_db->calib_ch_group_papd);
0136 
0137     for (i = 0; i < phy_db->n_group_txp; i++)
0138         iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
0139     kfree(phy_db->calib_ch_group_txp);
0140 
0141     kfree(phy_db);
0142 }
0143 IWL_EXPORT_SYMBOL(iwl_phy_db_free);
0144 
0145 int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
0146                struct iwl_rx_packet *pkt)
0147 {
0148     unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
0149     struct iwl_calib_res_notif_phy_db *phy_db_notif =
0150             (struct iwl_calib_res_notif_phy_db *)pkt->data;
0151     enum iwl_phy_db_section_type type;
0152     u16 size;
0153     struct iwl_phy_db_entry *entry;
0154     u16 chg_id = 0;
0155 
0156     if (pkt_len < sizeof(*phy_db_notif))
0157         return -EINVAL;
0158 
0159     type = le16_to_cpu(phy_db_notif->type);
0160     size = le16_to_cpu(phy_db_notif->length);
0161 
0162     if (pkt_len < sizeof(*phy_db_notif) + size)
0163         return -EINVAL;
0164 
0165     if (!phy_db)
0166         return -EINVAL;
0167 
0168     if (type == IWL_PHY_DB_CALIB_CHG_PAPD) {
0169         chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
0170         if (phy_db && !phy_db->calib_ch_group_papd) {
0171             /*
0172              * Firmware sends the largest index first, so we can use
0173              * it to know how much we should allocate.
0174              */
0175             phy_db->calib_ch_group_papd = kcalloc(chg_id + 1,
0176                                   sizeof(struct iwl_phy_db_entry),
0177                                   GFP_ATOMIC);
0178             if (!phy_db->calib_ch_group_papd)
0179                 return -ENOMEM;
0180             phy_db->n_group_papd = chg_id + 1;
0181         }
0182     } else if (type == IWL_PHY_DB_CALIB_CHG_TXP) {
0183         chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
0184         if (phy_db && !phy_db->calib_ch_group_txp) {
0185             /*
0186              * Firmware sends the largest index first, so we can use
0187              * it to know how much we should allocate.
0188              */
0189             phy_db->calib_ch_group_txp = kcalloc(chg_id + 1,
0190                                  sizeof(struct iwl_phy_db_entry),
0191                                  GFP_ATOMIC);
0192             if (!phy_db->calib_ch_group_txp)
0193                 return -ENOMEM;
0194             phy_db->n_group_txp = chg_id + 1;
0195         }
0196     }
0197 
0198     entry = iwl_phy_db_get_section(phy_db, type, chg_id);
0199     if (!entry)
0200         return -EINVAL;
0201 
0202     kfree(entry->data);
0203     entry->data = kmemdup(phy_db_notif->data, size, GFP_ATOMIC);
0204     if (!entry->data) {
0205         entry->size = 0;
0206         return -ENOMEM;
0207     }
0208 
0209     entry->size = size;
0210 
0211     IWL_DEBUG_INFO(phy_db->trans,
0212                "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
0213                __func__, __LINE__, type, size);
0214 
0215     return 0;
0216 }
0217 IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
0218 
0219 static int is_valid_channel(u16 ch_id)
0220 {
0221     if (ch_id <= 14 ||
0222         (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
0223         (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
0224         (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
0225         return 1;
0226     return 0;
0227 }
0228 
0229 static u8 ch_id_to_ch_index(u16 ch_id)
0230 {
0231     if (WARN_ON(!is_valid_channel(ch_id)))
0232         return 0xff;
0233 
0234     if (ch_id <= 14)
0235         return ch_id - 1;
0236     if (ch_id <= 64)
0237         return (ch_id + 20) / 4;
0238     if (ch_id <= 140)
0239         return (ch_id - 12) / 4;
0240     return (ch_id - 13) / 4;
0241 }
0242 
0243 
0244 static u16 channel_id_to_papd(u16 ch_id)
0245 {
0246     if (WARN_ON(!is_valid_channel(ch_id)))
0247         return 0xff;
0248 
0249     if (1 <= ch_id && ch_id <= 14)
0250         return 0;
0251     if (36 <= ch_id && ch_id <= 64)
0252         return 1;
0253     if (100 <= ch_id && ch_id <= 140)
0254         return 2;
0255     return 3;
0256 }
0257 
0258 static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
0259 {
0260     struct iwl_phy_db_chg_txp *txp_chg;
0261     int i;
0262     u8 ch_index = ch_id_to_ch_index(ch_id);
0263     if (ch_index == 0xff)
0264         return 0xff;
0265 
0266     for (i = 0; i < phy_db->n_group_txp; i++) {
0267         txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
0268         if (!txp_chg)
0269             return 0xff;
0270         /*
0271          * Looking for the first channel group that its max channel is
0272          * higher then wanted channel.
0273          */
0274         if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
0275             return i;
0276     }
0277     return 0xff;
0278 }
0279 static
0280 int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
0281                 u32 type, u8 **data, u16 *size, u16 ch_id)
0282 {
0283     struct iwl_phy_db_entry *entry;
0284     u16 ch_group_id = 0;
0285 
0286     if (!phy_db)
0287         return -EINVAL;
0288 
0289     /* find wanted channel group */
0290     if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
0291         ch_group_id = channel_id_to_papd(ch_id);
0292     else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
0293         ch_group_id = channel_id_to_txp(phy_db, ch_id);
0294 
0295     entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
0296     if (!entry)
0297         return -EINVAL;
0298 
0299     *data = entry->data;
0300     *size = entry->size;
0301 
0302     IWL_DEBUG_INFO(phy_db->trans,
0303                "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
0304                __func__, __LINE__, type, *size);
0305 
0306     return 0;
0307 }
0308 
0309 static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
0310                    u16 length, void *data)
0311 {
0312     struct iwl_phy_db_cmd phy_db_cmd;
0313     struct iwl_host_cmd cmd = {
0314         .id = PHY_DB_CMD,
0315     };
0316 
0317     IWL_DEBUG_INFO(phy_db->trans,
0318                "Sending PHY-DB hcmd of type %d, of length %d\n",
0319                type, length);
0320 
0321     /* Set phy db cmd variables */
0322     phy_db_cmd.type = cpu_to_le16(type);
0323     phy_db_cmd.length = cpu_to_le16(length);
0324 
0325     /* Set hcmd variables */
0326     cmd.data[0] = &phy_db_cmd;
0327     cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
0328     cmd.data[1] = data;
0329     cmd.len[1] = length;
0330     cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
0331 
0332     return iwl_trans_send_cmd(phy_db->trans, &cmd);
0333 }
0334 
0335 static int iwl_phy_db_send_all_channel_groups(
0336                     struct iwl_phy_db *phy_db,
0337                     enum iwl_phy_db_section_type type,
0338                     u8 max_ch_groups)
0339 {
0340     u16 i;
0341     int err;
0342     struct iwl_phy_db_entry *entry;
0343 
0344     /* Send all the  channel specific groups to operational fw */
0345     for (i = 0; i < max_ch_groups; i++) {
0346         entry = iwl_phy_db_get_section(phy_db,
0347                            type,
0348                            i);
0349         if (!entry)
0350             return -EINVAL;
0351 
0352         if (!entry->size)
0353             continue;
0354 
0355         /* Send the requested PHY DB section */
0356         err = iwl_send_phy_db_cmd(phy_db,
0357                       type,
0358                       entry->size,
0359                       entry->data);
0360         if (err) {
0361             IWL_ERR(phy_db->trans,
0362                 "Can't SEND phy_db section %d (%d), err %d\n",
0363                 type, i, err);
0364             return err;
0365         }
0366 
0367         IWL_DEBUG_INFO(phy_db->trans,
0368                    "Sent PHY_DB HCMD, type = %d num = %d\n",
0369                    type, i);
0370     }
0371 
0372     return 0;
0373 }
0374 
0375 int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
0376 {
0377     u8 *data = NULL;
0378     u16 size = 0;
0379     int err;
0380 
0381     IWL_DEBUG_INFO(phy_db->trans,
0382                "Sending phy db data and configuration to runtime image\n");
0383 
0384     /* Send PHY DB CFG section */
0385     err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
0386                       &data, &size, 0);
0387     if (err) {
0388         IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
0389         return err;
0390     }
0391 
0392     err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
0393     if (err) {
0394         IWL_ERR(phy_db->trans,
0395             "Cannot send HCMD of  Phy DB cfg section\n");
0396         return err;
0397     }
0398 
0399     err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
0400                       &data, &size, 0);
0401     if (err) {
0402         IWL_ERR(phy_db->trans,
0403             "Cannot get Phy DB non specific channel section\n");
0404         return err;
0405     }
0406 
0407     err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
0408     if (err) {
0409         IWL_ERR(phy_db->trans,
0410             "Cannot send HCMD of Phy DB non specific channel section\n");
0411         return err;
0412     }
0413 
0414     /* Send all the TXP channel specific data */
0415     err = iwl_phy_db_send_all_channel_groups(phy_db,
0416                          IWL_PHY_DB_CALIB_CHG_PAPD,
0417                          phy_db->n_group_papd);
0418     if (err) {
0419         IWL_ERR(phy_db->trans,
0420             "Cannot send channel specific PAPD groups\n");
0421         return err;
0422     }
0423 
0424     /* Send all the TXP channel specific data */
0425     err = iwl_phy_db_send_all_channel_groups(phy_db,
0426                          IWL_PHY_DB_CALIB_CHG_TXP,
0427                          phy_db->n_group_txp);
0428     if (err) {
0429         IWL_ERR(phy_db->trans,
0430             "Cannot send channel specific TX power groups\n");
0431         return err;
0432     }
0433 
0434     IWL_DEBUG_INFO(phy_db->trans,
0435                "Finished sending phy db non channel data\n");
0436     return 0;
0437 }
0438 IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);