root/drivers/net/wireless/ti/wl12xx/cmd.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * This file is part of wl12xx
 *
 * Copyright (C) 2009-2010 Nokia Corporation
 * Copyright (C) 2011 Texas Instruments Inc.
 */

#include "../wlcore/cmd.h"
#include "../wlcore/debug.h"

#include "wl12xx.h"
#include "cmd.h"

int wl1271_cmd_ext_radio_parms(struct wl1271 *wl)
{
        struct wl1271_ext_radio_parms_cmd *ext_radio_parms;
        struct wl12xx_priv *priv = wl->priv;
        struct wl12xx_conf_rf *rf = &priv->conf.rf;
        int ret;

        if (!wl->nvs)
                return -ENODEV;

        ext_radio_parms = kzalloc_obj(*ext_radio_parms);
        if (!ext_radio_parms)
                return -ENOMEM;

        ext_radio_parms->test.id = TEST_CMD_INI_FILE_RF_EXTENDED_PARAM;

        memcpy(ext_radio_parms->tx_per_channel_power_compensation_2,
               rf->tx_per_channel_power_compensation_2,
               CONF_TX_PWR_COMPENSATION_LEN_2);
        memcpy(ext_radio_parms->tx_per_channel_power_compensation_5,
               rf->tx_per_channel_power_compensation_5,
               CONF_TX_PWR_COMPENSATION_LEN_5);

        wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_EXT_RADIO_PARAM: ",
                    ext_radio_parms, sizeof(*ext_radio_parms));

        ret = wl1271_cmd_test(wl, ext_radio_parms, sizeof(*ext_radio_parms), 0);
        if (ret < 0)
                wl1271_warning("TEST_CMD_INI_FILE_RF_EXTENDED_PARAM failed");

        kfree(ext_radio_parms);
        return ret;
}

int wl1271_cmd_general_parms(struct wl1271 *wl)
{
        struct wl1271_general_parms_cmd *gen_parms;
        struct wl1271_ini_general_params *gp =
                &((struct wl1271_nvs_file *)wl->nvs)->general_params;
        struct wl12xx_priv *priv = wl->priv;
        bool answer = false;
        int ret;

        if (!wl->nvs)
                return -ENODEV;

        if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
                wl1271_warning("FEM index from INI out of bounds");
                return -EINVAL;
        }

        gen_parms = kzalloc_obj(*gen_parms);
        if (!gen_parms)
                return -ENOMEM;

        gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM;

        memcpy(&gen_parms->general_params, gp, sizeof(*gp));

        /* If we started in PLT FEM_DETECT mode, force auto detect */
        if (wl->plt_mode == PLT_FEM_DETECT)
                gen_parms->general_params.tx_bip_fem_auto_detect = true;

        if (gen_parms->general_params.tx_bip_fem_auto_detect)
                answer = true;

        /* Override the REF CLK from the NVS with the one from platform data */
        gen_parms->general_params.ref_clock = priv->ref_clock;

        ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
        if (ret < 0) {
                wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
                goto out;
        }

        gp->tx_bip_fem_manufacturer =
                gen_parms->general_params.tx_bip_fem_manufacturer;

        if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
                wl1271_warning("FEM index from FW out of bounds");
                ret = -EINVAL;
                goto out;
        }

        /* If we are in calibrator based fem auto detect - save fem nr */
        if (wl->plt_mode == PLT_FEM_DETECT)
                wl->fem_manuf = gp->tx_bip_fem_manufacturer;

        wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n",
                answer == false ?
                        "manual" :
                wl->plt_mode == PLT_FEM_DETECT ?
                        "calibrator_fem_detect" :
                        "auto",
                gp->tx_bip_fem_manufacturer);

out:
        kfree(gen_parms);
        return ret;
}

int wl128x_cmd_general_parms(struct wl1271 *wl)
{
        struct wl128x_general_parms_cmd *gen_parms;
        struct wl128x_ini_general_params *gp =
                &((struct wl128x_nvs_file *)wl->nvs)->general_params;
        struct wl12xx_priv *priv = wl->priv;
        bool answer = false;
        int ret;

        if (!wl->nvs)
                return -ENODEV;

        if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
                wl1271_warning("FEM index from ini out of bounds");
                return -EINVAL;
        }

        gen_parms = kzalloc_obj(*gen_parms);
        if (!gen_parms)
                return -ENOMEM;

        gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM;

        memcpy(&gen_parms->general_params, gp, sizeof(*gp));

        /* If we started in PLT FEM_DETECT mode, force auto detect */
        if (wl->plt_mode == PLT_FEM_DETECT)
                gen_parms->general_params.tx_bip_fem_auto_detect = true;

        if (gen_parms->general_params.tx_bip_fem_auto_detect)
                answer = true;

        /* Replace REF and TCXO CLKs with the ones from platform data */
        gen_parms->general_params.ref_clock = priv->ref_clock;
        gen_parms->general_params.tcxo_ref_clock = priv->tcxo_clock;

        ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
        if (ret < 0) {
                wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
                goto out;
        }

        gp->tx_bip_fem_manufacturer =
                gen_parms->general_params.tx_bip_fem_manufacturer;

        if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
                wl1271_warning("FEM index from FW out of bounds");
                ret = -EINVAL;
                goto out;
        }

        /* If we are in calibrator based fem auto detect - save fem nr */
        if (wl->plt_mode == PLT_FEM_DETECT)
                wl->fem_manuf = gp->tx_bip_fem_manufacturer;

        wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n",
                answer == false ?
                        "manual" :
                wl->plt_mode == PLT_FEM_DETECT ?
                        "calibrator_fem_detect" :
                        "auto",
                gp->tx_bip_fem_manufacturer);

out:
        kfree(gen_parms);
        return ret;
}

int wl1271_cmd_radio_parms(struct wl1271 *wl)
{
        struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs;
        struct wl1271_radio_parms_cmd *radio_parms;
        struct wl1271_ini_general_params *gp = &nvs->general_params;
        int ret, fem_idx;

        if (!wl->nvs)
                return -ENODEV;

        radio_parms = kzalloc_obj(*radio_parms);
        if (!radio_parms)
                return -ENOMEM;

        radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;

        fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer);

        /* 2.4GHz parameters */
        memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
               sizeof(struct wl1271_ini_band_params_2));
        memcpy(&radio_parms->dyn_params_2,
               &nvs->dyn_radio_params_2[fem_idx].params,
               sizeof(struct wl1271_ini_fem_params_2));

        /* 5GHz parameters */
        memcpy(&radio_parms->static_params_5,
               &nvs->stat_radio_params_5,
               sizeof(struct wl1271_ini_band_params_5));
        memcpy(&radio_parms->dyn_params_5,
               &nvs->dyn_radio_params_5[fem_idx].params,
               sizeof(struct wl1271_ini_fem_params_5));

        wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
                    radio_parms, sizeof(*radio_parms));

        ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0);
        if (ret < 0)
                wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed");

        kfree(radio_parms);
        return ret;
}

int wl128x_cmd_radio_parms(struct wl1271 *wl)
{
        struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;
        struct wl128x_radio_parms_cmd *radio_parms;
        struct wl128x_ini_general_params *gp = &nvs->general_params;
        int ret, fem_idx;

        if (!wl->nvs)
                return -ENODEV;

        radio_parms = kzalloc_obj(*radio_parms);
        if (!radio_parms)
                return -ENOMEM;

        radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;

        fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer);

        /* 2.4GHz parameters */
        memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
               sizeof(struct wl128x_ini_band_params_2));
        memcpy(&radio_parms->dyn_params_2,
               &nvs->dyn_radio_params_2[fem_idx].params,
               sizeof(struct wl128x_ini_fem_params_2));

        /* 5GHz parameters */
        memcpy(&radio_parms->static_params_5,
               &nvs->stat_radio_params_5,
               sizeof(struct wl128x_ini_band_params_5));
        memcpy(&radio_parms->dyn_params_5,
               &nvs->dyn_radio_params_5[fem_idx].params,
               sizeof(struct wl128x_ini_fem_params_5));

        radio_parms->fem_vendor_and_options = nvs->fem_vendor_and_options;

        wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
                    radio_parms, sizeof(*radio_parms));

        ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0);
        if (ret < 0)
                wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed");

        kfree(radio_parms);
        return ret;
}

int wl12xx_cmd_channel_switch(struct wl1271 *wl,
                              struct wl12xx_vif *wlvif,
                              struct ieee80211_channel_switch *ch_switch)
{
        struct wl12xx_cmd_channel_switch *cmd;
        int ret;

        wl1271_debug(DEBUG_ACX, "cmd channel switch");

        cmd = kzalloc_obj(*cmd);
        if (!cmd) {
                ret = -ENOMEM;
                goto out;
        }

        cmd->role_id = wlvif->role_id;
        cmd->channel = ch_switch->chandef.chan->hw_value;
        cmd->switch_time = ch_switch->count;
        cmd->stop_tx = ch_switch->block_tx;

        /* FIXME: control from mac80211 in the future */
        /* Enable TX on the target channel */
        cmd->post_switch_tx_disable = 0;

        ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("failed to send channel switch command");
                goto out_free;
        }

out_free:
        kfree(cmd);

out:
        return ret;
}