]> Git Repo - J-linux.git/blob - drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / net / wireless / intel / iwlwifi / iwl-phy-db.c
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);
This page took 0.053391 seconds and 4 git commands to generate.