new file mode 100644
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "hw.h"
+#include "rate_ctrl.h"
+#include <linux/spinlock.h>
+#include "reg/reg_access.h"
+
+static void cl_hw_init_tcv0(struct cl_hw *cl_hw)
+{
+ struct cl_controller_reg *controller_reg = &cl_hw->controller_reg;
+
+ cl_hw->fw_dst_kern_id = KERN_LMAC;
+ cl_hw->fw_prefix = 'l';
+
+ controller_reg->breset = LMAC_BRESET;
+ controller_reg->debug_enable = LMAC_DEBUG_ENABLE;
+ controller_reg->dreset = LMAC_DRESET;
+ controller_reg->ocd_halt_on_reset = LMAC_OCD_HALT_ON_RESET;
+ controller_reg->run_stall = LMAC_RUN_STALL;
+
+ cl_hw->mac_hw_regs_offset = 0;
+ cl_hw->phy_regs_offset = 0;
+}
+
+static void cl_hw_init_tcv1(struct cl_hw *cl_hw)
+{
+ struct cl_controller_reg *controller_reg = &cl_hw->controller_reg;
+
+ cl_hw->fw_dst_kern_id = KERN_SMAC;
+ cl_hw->fw_prefix = 's';
+
+ controller_reg->breset = SMAC_BRESET;
+ controller_reg->debug_enable = SMAC_DEBUG_ENABLE;
+ controller_reg->dreset = SMAC_DRESET;
+ controller_reg->ocd_halt_on_reset = SMAC_OCD_HALT_ON_RESET;
+ controller_reg->run_stall = SMAC_RUN_STALL;
+
+ cl_hw->mac_hw_regs_offset = REG_MAC_HW_SMAC_OFFSET;
+ cl_hw->phy_regs_offset = REG_PHY_SMAC_OFFSET;
+}
+
+void cl_hw_init(struct cl_chip *chip, struct cl_hw *cl_hw, u8 tcv_idx)
+{
+ write_lock(&chip->cl_hw_lock);
+ chip->cl_hw_lut[tcv_idx] = cl_hw;
+ write_unlock(&chip->cl_hw_lock);
+
+ if (tcv_idx == TCV0)
+ cl_hw_init_tcv0(cl_hw);
+ else
+ cl_hw_init_tcv1(cl_hw);
+}
+
+void cl_hw_deinit(struct cl_hw *cl_hw, u8 tcv_idx)
+{
+ struct cl_chip *chip = cl_hw->chip;
+
+ write_lock(&chip->cl_hw_lock);
+ chip->cl_hw_lut[tcv_idx] = NULL;
+ write_unlock(&chip->cl_hw_lock);
+}
+
+void cl_hw_lock(struct cl_hw *cl_hw)
+{
+ read_lock(&cl_hw->chip->cl_hw_lock);
+}
+
+void cl_hw_unlock(struct cl_hw *cl_hw)
+{
+ read_unlock(&cl_hw->chip->cl_hw_lock);
+}
+
+struct cl_hw *cl_hw_other_tcv(struct cl_hw *cl_hw)
+{
+ /* This function must be called after read lock is taken */
+ return cl_hw->chip->cl_hw_lut[1 - cl_hw->tcv_idx];
+}
+
+bool cl_hw_is_tcv0(struct cl_hw *cl_hw)
+{
+ return (cl_hw->tcv_idx == TCV0);
+}
+
+bool cl_hw_is_tcv1(struct cl_hw *cl_hw)
+{
+ return (cl_hw->tcv_idx == TCV1);
+}
+
+int cl_hw_set_antennas(struct cl_hw *cl_hw)
+{
+ struct cl_chip *chip = cl_hw->chip;
+ u8 ant_shift = cl_hw_ant_shift(cl_hw);
+
+ /* Set num_antennas and max_antennas + masks for both. */
+ switch (chip->fem.wiring_id) {
+ case FEM_WIRING_0_TCV0_6_TCV1_6:
+ case FEM_WIRING_1_TCV0_6_TCV1_6:
+ case FEM_WIRING_2_TCV0_6_TCV1_6:
+ case FEM_WIRING_3_TCV0_2_ELASTIC_4_TCV1_2:
+ case FEM_WIRING_4_TCV0_2_ELASTIC_4_TCV1_2:
+ case FEM_WIRING_5_TCV0_2_ELASTIC_4_TCV1_2:
+ case FEM_WIRING_6_TCV0_2_ELASTIC_4_TCV1_2:
+ cl_hw->max_antennas = 6;
+ break;
+ case FEM_WIRING_7_TCV0_4_TCV1_4:
+ case FEM_WIRING_8_TCV0_4_TCV1_4:
+ case FEM_WIRING_9_TCV0_4_TCV1_4:
+ case FEM_WIRING_10_TCV0_4_TCV1_4:
+ case FEM_WIRING_11_TCV0_4_TCV1_4_RX_ONLY:
+ case FEM_WIRING_12_TCV0_4_TCV1_4_RX_ONLY:
+ case FEM_WIRING_15_CHAMELEON_4TX_4RX:
+ case FEM_WIRING_18_TCV0_4_TCV1_4:
+ case FEM_WIRING_19_TCV0_2_TCV1_2_SWAPPED:
+ cl_hw->max_antennas = 4;
+ break;
+ case FEM_WIRING_13_SENSING_4RX_2TX:
+ case FEM_WIRING_14_SENSING_4TX_2RX:
+ case FEM_WIRING_20_TCV0_4_TCV1_2:
+ case FEM_WIRING_21_TCV0_4_TCV1_2:
+ cl_hw->max_antennas = cl_hw_is_tcv0(cl_hw) ? 4 : 2;
+ break;
+ case FEM_WIRING_17_TCV0_4_TCV1_0:
+ cl_hw->max_antennas = cl_hw_is_tcv0(cl_hw) ? 4 : 0;
+ break;
+ case FEM_WIRING_16_TCV0_2_TCV1_2:
+ cl_hw->max_antennas = 2;
+ break;
+ default:
+ if (chip->conf->ce_production_mode)
+ cl_hw->max_antennas = chip->max_antennas / 2;
+ else
+ return -1;
+ break;
+ }
+
+ cl_hw->num_antennas = cl_hw->conf->ce_num_antennas;
+ cl_hw->mask_num_antennas = ANT_MASK(cl_hw->num_antennas);
+ cl_hw->first_ant = ant_shift;
+ cl_hw->last_ant = cl_hw->num_antennas + ant_shift - 1;
+
+ cl_dbg_trace(cl_hw, "num_antennas = %u, max_antennas = %u\n",
+ cl_hw->num_antennas, cl_hw->max_antennas);
+
+ if (cl_hw->num_antennas > cl_hw->max_antennas) {
+ CL_DBG_ERROR(cl_hw, "num_antennas (%u) > max_antennas (%u)\n",
+ cl_hw->num_antennas, cl_hw->max_antennas);
+ return -1;
+ }
+
+ return 0;
+}
+
+u8 cl_hw_ant_shift(struct cl_hw *cl_hw)
+{
+ struct cl_chip *chip = cl_hw->chip;
+
+ /* CL808x uses chains 0 - 3 for both bands */
+ if (cl_chip_is_8ant(chip))
+ return 0;
+
+ /* CL804x uses chains 0 - 1 for TCV0 and chains 2 - 3 for TCV1 */
+ /* CL806x uses chains 0 - 3 for TCV0 and chains 2 - 3 for TCV1 */
+ return cl_hw_is_tcv0(cl_hw) ? 0 : 2;
+}
+