439 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2005-2014, 2020-2021 Intel Corporation4 * Copyright (C) 2016 Intel Deutschland GmbH5 */6#include <linux/slab.h>7#include <linux/string.h>8#include <linux/export.h>9 10#include "iwl-drv.h"11#include "iwl-phy-db.h"12#include "iwl-debug.h"13#include "iwl-op-mode.h"14#include "iwl-trans.h"15 16struct iwl_phy_db_entry {17 u16 size;18 u8 *data;19};20 21/**22 * struct iwl_phy_db - stores phy configuration and calibration data.23 *24 * @cfg: phy configuration.25 * @calib_nch: non channel specific calibration data.26 * @n_group_papd: number of entries in papd channel group.27 * @calib_ch_group_papd: calibration data related to papd channel group.28 * @n_group_txp: number of entries in tx power channel group.29 * @calib_ch_group_txp: calibration data related to tx power chanel group.30 * @trans: transport layer31 */32struct iwl_phy_db {33 struct iwl_phy_db_entry cfg;34 struct iwl_phy_db_entry calib_nch;35 int n_group_papd;36 struct iwl_phy_db_entry *calib_ch_group_papd;37 int n_group_txp;38 struct iwl_phy_db_entry *calib_ch_group_txp;39 40 struct iwl_trans *trans;41};42 43enum iwl_phy_db_section_type {44 IWL_PHY_DB_CFG = 1,45 IWL_PHY_DB_CALIB_NCH,46 IWL_PHY_DB_UNUSED,47 IWL_PHY_DB_CALIB_CHG_PAPD,48 IWL_PHY_DB_CALIB_CHG_TXP,49 IWL_PHY_DB_MAX50};51 52#define PHY_DB_CMD 0x6c53 54/* for parsing of tx power channel group data that comes from the firmware*/55struct iwl_phy_db_chg_txp {56 __le32 space;57 __le16 max_channel_idx;58} __packed;59 60struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)61{62 struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),63 GFP_KERNEL);64 65 if (!phy_db)66 return phy_db;67 68 phy_db->trans = trans;69 70 phy_db->n_group_txp = -1;71 phy_db->n_group_papd = -1;72 73 /* TODO: add default values of the phy db. */74 return phy_db;75}76IWL_EXPORT_SYMBOL(iwl_phy_db_init);77 78/*79 * get phy db section: returns a pointer to a phy db section specified by80 * type and channel group id.81 */82static struct iwl_phy_db_entry *83iwl_phy_db_get_section(struct iwl_phy_db *phy_db,84 enum iwl_phy_db_section_type type,85 u16 chg_id)86{87 if (!phy_db || type >= IWL_PHY_DB_MAX)88 return NULL;89 90 switch (type) {91 case IWL_PHY_DB_CFG:92 return &phy_db->cfg;93 case IWL_PHY_DB_CALIB_NCH:94 return &phy_db->calib_nch;95 case IWL_PHY_DB_CALIB_CHG_PAPD:96 if (chg_id >= phy_db->n_group_papd)97 return NULL;98 return &phy_db->calib_ch_group_papd[chg_id];99 case IWL_PHY_DB_CALIB_CHG_TXP:100 if (chg_id >= phy_db->n_group_txp)101 return NULL;102 return &phy_db->calib_ch_group_txp[chg_id];103 default:104 return NULL;105 }106 return NULL;107}108 109static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,110 enum iwl_phy_db_section_type type,111 u16 chg_id)112{113 struct iwl_phy_db_entry *entry =114 iwl_phy_db_get_section(phy_db, type, chg_id);115 if (!entry)116 return;117 118 kfree(entry->data);119 entry->data = NULL;120 entry->size = 0;121}122 123void iwl_phy_db_free(struct iwl_phy_db *phy_db)124{125 int i;126 127 if (!phy_db)128 return;129 130 iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);131 iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);132 133 for (i = 0; i < phy_db->n_group_papd; i++)134 iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);135 kfree(phy_db->calib_ch_group_papd);136 137 for (i = 0; i < phy_db->n_group_txp; i++)138 iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);139 kfree(phy_db->calib_ch_group_txp);140 141 kfree(phy_db);142}143IWL_EXPORT_SYMBOL(iwl_phy_db_free);144 145int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,146 struct iwl_rx_packet *pkt)147{148 unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);149 struct iwl_calib_res_notif_phy_db *phy_db_notif =150 (struct iwl_calib_res_notif_phy_db *)pkt->data;151 enum iwl_phy_db_section_type type;152 u16 size;153 struct iwl_phy_db_entry *entry;154 u16 chg_id = 0;155 156 if (pkt_len < sizeof(*phy_db_notif))157 return -EINVAL;158 159 type = le16_to_cpu(phy_db_notif->type);160 size = le16_to_cpu(phy_db_notif->length);161 162 if (pkt_len < sizeof(*phy_db_notif) + size)163 return -EINVAL;164 165 if (!phy_db)166 return -EINVAL;167 168 if (type == IWL_PHY_DB_CALIB_CHG_PAPD) {169 chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);170 if (phy_db && !phy_db->calib_ch_group_papd) {171 /*172 * Firmware sends the largest index first, so we can use173 * it to know how much we should allocate.174 */175 phy_db->calib_ch_group_papd = kcalloc(chg_id + 1,176 sizeof(struct iwl_phy_db_entry),177 GFP_ATOMIC);178 if (!phy_db->calib_ch_group_papd)179 return -ENOMEM;180 phy_db->n_group_papd = chg_id + 1;181 }182 } else if (type == IWL_PHY_DB_CALIB_CHG_TXP) {183 chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);184 if (phy_db && !phy_db->calib_ch_group_txp) {185 /*186 * Firmware sends the largest index first, so we can use187 * it to know how much we should allocate.188 */189 phy_db->calib_ch_group_txp = kcalloc(chg_id + 1,190 sizeof(struct iwl_phy_db_entry),191 GFP_ATOMIC);192 if (!phy_db->calib_ch_group_txp)193 return -ENOMEM;194 phy_db->n_group_txp = chg_id + 1;195 }196 }197 198 entry = iwl_phy_db_get_section(phy_db, type, chg_id);199 if (!entry)200 return -EINVAL;201 202 kfree(entry->data);203 entry->data = kmemdup(phy_db_notif->data, size, GFP_ATOMIC);204 if (!entry->data) {205 entry->size = 0;206 return -ENOMEM;207 }208 209 entry->size = size;210 211 IWL_DEBUG_INFO(phy_db->trans,212 "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",213 __func__, __LINE__, type, size);214 215 return 0;216}217IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);218 219static int is_valid_channel(u16 ch_id)220{221 if (ch_id <= 14 ||222 (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||223 (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||224 (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))225 return 1;226 return 0;227}228 229static u8 ch_id_to_ch_index(u16 ch_id)230{231 if (WARN_ON(!is_valid_channel(ch_id)))232 return 0xff;233 234 if (ch_id <= 14)235 return ch_id - 1;236 if (ch_id <= 64)237 return (ch_id + 20) / 4;238 if (ch_id <= 140)239 return (ch_id - 12) / 4;240 return (ch_id - 13) / 4;241}242 243 244static u16 channel_id_to_papd(u16 ch_id)245{246 if (WARN_ON(!is_valid_channel(ch_id)))247 return 0xff;248 249 if (1 <= ch_id && ch_id <= 14)250 return 0;251 if (36 <= ch_id && ch_id <= 64)252 return 1;253 if (100 <= ch_id && ch_id <= 140)254 return 2;255 return 3;256}257 258static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)259{260 struct iwl_phy_db_chg_txp *txp_chg;261 int i;262 u8 ch_index = ch_id_to_ch_index(ch_id);263 if (ch_index == 0xff)264 return 0xff;265 266 for (i = 0; i < phy_db->n_group_txp; i++) {267 txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;268 if (!txp_chg)269 return 0xff;270 /*271 * Looking for the first channel group that its max channel is272 * higher then wanted channel.273 */274 if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)275 return i;276 }277 return 0xff;278}279static280int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,281 u32 type, u8 **data, u16 *size, u16 ch_id)282{283 struct iwl_phy_db_entry *entry;284 u16 ch_group_id = 0;285 286 if (!phy_db)287 return -EINVAL;288 289 /* find wanted channel group */290 if (type == IWL_PHY_DB_CALIB_CHG_PAPD)291 ch_group_id = channel_id_to_papd(ch_id);292 else if (type == IWL_PHY_DB_CALIB_CHG_TXP)293 ch_group_id = channel_id_to_txp(phy_db, ch_id);294 295 entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);296 if (!entry)297 return -EINVAL;298 299 *data = entry->data;300 *size = entry->size;301 302 IWL_DEBUG_INFO(phy_db->trans,303 "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",304 __func__, __LINE__, type, *size);305 306 return 0;307}308 309static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,310 u16 length, void *data)311{312 struct iwl_phy_db_cmd phy_db_cmd;313 struct iwl_host_cmd cmd = {314 .id = PHY_DB_CMD,315 };316 317 IWL_DEBUG_INFO(phy_db->trans,318 "Sending PHY-DB hcmd of type %d, of length %d\n",319 type, length);320 321 /* Set phy db cmd variables */322 phy_db_cmd.type = cpu_to_le16(type);323 phy_db_cmd.length = cpu_to_le16(length);324 325 /* Set hcmd variables */326 cmd.data[0] = &phy_db_cmd;327 cmd.len[0] = sizeof(struct iwl_phy_db_cmd);328 cmd.data[1] = data;329 cmd.len[1] = length;330 cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;331 332 return iwl_trans_send_cmd(phy_db->trans, &cmd);333}334 335static int iwl_phy_db_send_all_channel_groups(336 struct iwl_phy_db *phy_db,337 enum iwl_phy_db_section_type type,338 u8 max_ch_groups)339{340 u16 i;341 int err;342 struct iwl_phy_db_entry *entry;343 344 /* Send all the channel specific groups to operational fw */345 for (i = 0; i < max_ch_groups; i++) {346 entry = iwl_phy_db_get_section(phy_db,347 type,348 i);349 if (!entry)350 return -EINVAL;351 352 if (!entry->size)353 continue;354 355 /* Send the requested PHY DB section */356 err = iwl_send_phy_db_cmd(phy_db,357 type,358 entry->size,359 entry->data);360 if (err) {361 IWL_ERR(phy_db->trans,362 "Can't SEND phy_db section %d (%d), err %d\n",363 type, i, err);364 return err;365 }366 367 IWL_DEBUG_INFO(phy_db->trans,368 "Sent PHY_DB HCMD, type = %d num = %d\n",369 type, i);370 }371 372 return 0;373}374 375int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)376{377 u8 *data = NULL;378 u16 size = 0;379 int err;380 381 IWL_DEBUG_INFO(phy_db->trans,382 "Sending phy db data and configuration to runtime image\n");383 384 /* Send PHY DB CFG section */385 err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,386 &data, &size, 0);387 if (err) {388 IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");389 return err;390 }391 392 err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);393 if (err) {394 IWL_ERR(phy_db->trans,395 "Cannot send HCMD of Phy DB cfg section\n");396 return err;397 }398 399 err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,400 &data, &size, 0);401 if (err) {402 IWL_ERR(phy_db->trans,403 "Cannot get Phy DB non specific channel section\n");404 return err;405 }406 407 err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);408 if (err) {409 IWL_ERR(phy_db->trans,410 "Cannot send HCMD of Phy DB non specific channel section\n");411 return err;412 }413 414 /* Send all the TXP channel specific data */415 err = iwl_phy_db_send_all_channel_groups(phy_db,416 IWL_PHY_DB_CALIB_CHG_PAPD,417 phy_db->n_group_papd);418 if (err) {419 IWL_ERR(phy_db->trans,420 "Cannot send channel specific PAPD groups\n");421 return err;422 }423 424 /* Send all the TXP channel specific data */425 err = iwl_phy_db_send_all_channel_groups(phy_db,426 IWL_PHY_DB_CALIB_CHG_TXP,427 phy_db->n_group_txp);428 if (err) {429 IWL_ERR(phy_db->trans,430 "Cannot send channel specific TX power groups\n");431 return err;432 }433 434 IWL_DEBUG_INFO(phy_db->trans,435 "Finished sending phy db non channel data\n");436 return 0;437}438IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);439