Search Linux Wireless

[RFC v1 109/256] cl8k: add main.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/main.c | 584 ++++++++++++++++++++++++
 1 file changed, 584 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/main.c

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;
+}
--
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