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

Commit 7c616cba authored by Tomas Winkler's avatar Tomas Winkler Committed by John W. Linville
Browse files

iwlwifi-5000: implement initial calibration for 5000



This patch adds initial calibration framework for 5000 HW faimily.

Signed-off-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarGregory Greenman <gregory.greenman@intel.com>
Signed-off-by: default avatarRon Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent c1354754
Loading
Loading
Loading
Loading
+127 −0
Original line number Diff line number Diff line
@@ -395,6 +395,8 @@ static struct iwl_sensitivity_ranges iwl5000_sensitivity = {

#endif /* CONFIG_IWL5000_RUN_TIME_CALIB */



static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv,
					   size_t offset)
{
@@ -403,6 +405,118 @@ static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv,
	return &priv->eeprom[address];
}

/*
 *  Calibration
 */
static int iwl5000_send_Xtal_calib(struct iwl_priv *priv)
{
	u16 *xtal_calib = (u16 *)iwl_eeprom_query_addr(priv, EEPROM_5000_XTAL);

	struct iwl5000_calibration cal_cmd = {
		.op_code = IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD,
		.data = {
			(u8)xtal_calib[0],
			(u8)xtal_calib[1],
		}
	};

	return iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
				sizeof(cal_cmd), &cal_cmd);
}

static int iwl5000_send_calib_results(struct iwl_priv *priv)
{
	int ret = 0;

	if (priv->calib_results.lo_res)
		ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
					priv->calib_results.lo_res_len,
					priv->calib_results.lo_res);
	if (ret)
		goto err;


	if (priv->calib_results.tx_iq_res)
		ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
				priv->calib_results.tx_iq_res_len,
				priv->calib_results.tx_iq_res);

	if (ret)
		goto err;

	if (priv->calib_results.tx_iq_perd_res)
		ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
				      priv->calib_results.tx_iq_perd_res_len,
				      priv->calib_results.tx_iq_perd_res);
	if (ret)
		goto err;

	return 0;
err:
	IWL_ERROR("Error %d\n", ret);
	return ret;
}

static int iwl5000_send_calib_cfg(struct iwl_priv *priv)
{
	struct iwl5000_calib_cfg_cmd calib_cfg_cmd;
	struct iwl_host_cmd cmd = {
		.id = CALIBRATION_CFG_CMD,
		.len = sizeof(struct iwl5000_calib_cfg_cmd),
		.data = &calib_cfg_cmd,
	};

	memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
	calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL;
	calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL;
	calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL;
	calib_cfg_cmd.ucd_calib_cfg.flags = IWL_CALIB_INIT_CFG_ALL;

	return iwl_send_cmd(priv, &cmd);
}

static void iwl5000_rx_calib_result(struct iwl_priv *priv,
			     struct iwl_rx_mem_buffer *rxb)
{
	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
	struct iwl5000_calib_hdr *hdr = (struct iwl5000_calib_hdr *)pkt->u.raw;
	int len = le32_to_cpu(pkt->len) & FH_RSCSR_FRAME_SIZE_MSK;

	iwl_free_calib_results(priv);

	/* reduce the size of the length field itself */
	len -= 4;

	switch (hdr->op_code) {
	case IWL5000_PHY_CALIBRATE_LO_CMD:
		priv->calib_results.lo_res = kzalloc(len, GFP_ATOMIC);
		priv->calib_results.lo_res_len = len;
		memcpy(priv->calib_results.lo_res, pkt->u.raw, len);
		break;
	case IWL5000_PHY_CALIBRATE_TX_IQ_CMD:
		priv->calib_results.tx_iq_res = kzalloc(len, GFP_ATOMIC);
		priv->calib_results.tx_iq_res_len = len;
		memcpy(priv->calib_results.tx_iq_res, pkt->u.raw, len);
		break;
	case IWL5000_PHY_CALIBRATE_TX_IQ_PERD_CMD:
		priv->calib_results.tx_iq_perd_res = kzalloc(len, GFP_ATOMIC);
		priv->calib_results.tx_iq_perd_res_len = len;
		memcpy(priv->calib_results.tx_iq_perd_res, pkt->u.raw, len);
		break;
	default:
		IWL_ERROR("Unknown calibration notification %d\n",
			  hdr->op_code);
		return;
	}
}

static void iwl5000_rx_calib_complete(struct iwl_priv *priv,
			       struct iwl_rx_mem_buffer *rxb)
{
	IWL_DEBUG_INFO("Init. calibration is completed, restarting fw.\n");
	queue_work(priv->workqueue, &priv->restart);
}

/*
 * ucode
 */
@@ -565,6 +679,7 @@ static void iwl5000_init_alive_start(struct iwl_priv *priv)
		goto restart;
	}

	iwl5000_send_calib_cfg(priv);
	return;

restart:
@@ -684,8 +799,14 @@ static int iwl5000_alive_notify(struct iwl_priv *priv)
	iwl_release_nic_access(priv);
	spin_unlock_irqrestore(&priv->lock, flags);


	iwl5000_send_wimax_coex(priv);

	iwl5000_send_Xtal_calib(priv);

	if (priv->ucode_type == UCODE_RT)
		iwl5000_send_calib_results(priv);

	return 0;
}

@@ -856,8 +977,14 @@ static u16 iwl5000_get_hcmd_size(u8 cmd_id, u16 len)

static void iwl5000_rx_handler_setup(struct iwl_priv *priv)
{
	/* init calibration handlers */
	priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] =
					iwl5000_rx_calib_result;
	priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] =
					iwl5000_rx_calib_complete;
}


static int iwl5000_hw_valid_rtc_data_addr(u32 addr)
{
	return (addr >= RTC_DATA_LOWER_BOUND) &&
+50 −0
Original line number Diff line number Diff line
@@ -2778,10 +2778,59 @@ enum {
	IWL5000_PHY_CALIBRATE_AGC_TABLE_CMD	= 14,
	IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD	= 15,
	IWL5000_PHY_CALIBRATE_BASE_BAND_CMD	= 16,
	IWL5000_PHY_CALIBRATE_TX_IQ_PERD_CMD	= 17,
	IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD = 18,
	IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD = 19,
};

enum {
	CALIBRATION_CFG_CMD = 0x65,
	CALIBRATION_RES_NOTIFICATION = 0x66,
	CALIBRATION_COMPLETE_NOTIFICATION = 0x67
};

struct iwl_cal_crystal_freq_cmd {
	u8 cap_pin1;
	u8 cap_pin2;
} __attribute__ ((packed));

struct iwl5000_calibration {
	u8 op_code;
	u8 first_group;
	u8 num_groups;
	u8 all_data_valid;
	struct iwl_cal_crystal_freq_cmd data;
} __attribute__ ((packed));

#define IWL_CALIB_INIT_CFG_ALL	__constant_cpu_to_le32(0xffffffff)

struct iwl_calib_cfg_elmnt_s {
	__le32 is_enable;
	__le32 start;
	__le32 send_res;
	__le32 apply_res;
	__le32 reserved;
} __attribute__ ((packed));

struct iwl_calib_cfg_status_s {
	struct iwl_calib_cfg_elmnt_s once;
	struct iwl_calib_cfg_elmnt_s perd;
	__le32 flags;
} __attribute__ ((packed));

struct iwl5000_calib_cfg_cmd {
	struct iwl_calib_cfg_status_s ucd_calib_cfg;
	struct iwl_calib_cfg_status_s drv_calib_cfg;
	__le32 reserved1;
} __attribute__ ((packed));

struct iwl5000_calib_hdr {
	u8 op_code;
	u8 first_group;
	u8 groups_num;
	u8 data_valid;
} __attribute__ ((packed));

struct iwl5000_calibration_chain_noise_reset_cmd {
	u8 op_code;	/* IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
	u8 flags;	/* not used */
@@ -2894,6 +2943,7 @@ struct iwl_rx_packet {
		struct iwl4965_notif_statistics stats;
		struct iwl4965_compressed_ba_resp compressed_ba;
		struct iwl4965_missed_beacon_notif missed_beacon;
		struct iwl5000_calibration calib;
		__le32 status;
		u8 raw[0];
	} u;
+16 −0
Original line number Diff line number Diff line
@@ -871,9 +871,25 @@ int iwl_init_drv(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_init_drv);

void iwl_free_calib_results(struct iwl_priv *priv)
{
	kfree(priv->calib_results.lo_res);
	priv->calib_results.lo_res = NULL;
	priv->calib_results.lo_res_len = 0;

	kfree(priv->calib_results.tx_iq_res);
	priv->calib_results.tx_iq_res = NULL;
	priv->calib_results.tx_iq_res_len = 0;

	kfree(priv->calib_results.tx_iq_perd_res);
	priv->calib_results.tx_iq_perd_res = NULL;
	priv->calib_results.tx_iq_perd_res_len = 0;
}
EXPORT_SYMBOL(iwl_free_calib_results);

void iwl_uninit_drv(struct iwl_priv *priv)
{
	iwl_free_calib_results(priv);
	iwlcore_free_geos(priv);
	iwl_free_channel_map(priv);
}
+1 −0
Original line number Diff line number Diff line
@@ -172,6 +172,7 @@ struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg,
void iwl_hw_detect(struct iwl_priv *priv);

void iwlcore_clear_stations_table(struct iwl_priv *priv);
void iwl_free_calib_results(struct iwl_priv *priv);
void iwl_reset_qos(struct iwl_priv *priv);
void iwl_set_rxon_chain(struct iwl_priv *priv);
int iwl_set_rxon_channel(struct iwl_priv *priv,
+12 −0
Original line number Diff line number Diff line
@@ -876,6 +876,15 @@ struct statistics_general_data {
	u32 beacon_energy_c;
};

struct iwl_calib_results {
	void *tx_iq_res;
	void *tx_iq_perd_res;
	void *lo_res;
	u32 tx_iq_res_len;
	u32 tx_iq_perd_res_len;
	u32 lo_res_len;
};

enum ucode_type {
	UCODE_NONE = 0,
	UCODE_INIT,
@@ -983,6 +992,9 @@ struct iwl_priv {
	s32 temperature;	/* degrees Kelvin */
	s32 last_temperature;

	/* init calibration results */
	struct iwl_calib_results calib_results;

	/* Scan related variables */
	unsigned long last_scan_jiffies;
	unsigned long next_scan_jiffies;
Loading