0001
0002
0003
0004
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
0023
0024
0025
0026
0027
0028
0029
0030
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
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
0074 return phy_db;
0075 }
0076 IWL_EXPORT_SYMBOL(iwl_phy_db_init);
0077
0078
0079
0080
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
0173
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
0187
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
0272
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
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
0322 phy_db_cmd.type = cpu_to_le16(type);
0323 phy_db_cmd.length = cpu_to_le16(length);
0324
0325
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
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
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
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
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
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);