iwl-phy-db.c (10517B)
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2/* 3 * Copyright (C) 2005-2014, 2020-2021 Intel Corporation 4 * Copyright (C) 2016 Intel Deutschland GmbH 5 */ 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 layer 31 */ 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_MAX 50}; 51 52#define PHY_DB_CMD 0x6c 53 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 by 80 * 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 use 173 * 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 use 187 * 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 is 272 * 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} 279static 280int 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);