Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33fd5033 authored by Emmanuel Grumbach's avatar Emmanuel Grumbach Committed by John W. Linville
Browse files

iwlwifi-5000: add run time calibrations for 5000



This patch adds support for run time calibrations for the 5000 family HW.
Those calibrations are sensitivity calibration, and chain noise calibration.

Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent c031bf80
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -119,6 +119,15 @@ config IWL5000
	  This option enables support for Intel Wireless WiFi Link 5000AGN Family
	  Dependency on 4965 is temporary

config IWL5000_RUN_TIME_CALIB
	bool "Enable run time Calibration for 5000 NIC"
	select IWLWIFI_RUN_TIME_CALIB
	depends on IWL5000
	default y
	---help---
	  This option will enable run time calibration for the iwl5000 driver.
	  These calibrations are Sensitivity and Chain Noise. If unsure, say yes


config IWLWIFI_DEBUGFS
        bool "Iwlwifi debugfs support"
+31 −0
Original line number Diff line number Diff line
@@ -2671,6 +2671,37 @@ struct iwl4965_calibration_cmd {
	u8 reserved1;
} __attribute__ ((packed));

/* Phy calibration command for 5000 series */

enum {
	IWL5000_PHY_CALIBRATE_DC_CMD		= 8,
	IWL5000_PHY_CALIBRATE_LO_CMD		= 9,
	IWL5000_PHY_CALIBRATE_RX_BB_CMD		= 10,
	IWL5000_PHY_CALIBRATE_TX_IQ_CMD		= 11,
	IWL5000_PHY_CALIBRATE_RX_IQ_CMD		= 12,
	IWL5000_PHY_CALIBRATION_NOISE_CMD	= 13,
	IWL5000_PHY_CALIBRATE_AGC_TABLE_CMD	= 14,
	IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD	= 15,
	IWL5000_PHY_CALIBRATE_BASE_BAND_CMD	= 16,
	IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD = 18,
	IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD = 19,
};

struct iwl5000_calibration_chain_noise_reset_cmd {
	u8 op_code;	/* IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
	u8 flags;	/* not used */
	__le16 reserved;
} __attribute__ ((packed));

struct iwl5000_calibration_chain_noise_gain_cmd {
	u8 op_code;	/* IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */
	u8 flags;	/* not used */
	__le16 reserved;
	u8 delta_gain_1;
	u8 delta_gain_2;
	__le16 reserved1;
} __attribute__ ((packed));

/******************************************************************************
 * (12)
 * Miscellaneous Commands:
+101 −0
Original line number Diff line number Diff line
@@ -125,6 +125,100 @@ static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address)
	return (address & ADDRESS_MSK) + (offset << 1);
}

#ifdef CONFIG_IWL5000_RUN_TIME_CALIB

static void iwl5000_gain_computation(struct iwl_priv *priv,
		u32 average_noise[NUM_RX_CHAINS],
		u16 min_average_noise_antenna_i,
		u32 min_average_noise)
{
	int i;
	s32 delta_g;
	struct iwl_chain_noise_data *data = &priv->chain_noise_data;

	/* Find Gain Code for the antennas B and C */
	for (i = 1; i < NUM_RX_CHAINS; i++) {
		if ((data->disconn_array[i])) {
			data->delta_gain_code[i] = 0;
			continue;
		}
		delta_g = (1000 * ((s32)average_noise[0] -
			(s32)average_noise[i])) / 1500;
		/* bound gain by 2 bits value max, 3rd bit is sign */
		data->delta_gain_code[i] =
			min(abs(delta_g), CHAIN_NOISE_MAX_DELTA_GAIN_CODE);

		if (delta_g < 0)
			/* set negative sign */
			data->delta_gain_code[i] |= (1 << 2);
	}

	IWL_DEBUG_CALIB("Delta gains: ANT_B = %d  ANT_C = %d\n",
			data->delta_gain_code[1], data->delta_gain_code[2]);

	if (!data->radio_write) {
		struct iwl5000_calibration_chain_noise_gain_cmd cmd;
		memset(&cmd, 0, sizeof(cmd));

		cmd.op_code = IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD;
		cmd.delta_gain_1 = data->delta_gain_code[1];
		cmd.delta_gain_2 = data->delta_gain_code[2];
		iwl_send_cmd_pdu_async(priv, REPLY_PHY_CALIBRATION_CMD,
			sizeof(cmd), &cmd, NULL);

		data->radio_write = 1;
		data->state = IWL_CHAIN_NOISE_CALIBRATED;
	}

	data->chain_noise_a = 0;
	data->chain_noise_b = 0;
	data->chain_noise_c = 0;
	data->chain_signal_a = 0;
	data->chain_signal_b = 0;
	data->chain_signal_c = 0;
	data->beacon_count = 0;
}

static void iwl5000_chain_noise_reset(struct iwl_priv *priv)
{
	struct iwl_chain_noise_data *data = &priv->chain_noise_data;

	if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_associated(priv)) {
		struct iwl5000_calibration_chain_noise_reset_cmd cmd;

		memset(&cmd, 0, sizeof(cmd));
		cmd.op_code = IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD;
		if (iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
			sizeof(cmd), &cmd))
			IWL_ERROR("Could not send REPLY_PHY_CALIBRATION_CMD\n");
		data->state = IWL_CHAIN_NOISE_ACCUMULATE;
		IWL_DEBUG_CALIB("Run chain_noise_calibrate\n");
	}
}

static struct iwl_sensitivity_ranges iwl5000_sensitivity = {
	.min_nrg_cck = 95,
	.max_nrg_cck = 0,
	.auto_corr_min_ofdm = 90,
	.auto_corr_min_ofdm_mrc = 170,
	.auto_corr_min_ofdm_x1 = 120,
	.auto_corr_min_ofdm_mrc_x1 = 240,

	.auto_corr_max_ofdm = 120,
	.auto_corr_max_ofdm_mrc = 210,
	.auto_corr_max_ofdm_x1 = 155,
	.auto_corr_max_ofdm_mrc_x1 = 290,

	.auto_corr_min_cck = 125,
	.auto_corr_max_cck = 200,
	.auto_corr_min_cck_mrc = 170,
	.auto_corr_max_cck_mrc = 400,
	.nrg_th_cck = 95,
	.nrg_th_ofdm = 95,
};

#endif /* CONFIG_IWL5000_RUN_TIME_CALIB */

static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv,
					   size_t offset)
{
@@ -159,6 +253,9 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
	priv->hw_params.max_bsm_size = BSM_SRAM_SIZE;
	priv->hw_params.fat_channel =  BIT(IEEE80211_BAND_2GHZ) |
					BIT(IEEE80211_BAND_5GHZ);
#ifdef CONFIG_IWL5000_RUN_TIME_CALIB
	priv->hw_params.sens = &iwl5000_sensitivity;
#endif

	switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
	case CSR_HW_REV_TYPE_5100:
@@ -202,6 +299,10 @@ static struct iwl_hcmd_ops iwl5000_hcmd = {
};

static struct iwl_hcmd_utils_ops iwl5000_hcmd_utils = {
#ifdef CONFIG_IWL5000_RUN_TIME_CALIB
	.gain_computation = iwl5000_gain_computation,
	.chain_noise_reset = iwl5000_chain_noise_reset,
#endif
};

static struct iwl_lib_ops iwl5000_lib = {