diff mbox series

[RFC,v1,109/256] cl8k: add main.c

Message ID 20210617160223.160998-110-viktor.barna@celeno.com
State New
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna June 17, 2021, 3:59 p.m. UTC
From: Viktor Barna <viktor.barna@celeno.com>

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

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

--
2.30.0
diff mbox series

Patch

diff --git a/drivers/net/wireless/celeno/cl8k/main.c b/drivers/net/wireless/celeno/cl8k/main.c
new file mode 100644
index 000000000000..0b37ae8a03e1
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/main.c
@@ -0,0 +1,584 @@ 
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "chip.h"
+#include "main.h"
+#include "ops.h"
+#include "dfs/radar.h"
+#include "fw/msg_tx.h"
+#include "tx/tx.h"
+#include "reg/reg_access.h"
+#include "stats.h"
+#include "debugfs.h"
+#include "vendor_cmd.h"
+#include "chan_info.h"
+#include "tx/agg_cfm.h"
+#include "tx/single_cfm.h"
+#include "tx/bcmc_cfm.h"
+#include "tx/tx_queue.h"
+#include "rssi.h"
+#include "maintenance.h"
+#include "vns.h"
+#include "traffic.h"
+#include "ext/vlan_dscp.h"
+#include "sounding.h"
+#include "recovery.h"
+#include "rate_ctrl.h"
+#include "ext/dyn_mcast_rate.h"
+#include "ext/dyn_bcast_rate.h"
+#include "tx/tx_amsdu.h"
+#include "prot_mode.h"
+#include "utils/utils.h"
+#include "band.h"
+#include "phy/phy.h"
+#include "rf_boot.h"
+#include "dsp.h"
+#include "calib.h"
+#include "reg/reg_macsys_gcu.h"
+#include "dfs/dfs.h"
+#include "tx/sw_txhdr.h"
+#include "tx/tx_inject.h"
+#include "fem.h"
+#include "fw/fw_file.h"
+#include "cap.h"
+#include "tcv_config.h"
+#include "mac_addr.h"
+#include "hw_assert.h"
+#include "power_table.h"
+#include "noise.h"
+#include "twt.h"
+#include "fw/fw_dbg.h"
+#include "wrs/wrs_api.h"
+#ifdef CONFIG_CL_PCIE
+#include "fw/msg_rx.h"
+#include "bus/pci/irq.h"
+#include "reg/reg_ipc.h"
+#include "bus/pci/ipc.h"
+#endif
+
+MODULE_DESCRIPTION("Celeno 11ax driver for Linux");
+MODULE_VERSION("8.1.x");
+MODULE_AUTHOR("Copyright(c) 2021 Celeno Communications Ltd");
+MODULE_LICENSE("MIT");
+
+#define MAX_MU_CNT_LMAC 8
+#define MAX_MU_CNT_SMAC 8
+
+static struct ieee80211_ops cl_ops = {
+       .tx                           = cl_ops_tx,
+       .start                        = cl_ops_start,
+       .stop                         = cl_ops_stop,
+       .add_interface                = cl_ops_add_interface,
+       .remove_interface             = cl_ops_remove_interface,
+       .config                       = cl_ops_config,
+       .bss_info_changed             = cl_ops_bss_info_changed,
+       .start_ap                     = cl_ops_start_ap,
+       .stop_ap                      = cl_ops_stop_ap,
+       .prepare_multicast            = cl_ops_prepare_multicast,
+       .configure_filter             = cl_ops_configure_filter,
+       .set_key                      = cl_ops_set_key,
+       .sw_scan_start                = cl_ops_sw_scan_start,
+       .sta_state                    = cl_ops_sta_state,
+       .sta_notify                   = cl_ops_sta_notify,
+       .conf_tx                      = cl_ops_conf_tx,
+       .sta_rc_update                = cl_ops_sta_rc_update,
+       .ampdu_action                 = cl_ops_ampdu_action,
+       .post_channel_switch          = cl_ops_post_channel_switch,
+       .flush                        = cl_ops_flush,
+       .tx_frames_pending            = cl_ops_tx_frames_pending,
+       .reconfig_complete            = cl_ops_reconfig_complete,
+       .get_txpower                  = cl_ops_get_txpower,
+       .set_rts_threshold            = cl_ops_set_rts_threshold,
+       .event_callback               = cl_ops_event_callback,
+       .set_tim                      = cl_ops_set_tim,
+};
+
+static void cl_drv_workqueue_create(struct cl_hw *cl_hw)
+{
+       if (!cl_hw->drv_workqueue)
+               cl_hw->drv_workqueue = create_singlethread_workqueue("drv_workqueue");
+}
+
+static void cl_drv_workqueue_destroy(struct cl_hw *cl_hw)
+{
+       if (cl_hw->drv_workqueue) {
+               destroy_workqueue(cl_hw->drv_workqueue);
+               cl_hw->drv_workqueue = NULL;
+       }
+}
+
+static int cl_main_alloc(struct cl_hw *cl_hw)
+{
+       int ret = 0;
+
+       ret = cl_phy_data_alloc(cl_hw);
+       if (ret)
+               return ret;
+
+       ret = cl_calib_tables_alloc(cl_hw);
+       if (ret)
+               return ret;
+
+       ret = cl_power_table_alloc(cl_hw);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static void cl_main_free(struct cl_hw *cl_hw)
+{
+       cl_phy_data_free(cl_hw);
+       cl_calib_tables_free(cl_hw);
+       cl_power_table_free(cl_hw);
+}
+
+static void cl_free_hw(struct cl_hw *cl_hw)
+{
+       struct ieee80211_hw *hw = cl_hw->hw;
+
+       cl_tcv_config_free(cl_hw);
+
+       if (hw->wiphy->registered)
+               ieee80211_unregister_hw(hw);
+
+       cl_chip_unset_hw(cl_hw->chip, cl_hw);
+       ieee80211_free_hw(hw);
+}
+
+static void cl_free_chip(struct cl_chip *chip)
+{
+       cl_free_hw(chip->cl_hw_tcv0);
+       cl_free_hw(chip->cl_hw_tcv1);
+}
+
+static int cl_prepare_hw(struct cl_chip *chip, u8 tcv_idx,
+                        const struct cl_driver_ops *drv_ops)
+{
+       struct cl_hw *cl_hw = NULL;
+       struct ieee80211_hw *hw;
+       int ret = 0;
+
+       hw = ieee80211_alloc_hw(sizeof(struct cl_hw), &cl_ops);
+       if (!hw) {
+               cl_dbg_chip_err(chip, ": ieee80211_alloc_hw failed\n");
+               return -ENOMEM;
+       }
+
+       cl_hw_init(chip, hw->priv, tcv_idx);
+
+       cl_hw = hw->priv;
+       cl_hw->hw = hw;
+       cl_hw->tcv_idx = tcv_idx;
+       cl_hw->chip = chip;
+
+       /*
+        * chip0, tcv0 --> 0
+        * chip0, tcv1 --> 1
+        * chip1, tcv0 --> 2
+        * chip1, tcv1 --> 3
+        */
+       cl_hw->idx = chip->idx * CHIP_MAX + tcv_idx;
+
+       cl_hw->drv_ops = drv_ops;
+
+       if (cl_hw_is_tcv0(cl_hw))
+               cl_hw->max_mu_cnt = MAX_MU_CNT_LMAC;
+       else
+               cl_hw->max_mu_cnt = MAX_MU_CNT_SMAC;
+
+       SET_IEEE80211_DEV(hw, chip->dev);
+
+       ret = cl_tcv_config_alloc(cl_hw);
+       if (ret)
+               goto out_free_hw;
+
+       ret = cl_hw_set_antennas(cl_hw);
+       if (ret)
+               goto out_free_hw;
+
+       ret = cl_tcv_config_read(cl_hw);
+       if (ret)
+               goto out_free_hw;
+
+       cl_chip_set_hw(chip, cl_hw);
+
+       ret = cl_mac_addr_set(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "cl_mac_addr_set failed\n");
+               goto out_free_hw;
+       }
+
+       if (cl_band_is_6g(cl_hw))
+               cl_hw->nl_band = NL80211_BAND_6GHZ;
+       else if (cl_band_is_5g(cl_hw))
+               cl_hw->nl_band = NL80211_BAND_5GHZ;
+       else
+               cl_hw->nl_band = NL80211_BAND_2GHZ;
+
+       cl_cap_dyn_params(cl_hw);
+       cl_vendor_cmds_init(hw->wiphy);
+
+       /*
+        * ieee80211_register_hw() will take care of calling wiphy_register() and
+        * also ieee80211_if_add() (because IFTYPE_STATION is supported)
+        * which will internally call register_netdev()
+        */
+       ret = ieee80211_register_hw(hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "ieee80211_register_hw failed\n");
+               goto out_free_hw;
+       }
+
+       if (hw->wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
+               ret = regulatory_set_wiphy_regd(hw->wiphy, cl_hw->channel_info.rd);
+               if (ret)
+                       cl_dbg_err(cl_hw, "regulatory failed\n");
+       }
+
+       cl_dbg_verbose(cl_hw, "cl_hw created\n");
+
+       return 0;
+
+out_free_hw:
+       cl_free_hw(cl_hw);
+
+       return ret;
+}
+
+void cl_main_off(struct cl_hw *cl_hw)
+{
+#ifdef CONFIG_CL_PCIE
+       cl_irq_disable(cl_hw, cl_hw->ipc_e2a_irq.all);
+       cl_ipc_stop(cl_hw);
+#endif
+
+       if (!test_bit(CL_DEV_INIT, &cl_hw->drv_flags)) {
+               cl_tx_off(cl_hw);
+               cl_rx_off(cl_hw);
+#ifdef CONFIG_CL_PCIE
+               cl_msg_rx_flush_all(cl_hw);
+#endif
+       }
+
+       cl_fw_file_cleanup(cl_hw);
+}
+
+static void _cl_main_deinit(struct cl_hw *cl_hw)
+{
+       /* First bring down all interfaces */
+       cl_vif_bring_all_interfaces_down(cl_hw);
+
+       cl_hw->is_stop_context = true;
+
+       cl_drv_workqueue_destroy(cl_hw);
+
+       cl_noise_close(cl_hw);
+       cl_maintenance_close(cl_hw);
+       cl_vns_close(cl_hw);
+       cl_rssi_assoc_exit(cl_hw);
+       cl_radar_close(cl_hw);
+       cl_sounding_close(cl_hw);
+       cl_chan_info_deinit(cl_hw);
+       cl_wrs_api_close(cl_hw);
+       cl_dfs_close(cl_hw);
+       cl_twt_close(cl_hw);
+       cl_tx_inject_close(cl_hw);
+       cl_dbgfs_unregister(cl_hw);
+       cl_main_off(cl_hw);
+       /* These 2 must be called after cl_tx_off() (which is called from cl_main_off) */
+       cl_tx_amsdu_txhdr_deinit(cl_hw);
+       cl_sw_txhdr_deinit(cl_hw);
+       cl_stats_deinit(cl_hw);
+       cl_main_free(cl_hw);
+       cl_fw_file_release(cl_hw);
+       cl_vendor_timer_close(cl_hw);
+#ifdef CONFIG_CL_PCIE
+       cl_ipc_deinit(cl_hw);
+#endif
+       cl_hw_deinit(cl_hw, cl_hw->tcv_idx);
+}
+
+void cl_main_deinit(struct cl_chip *chip)
+{
+       struct cl_hw *cl_hw_tcv0 = chip->cl_hw_tcv0;
+       struct cl_hw *cl_hw_tcv1 = chip->cl_hw_tcv1;
+
+       if (cl_chip_is_tcv1_enabled(chip) && cl_hw_tcv1)
+               _cl_main_deinit(cl_hw_tcv1);
+
+       if (cl_chip_is_tcv0_enabled(chip) && cl_hw_tcv0)
+               _cl_main_deinit(cl_hw_tcv0);
+
+       if (cl_hw_tcv1) {
+               cl_phy_off(cl_hw_tcv1);
+               cl_free_hw(cl_hw_tcv1);
+       }
+
+       if (cl_hw_tcv0) {
+               cl_phy_off(cl_hw_tcv0);
+               cl_free_hw(cl_hw_tcv0);
+       }
+}
+
+struct cl_controller_reg all_controller_reg = {
+       .breset = XMAC_BRESET,
+       .debug_enable = XMAC_DEBUG_ENABLE,
+       .dreset = XMAC_DRESET,
+       .ocd_halt_on_reset = XMAC_OCD_HALT_ON_RESET,
+       .run_stall = XMAC_RUN_STALL
+};
+
+void cl_main_reset(struct cl_chip *chip, struct cl_controller_reg *controller_reg)
+{
+       /* Release TRST & BReset to enable JTAG connection to FPGA A */
+       u32 regval;
+
+       /* 1. return to reset value */
+       regval = macsys_gcu_xt_control_get(chip);
+       regval |= controller_reg->ocd_halt_on_reset;
+       regval &= ~(controller_reg->dreset | controller_reg->run_stall | controller_reg->breset);
+       macsys_gcu_xt_control_set(chip, regval);
+
+       regval = macsys_gcu_xt_control_get(chip);
+       regval |= controller_reg->dreset;
+       macsys_gcu_xt_control_set(chip, regval);
+
+       /* 2. stall xtensa & release ocd */
+       regval = macsys_gcu_xt_control_get(chip);
+       regval |= controller_reg->run_stall;
+       regval &= ~controller_reg->ocd_halt_on_reset;
+       macsys_gcu_xt_control_set(chip, regval);
+
+       /* 3. breset release & debug enable */
+       regval = macsys_gcu_xt_control_get(chip);
+       regval |= (controller_reg->debug_enable | controller_reg->breset);
+       macsys_gcu_xt_control_set(chip, regval);
+
+       msleep(100);
+}
+
+int cl_main_on(struct cl_hw *cl_hw)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       int ret;
+       u32 regval;
+
+       cl_hw->fw_active = false;
+
+       cl_txq_init(cl_hw);
+
+       cl_hw_assert_info_init(cl_hw);
+
+       if (cl_recovery_in_progress(cl_hw))
+               cl_main_reset(chip, &cl_hw->controller_reg);
+
+       ret = cl_fw_file_load(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "cl_fw_file_load failed %d\n", ret);
+               return ret;
+       }
+
+       /* Clear CL_DEV_FW_ERROR after firmware loaded */
+       clear_bit(CL_DEV_FW_ERROR, &cl_hw->drv_flags);
+
+#ifdef CONFIG_CL_PCIE
+       if (cl_recovery_in_progress(cl_hw))
+               cl_ipc_recovery(cl_hw);
+#endif
+
+       regval = macsys_gcu_xt_control_get(chip);
+
+       /* Set fw to run */
+       if (cl_hw->fw_active)
+               regval &= ~cl_hw->controller_reg.run_stall;
+
+#ifdef CONFIG_CL_PCIE
+       /* Ack all possibly pending IRQs */
+       ipc_xmac_2_host_ack_set(chip, cl_hw->ipc_e2a_irq.all);
+#endif
+
+       macsys_gcu_xt_control_set(chip, regval);
+
+#ifdef CONFIG_CL_PCIE
+       cl_irq_enable(cl_hw, cl_hw->ipc_e2a_irq.all);
+#endif
+
+       /*
+        * cl_irq_status_sync will set CL_DEV_FW_SYNC when fw raises IPC_IRQ_E2A_SYNC
+        * (indicate its ready to accept interrupts)
+        */
+       ret = wait_event_interruptible_timeout(cl_hw->fw_sync_wq,
+                                              test_and_clear_bit(CL_DEV_FW_SYNC,
+                                                                 &cl_hw->drv_flags),
+                                              msecs_to_jiffies(5000));
+
+       if (ret == 0) {
+               pr_err("[%s]: FW synchronization timeout.\n", __func__);
+               cl_hw_assert_check(cl_hw);
+               ret = -ETIMEDOUT;
+               goto out_free_cached_fw;
+       } else if (ret == -ERESTARTSYS) {
+               goto out_free_cached_fw;
+       }
+
+       return 0;
+
+out_free_cached_fw:
+       cl_fw_file_release(cl_hw);
+       return ret;
+}
+
+static int __cl_main_init(struct cl_hw *cl_hw)
+{
+       int ret = 0;
+
+       set_bit(CL_DEV_INIT, &cl_hw->drv_flags);
+
+       /* By default, set FEM mode to operational mode. */
+       cl_hw->fem_system_mode = FEM_MODE_OPERETIONAL;
+
+       cl_vif_init(cl_hw);
+
+       cl_drv_workqueue_create(cl_hw);
+
+       init_waitqueue_head(&cl_hw->wait_queue);
+       init_waitqueue_head(&cl_hw->fw_sync_wq);
+       init_waitqueue_head(&cl_hw->radio_wait_queue);
+
+       mutex_init(&cl_hw->dbginfo.mutex);
+       mutex_init(&cl_hw->msg_tx_mutex);
+       mutex_init(&cl_hw->set_channel_mutex);
+
+       spin_lock_init(&cl_hw->tx_lock_agg);
+       spin_lock_init(&cl_hw->tx_lock_cfm_agg);
+       spin_lock_init(&cl_hw->tx_lock_single);
+       spin_lock_init(&cl_hw->tx_lock_bcmc);
+
+#ifdef CONFIG_CL_PCIE
+       ret = cl_ipc_init(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "cl_ipc_init failed %d\n", ret);
+               return ret;
+       }
+#endif
+       ret = cl_main_on(cl_hw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "cl_main_on failed %d\n", ret);
+#ifdef CONFIG_CL_PCIE
+               cl_ipc_deinit(cl_hw);
+#endif
+               return ret;
+       }
+
+       ret = cl_main_alloc(cl_hw);
+       if (ret)
+               goto out_free;
+
+       /* Reset firmware */
+       ret = cl_msg_tx_reset(cl_hw);
+       if (ret)
+               goto out_free;
+
+       cl_calib_power_read(cl_hw);
+       cl_dbgfs_register(cl_hw, "cl");
+       cl_sta_init(cl_hw);
+       cl_sw_txhdr_init(cl_hw);
+       cl_tx_amsdu_txhdr_init(cl_hw);
+       cl_tx_init(cl_hw);
+       cl_rx_init(cl_hw);
+       cl_prot_mode_init(cl_hw);
+       cl_radar_init(cl_hw);
+       cl_sounding_init(cl_hw);
+       cl_vlan_dscp_init(cl_hw);
+       cl_traffic_init(cl_hw);
+       cl_rsrc_mgmt_init(cl_hw);
+       cl_vns_init(cl_hw);
+       cl_maintenance_init(cl_hw);
+       cl_rssi_assoc_init(cl_hw);
+       cl_agg_cfm_init(cl_hw);
+       cl_single_cfm_init(cl_hw);
+       cl_bcmc_cfm_init(cl_hw);
+       cl_dyn_mcast_rate_init(cl_hw);
+       cl_dyn_bcast_rate_init(cl_hw);
+       cl_wrs_api_init(cl_hw);
+       cl_dfs_init(cl_hw);
+       cl_tx_inject_init(cl_hw);
+       cl_noise_init(cl_hw);
+       cl_twt_init(cl_hw);
+       cl_fw_dbg_trigger_based_init(cl_hw);
+       cl_stats_init(cl_hw);
+       cl_vendor_timer_init(cl_hw);
+
+       return 0;
+
+out_free:
+       cl_main_free(cl_hw);
+
+       return ret;
+}
+
+static int _cl_main_init(struct cl_chip *chip, struct cl_hw *cl_hw)
+{
+       int ret = 0;
+
+       if (cl_chip_is_tcv_enabled(chip, cl_hw->tcv_idx)) {
+               ret = __cl_main_init(cl_hw);
+               if (ret) {
+                       cl_dbg_chip_err(chip, "TCV%u failed (%d)\n", cl_hw->tcv_idx, ret);
+                       return ret;
+               }
+       } else {
+               ieee80211_unregister_hw(cl_hw->hw);
+       }
+
+       return ret;
+}
+
+int cl_main_init(struct cl_chip *chip, const struct cl_driver_ops *drv_ops)
+{
+       int ret = 0;
+
+       /* All cores needs to be reset first (once per chip) */
+       cl_main_reset(chip, &all_controller_reg);
+
+       ret = cl_prepare_hw(chip, TCV0, drv_ops);
+       if (ret) {
+               cl_dbg_chip_err(chip, "cl_prepare_hw for TCV0 failed %d\n", ret);
+               return ret;
+       }
+
+       ret = cl_prepare_hw(chip, TCV1, drv_ops);
+       if (ret) {
+               cl_dbg_chip_err(chip, "cl_prepare_hw for TCV1 failed %d\n", ret);
+               cl_free_hw(chip->cl_hw_tcv0);
+               return ret;
+       }
+
+       ret = cl_rf_boot(chip);
+       if (ret) {
+               cl_dbg_chip_err(chip, "cl_rf_boot failed %d\n", ret);
+               return ret;
+       }
+
+       ret = cl_dsp_load_regular(chip);
+       if (ret) {
+               cl_dbg_chip_err(chip, "cl_dsp_load_regular failed %d\n", ret);
+               return ret;
+       }
+
+       ret = _cl_main_init(chip, chip->cl_hw_tcv0);
+       if (ret) {
+               cl_free_chip(chip);
+               return ret;
+       }
+
+       ret = _cl_main_init(chip, chip->cl_hw_tcv1);
+       if (ret) {
+               _cl_main_deinit(chip->cl_hw_tcv0);
+               cl_free_chip(chip);
+               return ret;
+       }
+
+       return ret;
+}