Search Linux Wireless

[RFC v1 028/256] cl8k: add calib.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Viktor Barna <viktor.barna@xxxxxxxxxx>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx>
---
 drivers/net/wireless/celeno/cl8k/calib.c | 1682 ++++++++++++++++++++++
 1 file changed, 1682 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/calib.c

diff --git a/drivers/net/wireless/celeno/cl8k/calib.c b/drivers/net/wireless/celeno/cl8k/calib.c
new file mode 100644
index 000000000000..8861964e3aff
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/calib.c
@@ -0,0 +1,1682 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+
+#include "calib.h"
+#include "temperature.h"
+#include "utils/utils.h"
+#include "chip.h"
+#include "chandef.h"
+#include "fw/msg_cfm.h"
+#include "fw/msg_tx.h"
+#include "band.h"
+#include "e2p.h"
+#include "channel.h"
+#include "power.h"
+#include "afe.h"
+#include "radio.h"
+
+/*
+ * CL80x0: TCV0 - 5g, TCV1 - 24g
+ * ==============================================
+ * 50  48  46  44  42  40  38  36  --> Start 5g
+ * 100 64  62  60  58  56  54  52
+ * 116 114 112 110 108 106 104 102
+ * 134 132 128 126 124 122 120 118
+ * 153 151 149 144 142 140 138 136
+ * 3   2   1   165 161 159 157 155  --> Start 24g
+ * 11  10  9   8   7   6   5   4
+ *                     14  13  12
+ */
+
+/*
+ * CL80x6: TCV0 - 6g, TCV1 - 5g
+ * ==============================================
+ * 25  21  17  13  9   5   2   1   --> Start 6g
+ * 57  53  49  45  41  37  33  29
+ * 89  85  81  77  73  69  65  61
+ * 121 117 113 109 105 101 97  93
+ * 153 147 143 139 135 131 127 123
+ * 185 181 177 173 169 165 161 157
+ * 217 213 209 205 201 197 193 189
+ * 42  40  38  36  233 229 225 221 --> Start 5g
+ * 58  56  54  52  50  48  46  44
+ * 108 106 104 102 100 64  62  60
+ * 124 122 120 118 116 114 112 110
+ * 142 140 138 136 134 132 128 126
+ * 161 159 157 155 153 151 149 144
+ *                             165
+ */
+
+#define BITMAP_80X0_START_TCV0  0
+#define BITMAP_80X0_MAX_TCV0    NUM_CHANNELS_5G
+
+#define BITMAP_80X0_START_TCV1 NUM_CHANNELS_5G
+#define BITMAP_80X0_MAX_TCV1   (NUM_CHANNELS_5G + NUM_CHANNELS_24G)
+
+#define BITMAP_80X6_START_TCV0  0
+#define BITMAP_80X6_MAX_TCV0    NUM_CHANNELS_6G
+
+#define BITMAP_80X6_START_TCV1  NUM_CHANNELS_6G
+#define BITMAP_80X6_MAX_TCV1    (NUM_CHANNELS_6G + NUM_CHANNELS_5G)
+
+#define INVALID_ADDR 0xffff
+
+#define S12_S_BIT (0x00000800)
+#define U12_BIT_MASK (0x00000FFF)
+#define CAST_S12_TO_S32(i) ((~(i) & S12_S_BIT) ? (i) : ((i) | ~U12_BIT_MASK))
+
+static const u8 calib_channels_24g[CALIB_CHAN_24G_MAX] = {
+       1, 6, 11
+};
+
+static const u8 calib_channels_5g[CALIB_CHAN_5G_MAX] = {
+       36, 52, 100, 116, 132, 149
+};
+
+static const u8 calib_channels_6g[CALIB_CHAN_6G_MAX] = {
+       1, 17, 33, 49, 65, 81, 97, 113, 129, 145, 161, 177, 193, 209, 225
+};
+
+static u8 tone_vector_arr[CHNL_BW_MAX][IQ_NUM_TONES_REQ] = {
+       {6, 10, 14, 18, 22, 24, 26, 27},
+       {10, 18, 26, 34, 41, 48, 53, 58},
+       {18, 34, 50, 66, 82, 98, 110, 122},
+       {18, 34, 66, 98, 130, 164, 224, 250}
+};
+
+static u8 get_bitmap_start_tcv1(struct cl_chip *chip)
+{
+       if (cl_chip_is_6g(chip))
+               return BITMAP_80X6_START_TCV1;
+       else
+               return BITMAP_80X0_START_TCV1;
+}
+
+static void get_bitmap_boundaries(struct cl_chip *chip, u8 tcv_idx, u8 *start, u8 *max)
+{
+       if (cl_chip_is_6g(chip)) {
+               if (tcv_idx == TCV0) {
+                       *start = BITMAP_80X6_START_TCV0;
+                       *max = BITMAP_80X6_MAX_TCV0;
+               } else {
+                       *start = BITMAP_80X6_START_TCV1;
+                       *max = BITMAP_80X6_MAX_TCV1;
+               }
+       } else {
+               if (tcv_idx == TCV0) {
+                       *start = BITMAP_80X0_START_TCV0;
+                       *max = BITMAP_80X0_MAX_TCV0;
+               } else {
+                       *start = BITMAP_80X0_START_TCV1;
+                       *max = BITMAP_80X0_MAX_TCV1;
+               }
+       }
+}
+
+static u8 idx_to_arr_offset(u8 idx)
+{
+       /* Divide by 8 for array index */
+       return idx >> 3;
+}
+
+static u8 idx_to_bit_offset(u8 idx)
+{
+       /* Reminder is for bit index (assummed array of u8) */
+       return idx & 0x07;
+}
+
+static const u8 bits_cnt_table256[] = {
+       0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+       1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+       1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+       2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+       1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+       2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+       2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+       3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+       1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+       2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+       2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+       3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+       2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+       3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+       3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+       4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+};
+
+static u8 count_bits(const u8 *bitmap)
+{
+       /*
+        * Count bits in a given u8 array ASSUMED ARRAY SIZE IS BIT_MAP_SIZE
+        * bitmap - pointer to u8 array (bitmap)
+        */
+       u8 i = 0, cnt = 0;
+
+       for (i = 0; i < BIT_MAP_SIZE; i++)
+               cnt += bits_cnt_table256[bitmap[i]];
+
+       return cnt;
+}
+
+static bool is_vector_unset(const u8 *bitmap)
+{
+       /* Check bitmap is unset i.e. all values are CURR_BMP_UNSET */
+       u8 empty_bitmap[BIT_MAP_SIZE] = {0};
+
+       return !memcmp(bitmap, empty_bitmap, BIT_MAP_SIZE);
+}
+
+static bool bitmap_test_bit_idx(const u8 *bitmap, u8 idx)
+{
+       /* Check bit at a given index is set i.e. 1 */
+       u8 arr_idx = idx_to_arr_offset(idx), bit_idx = idx_to_bit_offset(idx);
+
+       if (arr_idx >= BIT_MAP_SIZE)
+               return false;
+
+       /* Convert non-zero to true and zero to false */
+       return !!(bitmap[arr_idx] & BIT(bit_idx));
+}
+
+static void bitmap_shift(u8 *bitmap, u8 shft)
+{
+       /* Shifts an array of byte of size len by shft number of bits to the left */
+       u8 bitmap_tmp[BIT_MAP_SIZE] = {0};
+       u8 msb_shifts = shft % 8;
+       u8 lsb_shifts = 8 - msb_shifts;
+       u8 byte_shift = shft / 8;
+       u8 last_byte = BIT_MAP_SIZE - byte_shift - 1;
+       u8 msb_idx;
+       u8 i;
+
+       memcpy(bitmap_tmp, bitmap, BIT_MAP_SIZE);
+       memset(bitmap, 0, BIT_MAP_SIZE);
+
+       for (i = 0;  i < BIT_MAP_SIZE; i++) {
+               if (i <= last_byte) {
+                       msb_idx = i + byte_shift;
+                       bitmap[i] = bitmap_tmp[msb_idx] >> msb_shifts;
+                       if (i != last_byte)
+                               bitmap[i] |= bitmap_tmp[msb_idx + 1] << lsb_shifts;
+               }
+       }
+}
+
+static bool bitmap_set_bit_idx(struct cl_hw *cl_hw, u8 *bitmap, u8 idx)
+{
+       /* Set bit at a given index */
+       u8 arr_idx = idx_to_arr_offset(idx), bit_idx = idx_to_bit_offset(idx);
+
+       if (arr_idx >= BIT_MAP_SIZE) {
+               cl_dbg_err(cl_hw, "invalid arr_idx (%u)\n", arr_idx);
+               return false;
+       }
+
+       bitmap[arr_idx] |= BIT(bit_idx);
+       return true;
+}
+
+static bool bitmap_clear_bit_idx(struct cl_hw *cl_hw, u8 *bitmap, u8 idx)
+{
+       /* Clear bit at a given index */
+       u8 arr_idx = idx_to_arr_offset(idx), bit_idx = idx_to_bit_offset(idx);
+
+       if (arr_idx >= BIT_MAP_SIZE) {
+               cl_dbg_err(cl_hw, "invalid arr_idx (%u)\n", arr_idx);
+               return false;
+       }
+
+       bitmap[arr_idx] &= ~BIT(bit_idx);
+       return true;
+}
+
+static u16 bitmap_look_lsb_up(struct cl_hw *cl_hw, u8 *bitmap, u16 idx)
+{
+       /* Find closest ON(1) bit with index haigher than idx inside bitmap */
+       u16 curr_idx = idx;
+       u8 curr = 0;
+
+       while (++curr_idx < cl_channel_num(cl_hw)) {
+               curr = bitmap[idx_to_arr_offset(curr_idx)];
+               if (curr & (1ULL << idx_to_bit_offset(curr_idx)))
+                       return curr_idx;
+       }
+
+       /* No matching bit found - return original index */
+       return idx;
+}
+
+static u16 bitmap_look_msb_down(struct cl_hw *cl_hw, u8 *bitmap, u16 idx)
+{
+       /* Find closest ON(1) bit with index lower than idx inside bitmap */
+       u16 curr_idx = idx;
+       u8 curr = 0;
+
+       if (idx >= cl_channel_num(cl_hw)) {
+               cl_dbg_err(cl_hw, "Invalid channel index [%u]\n", idx);
+               return idx;
+       }
+
+       while (curr_idx-- != 0) {
+               curr = bitmap[idx_to_arr_offset(curr_idx)];
+               if (curr & (1ULL << idx_to_bit_offset(curr_idx)))
+                       return curr_idx;
+       }
+
+       /* No matching bit found - return original index */
+       return idx;
+}
+
+static u8 address_offset_tcv1(struct cl_hw *cl_hw)
+{
+       /* Calculate eeprom calibration data offset for tcv1 */
+       struct cl_chip *chip = cl_hw->chip;
+       u8 i, cnt = 0;
+       u8 bitmap[BIT_MAP_SIZE] = {0};
+
+       if (cl_e2p_read(chip, bitmap, BIT_MAP_SIZE, ADDR_CALIB_CHAN_BMP))
+               return 0;
+
+       for (i = 0; i < get_bitmap_start_tcv1(chip); i++)
+               cnt += bitmap_test_bit_idx(bitmap, i);
+
+       return cnt;
+}
+
+static int point_idx_to_address(struct cl_hw *cl_hw, u8 *bitmap, struct point *pt)
+{
+       /* Calculate eeprom address for a given idx and phy (initiated point) */
+       u8 i, cnt = 0;
+
+       pt->addr = INVALID_ADDR;
+
+       if (!bitmap_test_bit_idx(bitmap, pt->idx))
+               return 0;
+
+       if (pt->phy >= MAX_ANTENNAS) {
+               cl_dbg_err(cl_hw, "Invalid phy number %u", pt->phy);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < pt->idx; i++)
+               cnt += bitmap_test_bit_idx(bitmap, i);
+
+       if (cl_hw_is_tcv1(cl_hw))
+               cnt += address_offset_tcv1(cl_hw);
+
+       pt->addr = ADDR_CALIB_PHY +
+               sizeof(struct eeprom_phy_calib) * (cnt * MAX_ANTENNAS + pt->phy);
+
+       return 0;
+}
+
+static bool linear_equation_signed(struct cl_hw *cl_hw, const u16 x, s8 *y,
+                                  const u16 x0, const s8 y0, const u16 x1, const s8 y1)
+{
+       /* Calculate y given to points (x0,y0) and (x1,y1) and x */
+       s32 numerator = (x - x0) * (y1 - y0);
+       s32 denominator = x1 - x0;
+
+       if (unlikely(!denominator)) {
+               cl_dbg_err(cl_hw, "zero denominator\n");
+               return false;
+       }
+
+       *y = (s8)(y0 + DIV_ROUND_CLOSEST(numerator, denominator));
+
+       return true;
+}
+
+static bool calculate_calib(struct cl_hw *cl_hw, u8 *bitmap,
+                           struct point *p0, struct point *p1, struct point *p2)
+{
+       /* Main interpolation/extrapolation function */
+       bool calc_succsess = false;
+       u16 freq0, freq1, freq2;
+
+       if (unlikely(is_vector_unset(bitmap)))
+               return false;
+
+       p1->idx = bitmap_look_lsb_up(cl_hw, bitmap, p0->idx);
+       p2->idx = bitmap_look_msb_down(cl_hw, bitmap, p0->idx);
+
+       /* Invalid case */
+       if (p1->idx == p0->idx && p2->idx == p0->idx) {
+               cl_dbg_err(cl_hw, "Invalid index %u or bad bit map\n", p0->idx);
+               return false;
+       }
+
+       /* Extrapolation case */
+       if (p1->idx == p0->idx)
+               p1->idx = bitmap_look_msb_down(cl_hw, bitmap, p2->idx);
+       if (p2->idx == p0->idx)
+               p2->idx = bitmap_look_lsb_up(cl_hw, bitmap, p1->idx);
+
+       /* Address from index */
+       if (point_idx_to_address(cl_hw, bitmap, p1) || p1->addr == INVALID_ADDR) {
+               cl_dbg_err(cl_hw, "Point calculation failed\n");
+               return false;
+       }
+
+       if (point_idx_to_address(cl_hw, bitmap, p2) || p2->addr == INVALID_ADDR) {
+               cl_dbg_err(cl_hw, "Point calculation failed\n");
+               return false;
+       }
+
+       /* Read from eeprom */
+       if (cl_e2p_read(cl_hw->chip, (u8 *)&p1->calib, sizeof(struct eeprom_phy_calib), p1->addr))
+               return false;
+
+       /* No interpolation required */
+       if (p1->addr == p2->addr) {
+               p0->calib = p1->calib;
+               return true;
+       }
+
+       /* Interpolation or extrapolation is required - read from eeprom */
+       if (cl_e2p_read(cl_hw->chip, (u8 *)&p2->calib, sizeof(struct eeprom_phy_calib), p2->addr))
+               return false;
+
+       freq0 = cl_channel_idx_to_freq(cl_hw, p0->idx);
+       freq1 = cl_channel_idx_to_freq(cl_hw, p1->idx);
+       freq2 = cl_channel_idx_to_freq(cl_hw, p2->idx);
+
+       /* Interpolate/extrapolate target power */
+       calc_succsess = linear_equation_signed(cl_hw,
+                                              freq0, &p0->calib.pow,
+                                              freq1, p1->calib.pow,
+                                              freq2, p2->calib.pow);
+
+       /* Interpolate/extrapolate power offset */
+       calc_succsess = calc_succsess && linear_equation_signed(cl_hw,
+                                                               freq0, &p0->calib.offset,
+                                                               freq1, p1->calib.offset,
+                                                               freq2, p2->calib.offset);
+
+       /* Interpolate/extrapolate calibration temperature */
+       calc_succsess = calc_succsess && linear_equation_signed(cl_hw,
+                                                               freq0, &p0->calib.tmp,
+                                                               freq1, p1->calib.tmp,
+                                                               freq2, p2->calib.tmp);
+
+       if (unlikely(!calc_succsess)) {
+               cl_dbg_err(cl_hw,
+                          "Calc failed: freq0 %u idx0 %u, freq1 %u idx1 %u, freq2 %u idx2 %u\n",
+                          freq0, p0->idx, freq1, p1->idx, freq2, p2->idx);
+               return false;
+       }
+
+       return true;
+}
+
+static int read_validate_vector_bitmap(struct cl_hw *cl_hw, u8 *bitmap)
+{
+       struct cl_chip *chip = cl_hw->chip;
+
+       if (cl_e2p_read(chip, bitmap, BIT_MAP_SIZE, ADDR_CALIB_CHAN_BMP))
+               return -1;
+
+       /* Test if e2p was read succsefull since it is not ALL EMPTY */
+       if (is_vector_unset(bitmap)) {
+               cl_dbg_err(cl_hw, "Vector not ready\n");
+               return -EPERM;
+       }
+
+       if (cl_hw_is_tcv1(cl_hw)) {
+               u8 bitmap_start = get_bitmap_start_tcv1(chip);
+
+               bitmap_shift(bitmap, bitmap_start);
+       }
+
+       return 0;
+}
+
+static int e2p_prepare(struct cl_hw *cl_hw, struct point *data, u8 *bitmap)
+{
+       int ret = read_validate_vector_bitmap(cl_hw, bitmap);
+
+       if (ret) {
+               cl_dbg_err(cl_hw, "read_validate_vector_bitmap failed\n");
+               return ret;
+       }
+
+       data->idx = cl_channel_to_index(cl_hw, data->chan);
+
+       return point_idx_to_address(cl_hw, bitmap, data);
+}
+
+static int read_or_interpolate_point(struct cl_hw *cl_hw, u8 *bitmap, struct point *p0)
+{
+       struct point p1 = {.phy = p0->phy};
+       struct point p2 = {.phy = p0->phy};
+       struct point tmp_pt = *p0;
+
+       /* Invalid address = no physical address was allocated to this channel */
+       if (tmp_pt.addr != INVALID_ADDR) {
+               if (cl_e2p_read(cl_hw->chip, (u8 *)&tmp_pt.calib,
+                               sizeof(struct eeprom_phy_calib), tmp_pt.addr))
+                       return -1;
+       } else {
+               /* Interpolate */
+               if (!calculate_calib(cl_hw, bitmap, &tmp_pt, &p1, &p2)) {
+                       cl_dbg_err(cl_hw, "Interpolation Error\n");
+                       return -EFAULT;
+               }
+       }
+
+       if (tmp_pt.calib.pow == 0 && tmp_pt.calib.offset == 0 && tmp_pt.calib.tmp == 0) {
+               u16 freq = cl_channel_idx_to_freq(cl_hw, tmp_pt.idx);
+
+               cl_dbg_err(cl_hw, "Verify calibration point: addr %x, idx %u, freq %u, phy %u\n",
+                          tmp_pt.addr, tmp_pt.idx, freq, tmp_pt.phy);
+               /* *Uninitiated eeprom value */
+               return -EINVAL;
+       }
+
+       /* Now p0 will contain "Valid" calculations of calib" */
+       p0->calib = tmp_pt.calib;
+       return 0;
+}
+
+int cl_calib_get(struct wiphy *wiphy, struct wireless_dev *wdev,
+                            const void *data, int data_len)
+{
+       /* Kernel space callback for handling E2P_GET_CALIB vendor subcmd */
+       int ret;
+       struct point *p0;
+       u8 e2p_bitmap[BIT_MAP_SIZE] = {0};
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+
+       if (!data) {
+               cl_dbg_err(cl_hw, "data is null\n");
+               return -1;
+       }
+
+       p0 = (struct point *)data;
+
+       ret = e2p_prepare(cl_hw, p0, e2p_bitmap);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Unable prepare e2p\n");
+               return ret;
+       }
+
+       ret = read_or_interpolate_point(cl_hw, e2p_bitmap, p0);
+       if (ret) {
+               cl_dbg_trace(cl_hw, "read_or_interpolate_point error\n");
+               return ret;
+       }
+
+       return cl_vendor_reply(cl_hw, &p0->calib, sizeof(p0->calib));
+}
+
+int cl_calib_set(struct wiphy *wiphy, struct wireless_dev *wdev,
+                            const void *data, int data_len)
+{
+       /* Kernel space callback for handling E2P_SET_CALIB vendor subcmd */
+       struct cl_hw *cl_hw = WIPHY_TO_CL_HW(wiphy);
+       struct point pt;
+       int ret;
+       u8 e2p_bitmap[BIT_MAP_SIZE] = {0};
+       u8 ch_idx = 0;
+
+       if (!data) {
+               cl_dbg_err(cl_hw, "data is null\n");
+               return -1;
+       }
+
+       pt = *(struct point *)data;
+
+       ret = e2p_prepare(cl_hw, &pt, e2p_bitmap);
+       if (ret) {
+               cl_dbg_err(cl_hw, "Unable prepare e2p\n");
+               return ret;
+       }
+
+       if (pt.addr == INVALID_ADDR) {
+               cl_dbg_err(cl_hw, "Invalid address - permission denied\n");
+               return -EPERM;
+       }
+
+       if (pt.calib.pow < POWER_MIN_DB || pt.calib.pow > POWER_MAX_DB) {
+               cl_dbg_err(cl_hw, "Invalid power (%d). Valid range (%d - %d)\n",
+                          pt.calib.pow, POWER_MIN_DB, POWER_MAX_DB);
+               return -1;
+       }
+
+       if (pt.calib.offset < POWER_OFFSET_MIN_Q2 || pt.calib.offset > POWER_OFFSET_MAX_Q2) {
+               cl_dbg_err(cl_hw, "Invalid power offset (%d). Valid range (%d - %d)\n",
+                          pt.calib.offset, POWER_OFFSET_MIN_Q2, POWER_OFFSET_MAX_Q2);
+               return -1;
+       }
+
+       if (!bitmap_test_bit_idx(e2p_bitmap, pt.idx)) {
+               cl_dbg_err(cl_hw, "No permition to write to this channel %u\n", pt.idx);
+               return -EACCES;
+       }
+
+       /*
+        * Temperature is an optional argument for "e2p set calib" command.
+        * If value is 0x7f then temperature argument was not set, and it
+        * should be set by the driver.
+        */
+       if (pt.calib.tmp == S8_MAX)
+               pt.calib.tmp = cl_temperature_read(cl_hw, TEMP_MODE_INTERNAL);
+
+       if (cl_e2p_write(cl_hw->chip, (u8 *)&pt.calib, sizeof(struct eeprom_phy_calib), pt.addr))
+               return -1;
+
+       ch_idx = cl_channel_to_index(cl_hw, pt.chan);
+
+       if (ch_idx < MAX_CHANNELS && pt.phy < MAX_ANTENNAS) {
+               cl_hw->tx_pow_info[ch_idx][pt.phy].power = pt.calib.pow;
+               cl_hw->tx_pow_info[ch_idx][pt.phy].offset = pt.calib.offset;
+               cl_hw->tx_pow_info[ch_idx][pt.phy].temperature = pt.calib.tmp;
+               cl_hw->set_calib = true;
+       }
+
+       return 0;
+}
+
+static void cl_calib_power_reset(struct cl_hw *cl_hw)
+{
+       u8 ch_idx;
+       u16 phy;
+       static const struct cl_tx_power_info default_info = {
+               .power       = UNCALIBRATED_POWER,
+               .offset      = UNCALIBRATED_POWER_OFFSET,
+               .temperature = UNCALIBRATED_TEMPERATURE
+       };
+
+       /* Initiate tx_pow_info struct to default values */
+       for (ch_idx = 0; ch_idx < cl_channel_num(cl_hw); ch_idx++)
+               for (phy = 0; phy < MAX_ANTENNAS; phy++)
+                       cl_hw->tx_pow_info[ch_idx][phy] = default_info;
+}
+
+#define PHY0_OFFSET_FIX_Q2 -8 /* -2db */
+#define PHY3_OFFSET_FIX_Q2 14 /* +3.5db */
+
+void cl_calib_power_read(struct cl_hw *cl_hw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       int ret;
+       u8 bitmap[BIT_MAP_SIZE] = {0};
+       struct point curr_point = {0};
+       u8 *phy = &curr_point.phy;
+       u8 *ch_idx = &curr_point.idx;
+
+       /* Initiate tx_pow_info struct to default values */
+       cl_calib_power_reset(cl_hw);
+
+       /* Vector not initiated set table to default values */
+       if (unlikely(read_validate_vector_bitmap(cl_hw, bitmap))) {
+               cl_dbg_trace(cl_hw, "initiate to default values\n");
+               return;
+       }
+
+       /* Perform only on calibrated boards - read_validate_vector_bitmap succeeded (0) */
+       for (*ch_idx = 0; *ch_idx < cl_channel_num(cl_hw); (*ch_idx)++)
+               for (*phy = 0; *phy < cl_hw->num_antennas; (*phy)++) {
+                       ret = point_idx_to_address(cl_hw, bitmap, &curr_point);
+
+                       if (ret) {
+                               /* *don't overwrite default values */
+                               cl_dbg_err(cl_hw, "point idx to address failed\n");
+                               continue;
+                       }
+
+                       ret = read_or_interpolate_point(cl_hw, bitmap, &curr_point);
+                       /* Unable to calculate new value ==> DON'T overwrite default values */
+                       if (unlikely(ret))
+                               continue;
+
+                       /*
+                        * Work around:
+                        * Add 3.5dB offset to PHY3 if EEPROM version is 0.
+                        * Decrease 2dB offset to all PHYs if EEPROM version is 1.
+                        */
+                       if (!cl_chip_is_6g(chip)) {
+                               u8 eeprom_version = chip->eeprom_cache->general.version;
+
+                               if (cl_band_is_5g(cl_hw) && eeprom_version == 0 && *phy == 3)
+                                       curr_point.calib.offset += PHY3_OFFSET_FIX_Q2;
+                               else if (cl_band_is_24g(cl_hw) && eeprom_version == 1)
+                                       curr_point.calib.offset += PHY0_OFFSET_FIX_Q2;
+                       }
+
+                       cl_hw->tx_pow_info[*ch_idx][*phy].power = curr_point.calib.pow;
+                       cl_hw->tx_pow_info[*ch_idx][*phy].offset = curr_point.calib.offset;
+                       cl_hw->tx_pow_info[*ch_idx][*phy].temperature = curr_point.calib.tmp;
+               }
+
+       cl_dbg_trace(cl_hw, "Created tx_pow_info\n");
+}
+
+void cl_calib_power_offset_fill(struct cl_hw *cl_hw, u8 channel,
+                                     u8 bw, u8 offset[MAX_ANTENNAS])
+{
+       u8 i;
+       u8 chan_idx = cl_channel_to_index(cl_hw, channel);
+       s8 signed_offset;
+       struct cl_ate_db *ate_db = &cl_hw->ate_db;
+
+       if (chan_idx == INVALID_CHAN_IDX)
+               return;
+
+       /* In ATE mode, use values of 'ATE power_offset' if it was set */
+       if (ate_db->active && ate_db->tx_power_offset[0] != S8_MAX) {
+               for (i = 0; i < MAX_ANTENNAS; i++) {
+                       s8 pow_offset = ate_db->tx_power_offset[i];
+
+                       signed_offset = cl_power_offset_check_margin(cl_hw, bw, i, pow_offset);
+                       offset[i] = cl_convert_signed_to_reg_value(signed_offset);
+               }
+
+               return;
+       }
+
+       for (i = 0; i < MAX_ANTENNAS; i++) {
+               s8 pow_offset = cl_hw->tx_pow_info[chan_idx][i].offset;
+
+               signed_offset = cl_power_offset_check_margin(cl_hw, bw, i, pow_offset);
+               offset[i] = cl_convert_signed_to_reg_value(signed_offset);
+       }
+}
+
+static void pivot_channels_reset(struct cl_hw *cl_hw, u8 *bitmap)
+{
+       u8 i, start = 0, max = 0;
+
+       get_bitmap_boundaries(cl_hw->chip, cl_hw->tcv_idx, &start, &max);
+
+       for (i = start; i < max; i++)
+               bitmap_clear_bit_idx(cl_hw, bitmap, i);
+}
+
+static u8 count_num_pivots(struct cl_chip *chip, const u8 *bitmap, u8 tcv_idx)
+{
+       u8 i = 0, cnt = 0, start = 0, max = 0;
+
+       get_bitmap_boundaries(chip, tcv_idx, &start, &max);
+
+       for (i = start; i < max; i++)
+               if (bitmap_test_bit_idx(bitmap, i))
+                       cnt++;
+
+       return cnt;
+}
+
+int cl_calib_pivot_channels_set(struct cl_hw *cl_hw, const void *chan_list, u32 size)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       u8 bitmap[BIT_MAP_SIZE] = {0};
+       u8 num_pivots = 0;
+       u8 idx = 0;
+
+       if (cl_e2p_read(chip, bitmap, BIT_MAP_SIZE, ADDR_CALIB_CHAN_BMP))
+               return -1;
+
+       num_pivots = count_num_pivots(chip, bitmap, cl_hw->tcv_idx);
+
+       if (num_pivots > 0) {
+               cl_dbg_err(cl_hw, "Vector already set\n");
+               return -EACCES;
+       }
+
+       while (size--) {
+               idx = cl_channel_to_index(cl_hw, ((u32 *)chan_list)[size]);
+
+               if (idx == INVALID_CHAN_IDX) {
+                       cl_dbg_err(cl_hw, "Bad channel index %u", idx);
+                       return -EINVAL;
+               }
+
+               if (cl_hw_is_tcv1(cl_hw))
+                       idx += get_bitmap_start_tcv1(chip);
+
+               if (!bitmap_set_bit_idx(cl_hw, bitmap, idx)) {
+                       cl_dbg_err(cl_hw, "Bad channel index %u", idx);
+                       return -EINVAL;
+               }
+       }
+
+       if (count_bits(bitmap) > NUM_OF_PIVOTS) {
+               cl_dbg_err(cl_hw, "Too much pivot channels chosen\n");
+               return -EINVAL;
+       }
+
+       if (cl_e2p_write(chip, bitmap, BIT_MAP_SIZE, ADDR_CALIB_CHAN_BMP))
+               return -1;
+
+       /*
+        * Pivots of tcv0 are located before the pivots of tcv1.
+        * If calibration of tcv1 was done before calibration of tcv0, we must move the
+        * calibration data of tcv1 so that there is room for the tcv0 calibration data.
+        */
+       if (cl_hw_is_tcv0(cl_hw)) {
+               u8 num_pivots_tcv0 = count_num_pivots(chip, bitmap, TCV0);
+               u8 num_pivots_tcv1 = count_num_pivots(chip, bitmap, TCV1);
+
+               if (num_pivots_tcv1 > 0) {
+                       struct eeprom_phy_calib phy_calib[NUM_PIVOT_PHYS] = { {0} };
+
+                       if (cl_e2p_read(chip, (u8 *)phy_calib, SIZE_CALIB_PHY, ADDR_CALIB_PHY))
+                               return -1;
+
+                       memmove(&phy_calib[num_pivots_tcv0 * MAX_ANTENNAS],
+                               &phy_calib[0],
+                               num_pivots_tcv1 * MAX_ANTENNAS * sizeof(struct eeprom_phy_calib));
+                       memset(&phy_calib[0],
+                              0,
+                              num_pivots_tcv0 * MAX_ANTENNAS * sizeof(struct eeprom_phy_calib));
+
+                       if (cl_e2p_write(chip, (u8 *)phy_calib, SIZE_CALIB_PHY, ADDR_CALIB_PHY))
+                               return -1;
+               }
+       }
+
+       return 0;
+}
+
+int cl_calib_pivot_channels_reset(struct cl_hw *cl_hw)
+{
+       /* Both eeprom and efuse are being set to 0 for reset */
+       struct cl_chip *chip = cl_hw->chip;
+       u8 bitmap[BIT_MAP_SIZE] = {0};
+       struct eeprom_phy_calib phy_calib[NUM_PIVOT_PHYS] = { {0} };
+       u8 num_pivots_tcv0 = 0;
+       u8 num_pivots_tcv1 = 0;
+
+       if (sizeof(phy_calib) != SIZE_CALIB_PHY) {
+               cl_dbg_err(cl_hw, "sizeof(phy_calib) != SIZE_CALIB_PHY\n");
+               return -1;
+       }
+
+       /* Read current bitmap and calibration data */
+       if (cl_e2p_read(chip, (u8 *)bitmap, BIT_MAP_SIZE, ADDR_CALIB_CHAN_BMP))
+               return -1;
+       if (cl_e2p_read(chip, (u8 *)phy_calib, SIZE_CALIB_PHY, ADDR_CALIB_PHY))
+               return -1;
+
+       /* Find number of pivots for each band */
+       num_pivots_tcv0 = count_num_pivots(chip, bitmap, TCV0);
+       num_pivots_tcv1 = count_num_pivots(chip, bitmap, TCV1);
+
+       /* Reset bitmap of this band */
+       pivot_channels_reset(cl_hw, bitmap);
+
+       /* Reset calibration data of this band */
+       if (cl_hw_is_tcv0(cl_hw)) {
+               if (num_pivots_tcv1 > 0) {
+                       /* For tcv0 shift calibration data of tcv1 to the beginning */
+                       memcpy(&phy_calib[0], &phy_calib[num_pivots_tcv0 * MAX_ANTENNAS],
+                              num_pivots_tcv1 * MAX_ANTENNAS * sizeof(struct eeprom_phy_calib));
+                       memset(&phy_calib[num_pivots_tcv1 * MAX_ANTENNAS], 0,
+                              num_pivots_tcv0 * MAX_ANTENNAS * sizeof(struct eeprom_phy_calib));
+               } else {
+                       memset(&phy_calib[0], 0,
+                              num_pivots_tcv0 * MAX_ANTENNAS * sizeof(struct eeprom_phy_calib));
+               }
+       } else {
+               memset(&phy_calib[num_pivots_tcv0 * MAX_ANTENNAS],
+                      0, num_pivots_tcv1 * MAX_ANTENNAS * sizeof(struct eeprom_phy_calib));
+       }
+
+       /* Write back modified bitmap and calibration data */
+       if (cl_e2p_write(chip, (u8 *)bitmap, BIT_MAP_SIZE, ADDR_CALIB_CHAN_BMP))
+               return -1;
+       if (cl_e2p_write(chip, (u8 *)phy_calib, SIZE_CALIB_PHY, ADDR_CALIB_PHY))
+               return -1;
+
+       /* Reset host calibration data */
+       cl_calib_power_reset(cl_hw);
+
+       return 0;
+}
+
+static void cl_calib_init_cfm(struct cl_iq_dcoc_data *iq_dcoc_data)
+{
+       int i;
+
+       for (i = 0; i < CALIB_CFM_MAX; i++)
+               iq_dcoc_data->dcoc_iq_cfm[i].status = CALIB_FAIL;
+}
+
+static void cl_calib_save_channel(struct cl_hw *cl_hw, struct cl_calib_restore *calib_restore)
+{
+       calib_restore->bw = cl_hw->bw;
+       calib_restore->primary = cl_hw->primary_freq;
+       calib_restore->center = cl_hw->center_freq;
+       calib_restore->channel = ieee80211_frequency_to_channel(cl_hw->primary_freq);
+}
+
+static int cl_calib_set_idle(struct cl_hw *cl_hw, bool idle)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_hw *cl_hw_tcv0 = chip->cl_hw_tcv0;
+       struct cl_hw *cl_hw_tcv1 = chip->cl_hw_tcv1;
+       u8 is_prod = chip->conf->ce_production_mode;
+       bool tcv0_en = (cl_radio_is_on(cl_hw_tcv0) || (is_prod && cl_hw_tcv0->ate_db.active));
+       bool tcv1_en = (cl_radio_is_on(cl_hw_tcv1) || (is_prod && cl_hw_tcv1->ate_db.active));
+
+       if (!idle) {
+               if (tcv1_en)
+                       cl_msg_tx_set_idle(cl_hw_tcv1, MAC_ACTIVE);
+
+               if (tcv0_en)
+                       cl_msg_tx_set_idle(cl_hw_tcv0, MAC_ACTIVE);
+
+               return 0;
+       }
+
+       if (tcv1_en)
+               cl_msg_tx_idle_async(cl_hw_tcv1);
+
+       if (tcv0_en)
+               cl_msg_tx_set_idle(cl_hw_tcv0, MAC_IDLE_SYNC);
+
+       if (wait_event_timeout(cl_hw->wait_queue, !cl_hw->idle_async_set,
+                              CL_MSG_CFM_TIMEOUT_JIFFIES))
+               return 0;
+
+       cl_dbg_err(cl_hw, "Timeout occurred - MM_IDLE_ASYNC_IND\n");
+
+       return -ETIMEDOUT;
+}
+
+static int _cl_calib_set_channel(struct cl_hw *cl_hw, u32 channel, u32 bw)
+{
+       u32 primary = 0;
+       u32 center = 0;
+       enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
+
+       if (cl_chandef_calc(cl_hw, channel, bw, &width, &primary, &center)) {
+               cl_dbg_err(cl_hw, "cl_chandef_calc failed\n");
+               return -EINVAL;
+       }
+
+       cl_dbg_verbose(cl_hw, "Calibrate channel %u bw %u\n", channel, BW_TO_MHZ(bw));
+
+       return _cl_msg_tx_set_channel(cl_hw, channel, bw, primary, center, SET_CHANNEL_MODE_CALIB);
+}
+
+static void cl_calib_channels_6g(struct cl_hw *cl_hw)
+{
+       int i;
+
+       /* Calibrate channels: 1, 33, 65, 97, 129, 161, 193, 225 */
+       for (i = 0; i < CALIB_CHAN_6G_MAX; i += 2)
+               _cl_calib_set_channel(cl_hw, calib_channels_6g[i], CHNL_BW_160);
+
+       for (i = 0; i < CALIB_CHAN_6G_MAX; i++) {
+               _cl_calib_set_channel(cl_hw, calib_channels_6g[i], CHNL_BW_80);
+               _cl_calib_set_channel(cl_hw, calib_channels_6g[i], CHNL_BW_20);
+       }
+}
+
+static void cl_calib_channels_5g(struct cl_hw *cl_hw)
+{
+       int i;
+
+       _cl_calib_set_channel(cl_hw, 36, CHNL_BW_160);
+       _cl_calib_set_channel(cl_hw, 100, CHNL_BW_160);
+
+       for (i = 0; i < CALIB_CHAN_5G_MAX; i++) {
+               _cl_calib_set_channel(cl_hw, calib_channels_5g[i], CHNL_BW_80);
+               _cl_calib_set_channel(cl_hw, calib_channels_5g[i], CHNL_BW_20);
+       }
+}
+
+static void cl_calib_channels_24g(struct cl_hw *cl_hw)
+{
+       int i;
+
+       for (i = 0; i < CALIB_CHAN_24G_MAX; i++) {
+               _cl_calib_set_channel(cl_hw, calib_channels_24g[i], CHNL_BW_40);
+               _cl_calib_set_channel(cl_hw, calib_channels_24g[i], CHNL_BW_20);
+       }
+}
+
+static void cl_calib_scan_all_channels(struct cl_hw *cl_hw)
+{
+       if (cl_band_is_6g(cl_hw))
+               cl_calib_channels_6g(cl_hw);
+       else if (cl_band_is_5g(cl_hw))
+               cl_calib_channels_5g(cl_hw);
+       else
+               cl_calib_channels_24g(cl_hw);
+}
+
+static void cl_calib_restore_channel(struct cl_hw *cl_hw, struct cl_calib_restore *calib_restore)
+{
+       u8 bw = calib_restore->bw;
+       u32 primary = calib_restore->primary;
+       u32 center = calib_restore->center;
+       u8 channel = calib_restore->channel;
+
+       cl_msg_tx_set_channel(cl_hw, channel, bw, primary, center);
+}
+
+static void cl_calib_print_errors(struct cl_hw *cl_hw)
+{
+       struct cl_calib_errors *errors = &cl_hw->chip->calib_db.errors[cl_hw->tcv_idx];
+
+       if (!errors->dcoc && !errors->lolc && !errors->iq_rx && !errors->iq_tx)
+               return;
+
+       pr_warn("Calibration errors: DCOC %u, LOLC %u, IQ RX %u, IQ TX %u\n",
+               errors->dcoc, errors->lolc, errors->iq_rx, errors->iq_tx);
+}
+
+static u8 cl_calib_channel_to_idx(struct cl_hw *cl_hw, u8 channel)
+{
+       u8 i = 0;
+
+       if (cl_band_is_6g(cl_hw)) {
+               for (i = 0; i < CALIB_CHAN_6G_MAX; i++)
+                       if (calib_channels_6g[i] == channel)
+                               return i;
+       } else if (cl_band_is_5g(cl_hw)) {
+               for (i = 0; i < CALIB_CHAN_5G_MAX; i++)
+                       if (calib_channels_5g[i] == channel)
+                               return i;
+       } else {
+               for (i = 0; i < CALIB_CHAN_24G_MAX; i++)
+                       if (calib_channels_24g[i] == channel)
+                               return i;
+       }
+
+       return 0;
+}
+
+static void cl_calib_check_err_dcoc(struct cl_hw *cl_hw, s16 calib_temperature,
+                                   int channel, u8 bw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       int lna, ant;
+       struct cl_dcoc_report *dcoc_calib_report_dma;
+       u8 dcoc_threshold = chip->conf->ci_dcoc_mv_thr[bw];
+       s16 i, q;
+
+       for (lna = 0; lna < DCOC_LNA_GAIN_NUM; lna++) {
+               ant_for_each(ant) {
+                       dcoc_calib_report_dma =
+                               &cl_hw->iq_dcoc_data_info.iq_dcoc_data->report.dcoc[lna][ant];
+                       i = (s16)le16_to_cpu(dcoc_calib_report_dma->i_dc);
+                       q = (s16)le16_to_cpu(dcoc_calib_report_dma->q_dc);
+
+                       if (abs(i) > dcoc_threshold) {
+                               chip->calib_db.errors[cl_hw->tcv_idx].dcoc++;
+                               cl_dbg_info(cl_hw,
+                                           "DCOC Error: lna = %u, ant = %u, "
+                                           "i (|%d|) > threshold (%d)\n",
+                                           lna, ant, i, dcoc_threshold);
+                       } else {
+                               cl_dbg_info(cl_hw,
+                                           "DCOC Valid: lna = %u, ant = %u, "
+                                           "i (|%d|) < threshold (%d)\n",
+                                           lna, ant, i, dcoc_threshold);
+                       }
+
+                       if (abs(q) > dcoc_threshold) {
+                               chip->calib_db.errors[cl_hw->tcv_idx].dcoc++;
+                               cl_dbg_info(cl_hw,
+                                           "DCOC Error: lna = %u, ant = %u, "
+                                           "q (|%d|) > threshold (%d)\n",
+                                           lna, ant, q, dcoc_threshold);
+                       } else {
+                               cl_dbg_info(cl_hw,
+                                           "DCOC Valid: lna = %u, ant = %u, "
+                                           "q (|%d|) < threshold (%d)\n",
+                                           lna, ant, q, dcoc_threshold);
+                       }
+               }
+       }
+}
+
+static void cl_calib_check_err_iq_lolc(struct cl_hw *cl_hw, s16 calib_temperature,
+                                      int channel, u8 bw, u8 plan_bitmap)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_iq_dcoc_report *report = &cl_hw->iq_dcoc_data_info.iq_dcoc_data->report;
+       int ant;
+       struct cl_lolc_report lolc_report_dma;
+       s16 lolc_threshold = chip->conf->ci_lolc_db_thr;
+       s32 lolc_qual = 0;
+
+       ant_for_each(ant) {
+               if ((plan_bitmap & (1 << ant)) == 0)
+                       continue;
+
+               lolc_report_dma = report->lolc_report[ant];
+               lolc_qual = (s16)le16_to_cpu(lolc_report_dma.lolc_qual) >> 8;
+
+               if (lolc_qual > lolc_threshold) {
+                       chip->calib_db.errors[cl_hw->tcv_idx].lolc++;
+
+                       cl_dbg_info(cl_hw,
+                                   "LOLC Error: ant = %u, n_iter = %u, "
+                                   "quality (%d) > threshold (%d)\n",
+                                   ant, lolc_report_dma.n_iter, lolc_qual, lolc_threshold);
+               } else {
+                       cl_dbg_info(cl_hw,
+                                   "LOLC Valid: ant = %u, n_iter = %u, "
+                                   "quality (%d) < threshold (%d)\n",
+                                   ant, lolc_report_dma.n_iter, lolc_qual, lolc_threshold);
+               }
+       }
+}
+
+static void cl_calib_check_err_iq(struct cl_hw *cl_hw, s16 calib_temperature,
+                                 u8 ch, u8 bw, u8 plan_bitmap)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 ant = 0;
+       struct cl_iq_report iq_report_dma;
+       s8 iq_threshold = cl_hw->chip->conf->ci_iq_db_thr;
+
+       ant_for_each(ant) {
+               if ((plan_bitmap & (1 << ant)) == 0)
+                       continue;
+
+               iq_report_dma = cl_hw->iq_dcoc_data_info.iq_dcoc_data->report.iq_tx[ant];
+
+               if (iq_report_dma.ir_db_avg_post > iq_threshold) {
+                       chip->calib_db.errors[tcv_idx].iq_tx++;
+                       cl_dbg_info(cl_hw, "IQ TX Error: ant = %u, ir (%d) > threshold (%d)\n",
+                                   ant, iq_report_dma.ir_db_avg_post, iq_threshold);
+               } else {
+                       cl_dbg_info(cl_hw, "IQ TX Valid: ant = %u, ir (%d) < threshold (%d)\n",
+                                   ant, iq_report_dma.ir_db_avg_post, iq_threshold);
+               }
+
+               iq_report_dma = cl_hw->iq_dcoc_data_info.iq_dcoc_data->report.iq_rx[ant];
+
+               if (iq_report_dma.ir_db_avg_post > iq_threshold) {
+                       chip->calib_db.errors[tcv_idx].iq_rx++;
+                       cl_dbg_info(cl_hw, "IQ RX Error: ant = %u, ir (%d) > threshold (%d)\n",
+                                   ant, iq_report_dma.ir_db_avg_post, iq_threshold);
+               } else {
+                       cl_dbg_info(cl_hw, "IQ RX Valid: ant = %u, ir (%d) < threshold (%d)\n",
+                                   ant, iq_report_dma.ir_db_avg_post, iq_threshold);
+               }
+       }
+}
+
+static u8 cl_calib_center_freq_to_idx(struct cl_hw *cl_hw, u32 center_freq)
+{
+       u8 i = 0;
+       u8 center_channel = ieee80211_frequency_to_channel(center_freq);
+
+       if (cl_band_is_6g(cl_hw)) {
+               for (i = 1; i < CALIB_CHAN_6G_MAX; i++)
+                       if (calib_channels_6g[i] > center_channel)
+                               return (i - 1);
+
+               return (CALIB_CHAN_6G_MAX - 1);
+       }
+
+       if (cl_band_is_5g(cl_hw)) {
+               for (i = 1; i < CALIB_CHAN_5G_MAX; i++)
+                       if (calib_channels_5g[i] > center_channel)
+                               return (i - 1);
+
+               return (CALIB_CHAN_5G_MAX - 1);
+       }
+
+       for (i = 0; i < CALIB_CHAN_24G_MAX; i++)
+               if (abs(calib_channels_24g[i] - center_channel) < 3)
+                       return i;
+
+       return (CALIB_CHAN_24G_MAX - 1);
+}
+
+static void cl_calib_fill_data_dcoc(struct cl_hw *cl_hw, struct cl_iq_dcoc_info *iq_dcoc_db)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       u8 lna = 0, ant = 0;
+       u8 channel_idx = cl_calib_center_freq_to_idx(cl_hw, cl_hw->center_freq);
+       u8 bw = cl_hw->bw;
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 sx = tcv_idx;
+
+       for (lna = 0; lna < DCOC_LNA_GAIN_NUM; lna++)
+               ant_for_each(ant)
+                       iq_dcoc_db->dcoc[lna][ant] =
+                               chip->calib_db.dcoc[tcv_idx][channel_idx][bw][sx][ant][lna];
+}
+
+static void cl_calib_fill_data_iq(struct cl_hw *cl_hw, struct cl_iq_calib *iq_data,
+                                 struct cl_iq_calib *iq_chip_data)
+{
+       u8 ant = 0;
+
+       ant_for_each(ant) {
+               iq_data[ant].coef0 = cpu_to_le32(iq_chip_data[ant].coef0);
+               iq_data[ant].coef1 = cpu_to_le32(iq_chip_data[ant].coef1);
+               iq_data[ant].coef2 = cpu_to_le32(iq_chip_data[ant].coef2);
+               iq_data[ant].gain = cpu_to_le32(iq_chip_data[ant].gain);
+       }
+}
+
+static void cl_calib_fill_data_iq_lolc(struct cl_hw *cl_hw, __le32 *iq_lolc)
+{
+       struct cl_calib_db *calib_db = &cl_hw->chip->calib_db;
+       u8 ant = 0;
+       u8 chan_idx = cl_calib_center_freq_to_idx(cl_hw, cl_hw->center_freq);
+       u8 bw = cl_hw->bw;
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 sx = tcv_idx;
+
+       ant_for_each(ant)
+               iq_lolc[ant] = cpu_to_le32(calib_db->iq_tx_lolc[tcv_idx][chan_idx][bw][sx][ant]);
+}
+
+static void cl_calib_handle_cfm_dcoc(struct cl_hw *cl_hw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_dcoc_calib *dcoc_calib;
+       struct cl_dcoc_calib *dcoc_calib_dma;
+       struct calib_cfm *dcoc_iq_cfm =
+               &cl_hw->iq_dcoc_data_info.iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_DCOC];
+       int lna, ant;
+       u16 raw_bits = (le16_to_cpu(dcoc_iq_cfm->raw_bits_data_0) +
+                       le16_to_cpu(dcoc_iq_cfm->raw_bits_data_1)) / 2;
+       s16 calib_temperature = cl_temperature_calib_calc(cl_hw, raw_bits);
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 sx = tcv_idx;
+       u8 channel = cl_hw->channel;
+       u8 bw = cl_hw->bw;
+       u8 channel_idx = cl_calib_channel_to_idx(cl_hw, channel);
+
+       for (lna = 0; lna < DCOC_LNA_GAIN_NUM; lna++) {
+               ant_for_each(ant) {
+                       dcoc_calib = &chip->calib_db.dcoc[tcv_idx][channel_idx][bw][sx][ant][lna];
+                       dcoc_calib_dma =
+                               &cl_hw->iq_dcoc_data_info.iq_dcoc_data->iq_dcoc_db.dcoc[lna][ant];
+                       dcoc_calib->i = dcoc_calib_dma->i;
+                       dcoc_calib->q = dcoc_calib_dma->q;
+               }
+       }
+
+       cl_calib_check_err_dcoc(cl_hw, calib_temperature, channel, bw);
+
+       /*
+        * Set the default status to FAIL, to ensure FW is actually changing the value,
+        * if the calibration succeeded.
+        */
+       cl_hw->iq_dcoc_data_info.iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_DCOC].status = CALIB_FAIL;
+}
+
+static void cl_calib_handle_cfm_iq(struct cl_hw *cl_hw, u8 plan_bitmap)
+{
+       struct calib_cfm *dcoc_iq_cfm =
+               &cl_hw->iq_dcoc_data_info.iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_IQ];
+       u16 raw_bits_data_0 = le16_to_cpu(dcoc_iq_cfm->raw_bits_data_0);
+       u16 raw_bits_data_1 = le16_to_cpu(dcoc_iq_cfm->raw_bits_data_1);
+       u16 raw_bits = (raw_bits_data_0 + raw_bits_data_1) / 2;
+       s16 calib_temperature = cl_temperature_calib_calc(cl_hw, raw_bits);
+       u8 channel = cl_hw->channel;
+       u8 bw = cl_hw->bw;
+       int ant;
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 sx = tcv_idx;
+       u8 channel_idx = cl_calib_channel_to_idx(cl_hw, channel);
+
+       ant_for_each(ant) {
+               if ((plan_bitmap & (1 << ant)) == 0)
+                       continue;
+
+               cl_hw->chip->calib_db.iq_tx[tcv_idx][channel_idx][bw][sx][ant] =
+                       cl_hw->iq_dcoc_data_info.iq_dcoc_data->iq_dcoc_db.iq_tx[ant];
+
+               cl_hw->chip->calib_db.iq_rx[tcv_idx][channel_idx][bw][sx][ant] =
+                       cl_hw->iq_dcoc_data_info.iq_dcoc_data->iq_dcoc_db.iq_rx[ant];
+       }
+
+       cl_calib_check_err_iq(cl_hw, calib_temperature, channel, bw, plan_bitmap);
+
+       /*
+        * Set the default status to FAIL, to ensure FW is actually changing the value,
+        * if the calibration succeeded.
+        */
+       dcoc_iq_cfm->status = CALIB_FAIL;
+}
+
+static void cl_calib_handle_cfm_iq_lolc(struct cl_hw *cl_hw, u8 plan_bitmap)
+{
+       struct calib_cfm *dcoc_iq_cfm =
+               &cl_hw->iq_dcoc_data_info.iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_IQ];
+       u16 raw_bits = (le16_to_cpu(dcoc_iq_cfm->raw_bits_data_0) +
+               le16_to_cpu(dcoc_iq_cfm->raw_bits_data_1)) / 2;
+       s16 calib_temperature = cl_temperature_calib_calc(cl_hw, raw_bits);
+       u8 channel = cl_hw->channel;
+       u8 channel_idx = cl_calib_channel_to_idx(cl_hw, channel);
+       u8 bw = cl_hw->bw;
+       u8 sx = cl_hw->tcv_idx;
+       int ant;
+
+       ant_for_each(ant) {
+               if ((plan_bitmap & (1 << ant)) == 0)
+                       continue;
+
+               cl_hw->chip->calib_db.iq_tx_lolc[cl_hw->tcv_idx][channel_idx][bw][sx][ant] =
+                       cl_hw->iq_dcoc_data_info.iq_dcoc_data->iq_dcoc_db.iq_tx_lolc[ant];
+       }
+
+       cl_calib_check_err_iq_lolc(cl_hw, calib_temperature, channel, bw, plan_bitmap);
+
+       /*
+        * Set the default status to FAIL, to ensure FW is actually changing the value,
+        * if the calibration succeeded.
+        */
+       dcoc_iq_cfm->status = CALIB_FAIL;
+}
+
+static void cl_calib_set_channel_start_work(struct work_struct *ws)
+{
+       struct cl_calib_work *calib_work = container_of(ws, struct cl_calib_work, ws);
+       struct cl_hw *cl_hw = calib_work->cl_hw;
+       struct cl_hw *cl_hw_other = cl_hw_other_tcv(cl_hw);
+       struct cl_chip *chip = cl_hw->chip;
+
+       cl_calib_start(cl_hw);
+
+       if (cl_chip_is_both_enabled(chip))
+               cl_calib_start(cl_hw_other);
+
+       chip->calib_db.scan_complete = true;
+}
+
+int cl_calib_start(struct cl_hw *cl_hw)
+{
+       u8 channel = cl_hw->conf->ha_channel;
+       u8 bw = cl_hw->conf->ce_channel_bandwidth;
+       enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
+       u32 primary = 0;
+       u32 center = 0;
+
+       if (cl_chandef_calc(cl_hw, channel, bw, &width, &primary, &center))
+               return -EINVAL;
+
+       return cl_calib_set_channel(cl_hw, channel, bw, primary, center);
+}
+
+void cl_calib_fill_phy_data(struct cl_hw *cl_hw, struct cl_iq_dcoc_info *iq_dcoc_db, u8 flags)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       u8 channel_idx = cl_calib_center_freq_to_idx(cl_hw, cl_hw->center_freq);
+       u8 bw = cl_hw->bw;
+       u8 tcv_idx = cl_hw->tcv_idx;
+
+       if (flags & SET_PHY_DATA_FLAGS_DCOC)
+               cl_calib_fill_data_dcoc(cl_hw, iq_dcoc_db);
+
+       if (flags & SET_PHY_DATA_FLAGS_IQ_TX_LOLC)
+               cl_calib_fill_data_iq_lolc(cl_hw, iq_dcoc_db->iq_tx_lolc);
+
+       if (flags & SET_PHY_DATA_FLAGS_IQ_TX)
+               cl_calib_fill_data_iq(cl_hw, iq_dcoc_db->iq_tx,
+                                     chip->calib_db.iq_tx[tcv_idx][channel_idx][bw][tcv_idx]);
+
+       if (flags & SET_PHY_DATA_FLAGS_IQ_RX)
+               cl_calib_fill_data_iq(cl_hw, iq_dcoc_db->iq_rx,
+                                     chip->calib_db.iq_rx[tcv_idx][channel_idx][bw][tcv_idx]);
+}
+
+int cl_calib_tables_alloc(struct cl_hw *cl_hw)
+{
+       struct cl_iq_dcoc_data *buf = NULL;
+       u32 len = sizeof(struct cl_iq_dcoc_data);
+       dma_addr_t phys_dma_addr;
+
+       buf = dma_alloc_coherent(cl_hw->chip->dev, len, &phys_dma_addr, GFP_KERNEL);
+
+       if (!buf)
+               return -1;
+
+       cl_hw->iq_dcoc_data_info.iq_dcoc_data = buf;
+       cl_hw->iq_dcoc_data_info.dma_addr = cpu_to_le32(phys_dma_addr);
+
+       cl_calib_init_cfm(cl_hw->iq_dcoc_data_info.iq_dcoc_data);
+
+       return 0;
+}
+
+void cl_calib_tables_free(struct cl_hw *cl_hw)
+{
+       struct cl_iq_dcoc_data_info *iq_dcoc_data_info = &cl_hw->iq_dcoc_data_info;
+       u32 len = sizeof(struct cl_iq_dcoc_data);
+       dma_addr_t phys_dma_addr = le32_to_cpu(iq_dcoc_data_info->dma_addr);
+
+       if (!iq_dcoc_data_info->iq_dcoc_data)
+               return;
+
+       dma_free_coherent(cl_hw->chip->dev, len, (void *)iq_dcoc_data_info->iq_dcoc_data,
+                         phys_dma_addr);
+       iq_dcoc_data_info->iq_dcoc_data = NULL;
+}
+
+bool cl_calib_is_needed(struct cl_hw *cl_hw, u8 channel, u8 bw)
+{
+       u8 channel_idx;
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 ant;
+       u32 primary = 0;
+       u32 center_freq = 0;
+       enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
+
+       if (cl_chandef_calc(cl_hw, channel, bw, &width, &primary, &center_freq)) {
+               cl_dbg_err(cl_hw, "cl_chandef_calc failed\n");
+               return false;
+       }
+
+       channel_idx = cl_calib_center_freq_to_idx(cl_hw, center_freq);
+
+       /* Check if we already calibrated */
+       ant_for_each(ant) {
+               if (cl_hw->chip->calib_db.iq_tx_lolc[tcv_idx][channel_idx][bw][tcv_idx][ant])
+                       return false;
+       }
+
+       return true;
+}
+
+int cl_calib_set_channel(struct cl_hw *cl_hw, u8 channel, u8 bw, u32 primary, u32 center)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_hw *cl_hw_other = cl_hw_other_tcv(cl_hw);
+       struct cl_calib_restore calib_restore;
+       int ret = 0;
+       u8 fem_mode = cl_hw->fem_system_mode;
+       bool save_ch_other = !!cl_hw_other->primary_freq;
+
+       if (save_ch_other)
+               cl_calib_save_channel(cl_hw_other, &calib_restore);
+
+       ret = cl_calib_set_idle(cl_hw, true);
+       if (ret)
+               return ret;
+
+       cl_fem_set_system_mode(cl_hw, FEM_MODE_LNA_BYPASS_ONLY, U8_MAX);
+       cl_afe_cfg_calib(chip);
+
+       if (chip->conf->ce_calib_scan_en && !chip->calib_db.scan_complete && cl_hw->calib_ready)
+               cl_calib_scan_all_channels(cl_hw);
+       else
+               _cl_calib_set_channel(cl_hw, channel, bw);
+
+       cl_fem_set_system_mode(cl_hw, fem_mode, U8_MAX);
+       cl_afe_cfg_restore(chip);
+
+       _cl_msg_tx_set_channel(cl_hw, channel, bw, primary, center, SET_CHANNEL_MODE_OPERETIONAL);
+
+       if (save_ch_other)
+               cl_calib_restore_channel(cl_hw_other, &calib_restore);
+
+       cl_calib_set_idle(cl_hw, false);
+
+       return ret;
+}
+
+void cl_calib_start_work(struct cl_hw *cl_hw)
+{
+       struct cl_calib_work *calib_work = kzalloc(sizeof(*calib_work), GFP_ATOMIC);
+
+       if (!calib_work)
+               return;
+
+       calib_work->cl_hw = cl_hw;
+       INIT_WORK(&calib_work->ws, cl_calib_set_channel_start_work);
+       queue_work(cl_hw->drv_workqueue, &calib_work->ws);
+}
+
+int cl_calib_handle_cfm(struct cl_hw *cl_hw, u8 mode)
+{
+       struct cl_iq_dcoc_data *iq_dcoc_data = cl_hw->iq_dcoc_data_info.iq_dcoc_data;
+       struct cl_calib_errors *errors = &cl_hw->chip->calib_db.errors[cl_hw->tcv_idx];
+
+       /*
+        * In case any of the requested calibrations failed - no need to copy
+        * the other Calibration data, and fail the whole calibration process.
+        */
+       if ((mode & SET_CHANNEL_MODE_CALIB_DCOC &&
+            iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_DCOC].status != CALIB_SUCCESS) ||
+           (mode & SET_CHANNEL_MODE_CALIB_IQ &&
+            iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_IQ].status != CALIB_SUCCESS)) {
+               cl_dbg_err(cl_hw, "Calibration failed! mode = %u, DCOC_CFM_STATUS = %u, "
+                          "IQ_CFM_STATUS = %u\n",
+                          mode,
+                          iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_DCOC].status,
+                          iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_IQ].status);
+               /* Set status to CALIB_FAIL to ensure that FW is writing the values. */
+               iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_DCOC].status = CALIB_FAIL;
+               iq_dcoc_data->dcoc_iq_cfm[CALIB_CFM_IQ].status = CALIB_FAIL;
+               return -1;
+       }
+
+       if (mode & SET_CHANNEL_MODE_CALIB_DCOC)
+               cl_calib_handle_cfm_dcoc(cl_hw);
+
+       if (mode & SET_CHANNEL_MODE_CALIB_IQ)
+               cl_calib_handle_cfm_iq(cl_hw, cl_hw->mask_num_antennas);
+
+       if (mode & SET_CHANNEL_MODE_CALIB_LOLC)
+               cl_calib_handle_cfm_iq_lolc(cl_hw, cl_hw->mask_num_antennas);
+
+       /* Print calibration errors counters */
+       cl_calib_print_errors(cl_hw);
+
+       memset(errors, 0, sizeof(*errors));
+
+       return 0;
+}
+
+int cl_calib_validate_ants(struct cl_hw *cl_hw)
+{
+       struct cl_tcv_conf *conf = cl_hw->conf;
+       u8 ant = 0;
+       int ret = 0;
+
+       for (ant = 0; ant < cl_hw->num_antennas; ant++) {
+               if (conf->ci_calib_ant_tx[ant] < cl_hw->first_ant ||
+                   conf->ci_calib_ant_tx[ant] > cl_hw->last_ant) {
+                       CL_DBG_ERROR(cl_hw,
+                                    "TX: Antenna [%u] value is out of boundaries [%u].\n"
+                                    "Minimum value allowed is: %u\n"
+                                    "Maximum value allowed is: %u\n",
+                                    ant, conf->ci_calib_ant_tx[ant], cl_hw->first_ant,
+                                    cl_hw->last_ant);
+                       ret = -1;
+               }
+
+               if (conf->ci_calib_ant_rx[ant] < cl_hw->first_ant ||
+                   conf->ci_calib_ant_rx[ant] > cl_hw->last_ant) {
+                       CL_DBG_ERROR(cl_hw,
+                                    "RX: Antenna [%u] value is out of boundaries [%u]."
+                                    "Minimum value allowed is: %u\n"
+                                    "Maximum value allowed is: %u\n",
+                                    ant, conf->ci_calib_ant_tx[ant], cl_hw->first_ant,
+                                    cl_hw->last_ant);
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+void cl_calib_iq_get_tone_vector(u8 bw, u16 *tone_vector)
+{
+       u8 tone = 0;
+
+       for (tone = 0; tone < IQ_NUM_TONES_REQ; tone++)
+               tone_vector[tone] = cpu_to_le16((u16)tone_vector_arr[bw][tone]);
+}
+
+static int cl_calib_print_dcoc(struct cl_hw *cl_hw)
+{
+       struct cl_calib_db *calib_db = &cl_hw->chip->calib_db;
+       struct cl_dcoc_calib *dcoc_calib;
+       u8 lna = 0;
+       u8 ant = 0;
+       u8 channel_idx = cl_calib_center_freq_to_idx(cl_hw, cl_hw->center_freq);
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 sx = tcv_idx;
+       u8 bw = cl_hw->bw;
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int err = 0;
+       int len = 0;
+
+       if (!buf)
+               return -ENOMEM;
+
+       len += snprintf(buf + len, PAGE_SIZE - len,
+                       "DCOC:\n"
+                       "LNA GAIN   ANTENNA   I    Q\n"
+                       "----------------------------\n");
+
+       for (lna = 0; lna < DCOC_LNA_GAIN_NUM; lna++) {
+               ant_for_each(ant) {
+                       dcoc_calib =
+                               &calib_db->dcoc[tcv_idx][channel_idx][bw][sx][ant][lna];
+
+                       len += snprintf(buf + len, PAGE_SIZE - len,
+                                       "%-11u%-10u%-5d%-5d\n", lna,
+                                       ant, dcoc_calib->i, dcoc_calib->q);
+               }
+       }
+
+       err = cl_vendor_reply(cl_hw, buf, strlen(buf));
+       kfree(buf);
+
+       return err;
+}
+
+static int cl_calib_print_lolc(struct cl_hw *cl_hw)
+{
+       struct cl_calib_db *calib_db = &cl_hw->chip->calib_db;
+       u32 lolc_calib;
+       u8 ant = 0;
+       u8 channel_idx = cl_calib_center_freq_to_idx(cl_hw, cl_hw->center_freq);
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 sx = tcv_idx;
+       u8 bw = cl_hw->bw;
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int err = 0;
+       int len = 0;
+
+       if (!buf)
+               return -ENOMEM;
+
+       len += snprintf(buf + len, PAGE_SIZE - len,
+                       "LOLC:\n"
+                       "ANTENNA   I     Q\n"
+                       "---------------------\n");
+
+       ant_for_each(ant) {
+               lolc_calib = calib_db->iq_tx_lolc[tcv_idx][channel_idx][bw][sx][ant];
+
+               len += snprintf(buf + len, PAGE_SIZE - len,
+                               "%-10u%-6d%-6d\n",
+                               ant, CAST_S12_TO_S32(lolc_calib & U12_BIT_MASK),
+                               CAST_S12_TO_S32((lolc_calib >> 2) & U12_BIT_MASK));
+       }
+
+       err = cl_vendor_reply(cl_hw, buf, strlen(buf));
+       kfree(buf);
+
+       return err;
+}
+
+static int cl_calib_print_iq(struct cl_hw *cl_hw)
+{
+       struct cl_calib_db *calib_db = &cl_hw->chip->calib_db;
+       struct cl_iq_calib *iq;
+       u8 ant = 0;
+       u8 channel_idx = cl_calib_center_freq_to_idx(cl_hw, cl_hw->center_freq);
+       u8 tcv_idx = cl_hw->tcv_idx;
+       u8 sx = tcv_idx;
+       u8 bw = cl_hw->bw;
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int err = 0;
+       int len = 0;
+
+       if (!buf)
+               return -ENOMEM;
+
+       len += snprintf(buf + len, PAGE_SIZE - len,
+                       "IQ TX:\n"
+                       "ANTENNA COEF0      COEF1      COEF2      GAIN\n"
+                       "---------------------------------------------------\n");
+
+       ant_for_each(ant) {
+               iq = &calib_db->iq_tx[tcv_idx][channel_idx][bw][sx][ant];
+
+               len += snprintf(buf + len, PAGE_SIZE - len,
+                               "%-7u 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                               ant, iq->coef0, iq->coef1, iq->coef2, iq->gain);
+       }
+
+       len += snprintf(buf + len, PAGE_SIZE - len,
+                       "IQ RX:\n"
+                       "ANTENNA COEF0      COEF1      COEF2      GAIN\n"
+                       "---------------------------------------------------\n");
+
+       ant_for_each(ant) {
+               iq = &calib_db->iq_rx[tcv_idx][channel_idx][bw][sx][ant];
+
+               len += snprintf(buf + len, PAGE_SIZE - len,
+                               "%-7u 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                               ant, iq->coef0, iq->coef1, iq->coef2, iq->gain);
+       }
+       err = cl_vendor_reply(cl_hw, buf, strlen(buf));
+       kfree(buf);
+
+       return err;
+}
+
+static int cl_calib_common_cli_help(struct cl_hw *cl_hw)
+{
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int err = 0;
+
+       if (!buf)
+               return -ENOMEM;
+
+       snprintf(buf, PAGE_SIZE,
+                "calib usage:\n"
+                "-d : Print DCOC coefficients\n"
+                "-i : Print IQ coefficients\n"
+                "-l : Print LOLC coefficients\n");
+
+       err = cl_vendor_reply(cl_hw, buf, strlen(buf));
+       kfree(buf);
+
+       return err;
+}
+
+int cl_calib_cli(struct cl_hw *cl_hw, struct cli_params *cli_params)
+{
+       switch (cli_params->option) {
+       case 'd':
+               return cl_calib_print_dcoc(cl_hw);
+       case 'i':
+               return cl_calib_print_iq(cl_hw);
+       case 'l':
+               return cl_calib_print_lolc(cl_hw);
+       case '?':
+               return cl_calib_common_cli_help(cl_hw);
+       default:
+               cl_dbg_err(cl_hw, "Illegal option (%c) - try '?' for help\n",
+                          cli_params->option);
+               return 0;
+       }
+}
--
2.30.0

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________






[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux