Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/iwl-phy-db.c
48253 views
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
16
struct 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
*/
32
struct 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
43
enum 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*/
55
struct iwl_phy_db_chg_txp {
56
__le32 space;
57
__le16 max_channel_idx;
58
} __packed;
59
60
struct 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
}
76
IWL_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
*/
82
static struct iwl_phy_db_entry *
83
iwl_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
109
static 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
123
void 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
}
143
IWL_EXPORT_SYMBOL(iwl_phy_db_free);
144
145
int 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
}
217
IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
218
219
static 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
229
static 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
244
static 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
258
static 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
}
279
static
280
int 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
309
static 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
335
static 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
375
int 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
}
438
IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);
439
440