2010-03-17 01:23:29 +08:00
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* GPL LICENSE SUMMARY
|
|
|
|
*
|
2011-04-06 00:42:00 +08:00
|
|
|
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
|
2010-03-17 01:23:29 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of version 2 of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
|
|
|
|
* USA
|
|
|
|
*
|
|
|
|
* The full GNU General Public License is included in this distribution
|
|
|
|
* in the file called LICENSE.GPL.
|
|
|
|
*
|
|
|
|
* Contact Information:
|
|
|
|
* Intel Linux Wireless <ilw@linux.intel.com>
|
|
|
|
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
2010-03-17 01:23:30 +08:00
|
|
|
#include <linux/sched.h>
|
2011-11-10 22:55:10 +08:00
|
|
|
#include <linux/dma-mapping.h>
|
2010-03-17 01:23:29 +08:00
|
|
|
|
|
|
|
#include "iwl-dev.h"
|
|
|
|
#include "iwl-core.h"
|
2010-03-17 01:23:30 +08:00
|
|
|
#include "iwl-io.h"
|
2010-03-17 08:41:23 +08:00
|
|
|
#include "iwl-agn-hw.h"
|
2010-03-17 03:37:24 +08:00
|
|
|
#include "iwl-agn.h"
|
2010-09-23 00:02:11 +08:00
|
|
|
#include "iwl-agn-calib.h"
|
2011-07-08 23:46:16 +08:00
|
|
|
#include "iwl-trans.h"
|
2011-08-26 14:11:11 +08:00
|
|
|
#include "iwl-fh.h"
|
2010-03-17 03:37:24 +08:00
|
|
|
|
2010-04-28 05:10:00 +08:00
|
|
|
static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = {
|
|
|
|
{COEX_CU_UNASSOC_IDLE_RP, COEX_CU_UNASSOC_IDLE_WP,
|
|
|
|
0, COEX_UNASSOC_IDLE_FLAGS},
|
|
|
|
{COEX_CU_UNASSOC_MANUAL_SCAN_RP, COEX_CU_UNASSOC_MANUAL_SCAN_WP,
|
|
|
|
0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
|
|
|
|
{COEX_CU_UNASSOC_AUTO_SCAN_RP, COEX_CU_UNASSOC_AUTO_SCAN_WP,
|
|
|
|
0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
|
|
|
|
{COEX_CU_CALIBRATION_RP, COEX_CU_CALIBRATION_WP,
|
|
|
|
0, COEX_CALIBRATION_FLAGS},
|
|
|
|
{COEX_CU_PERIODIC_CALIBRATION_RP, COEX_CU_PERIODIC_CALIBRATION_WP,
|
|
|
|
0, COEX_PERIODIC_CALIBRATION_FLAGS},
|
|
|
|
{COEX_CU_CONNECTION_ESTAB_RP, COEX_CU_CONNECTION_ESTAB_WP,
|
|
|
|
0, COEX_CONNECTION_ESTAB_FLAGS},
|
|
|
|
{COEX_CU_ASSOCIATED_IDLE_RP, COEX_CU_ASSOCIATED_IDLE_WP,
|
|
|
|
0, COEX_ASSOCIATED_IDLE_FLAGS},
|
|
|
|
{COEX_CU_ASSOC_MANUAL_SCAN_RP, COEX_CU_ASSOC_MANUAL_SCAN_WP,
|
|
|
|
0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
|
|
|
|
{COEX_CU_ASSOC_AUTO_SCAN_RP, COEX_CU_ASSOC_AUTO_SCAN_WP,
|
|
|
|
0, COEX_ASSOC_AUTO_SCAN_FLAGS},
|
|
|
|
{COEX_CU_ASSOC_ACTIVE_LEVEL_RP, COEX_CU_ASSOC_ACTIVE_LEVEL_WP,
|
|
|
|
0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
|
|
|
|
{COEX_CU_RF_ON_RP, COEX_CU_RF_ON_WP, 0, COEX_CU_RF_ON_FLAGS},
|
|
|
|
{COEX_CU_RF_OFF_RP, COEX_CU_RF_OFF_WP, 0, COEX_RF_OFF_FLAGS},
|
|
|
|
{COEX_CU_STAND_ALONE_DEBUG_RP, COEX_CU_STAND_ALONE_DEBUG_WP,
|
|
|
|
0, COEX_STAND_ALONE_DEBUG_FLAGS},
|
|
|
|
{COEX_CU_IPAN_ASSOC_LEVEL_RP, COEX_CU_IPAN_ASSOC_LEVEL_WP,
|
|
|
|
0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
|
|
|
|
{COEX_CU_RSRVD1_RP, COEX_CU_RSRVD1_WP, 0, COEX_RSRVD1_FLAGS},
|
|
|
|
{COEX_CU_RSRVD2_RP, COEX_CU_RSRVD2_WP, 0, COEX_RSRVD2_FLAGS}
|
|
|
|
};
|
|
|
|
|
2011-11-10 22:55:10 +08:00
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* uCode download functions
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
static void iwl_free_fw_desc(struct iwl_bus *bus, struct fw_desc *desc)
|
|
|
|
{
|
|
|
|
if (desc->v_addr)
|
|
|
|
dma_free_coherent(bus->dev, desc->len,
|
|
|
|
desc->v_addr, desc->p_addr);
|
|
|
|
desc->v_addr = NULL;
|
|
|
|
desc->len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iwl_free_fw_img(struct iwl_bus *bus, struct fw_img *img)
|
|
|
|
{
|
|
|
|
iwl_free_fw_desc(bus, &img->code);
|
|
|
|
iwl_free_fw_desc(bus, &img->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void iwl_dealloc_ucode(struct iwl_trans *trans)
|
|
|
|
{
|
|
|
|
iwl_free_fw_img(bus(trans), &trans->ucode_rt);
|
|
|
|
iwl_free_fw_img(bus(trans), &trans->ucode_init);
|
|
|
|
iwl_free_fw_img(bus(trans), &trans->ucode_wowlan);
|
|
|
|
}
|
|
|
|
|
|
|
|
int iwl_alloc_fw_desc(struct iwl_bus *bus, struct fw_desc *desc,
|
|
|
|
const void *data, size_t len)
|
|
|
|
{
|
|
|
|
if (!len) {
|
|
|
|
desc->v_addr = NULL;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
desc->v_addr = dma_alloc_coherent(bus->dev, len,
|
|
|
|
&desc->p_addr, GFP_KERNEL);
|
|
|
|
if (!desc->v_addr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
desc->len = len;
|
|
|
|
memcpy(desc->v_addr, data, len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-17 01:23:30 +08:00
|
|
|
/*
|
|
|
|
* ucode
|
|
|
|
*/
|
2011-12-03 00:48:38 +08:00
|
|
|
static int iwl_load_section(struct iwl_trans *trans, const char *name,
|
2010-03-17 01:23:30 +08:00
|
|
|
struct fw_desc *image, u32 dst_addr)
|
|
|
|
{
|
2011-11-10 22:55:07 +08:00
|
|
|
struct iwl_bus *bus = bus(trans);
|
2010-03-17 01:23:30 +08:00
|
|
|
dma_addr_t phy_addr = image->p_addr;
|
|
|
|
u32 byte_cnt = image->len;
|
|
|
|
int ret;
|
|
|
|
|
2011-11-10 22:55:07 +08:00
|
|
|
trans->ucode_write_complete = 0;
|
2010-03-17 01:23:30 +08:00
|
|
|
|
2011-11-10 22:55:07 +08:00
|
|
|
iwl_write_direct32(bus,
|
2010-03-17 01:23:30 +08:00
|
|
|
FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
|
|
|
|
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
|
|
|
|
|
2011-11-10 22:55:07 +08:00
|
|
|
iwl_write_direct32(bus,
|
2010-03-17 01:23:30 +08:00
|
|
|
FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), dst_addr);
|
|
|
|
|
2011-11-10 22:55:07 +08:00
|
|
|
iwl_write_direct32(bus,
|
2010-03-17 01:23:30 +08:00
|
|
|
FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
|
|
|
|
phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
|
|
|
|
|
2011-11-10 22:55:07 +08:00
|
|
|
iwl_write_direct32(bus,
|
2010-03-17 01:23:30 +08:00
|
|
|
FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
|
|
|
|
(iwl_get_dma_hi_addr(phy_addr)
|
|
|
|
<< FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
|
|
|
|
|
2011-11-10 22:55:07 +08:00
|
|
|
iwl_write_direct32(bus,
|
2010-03-17 01:23:30 +08:00
|
|
|
FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL),
|
|
|
|
1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
|
|
|
|
1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
|
|
|
|
FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
|
|
|
|
|
2011-11-10 22:55:07 +08:00
|
|
|
iwl_write_direct32(bus,
|
2010-03-17 01:23:30 +08:00
|
|
|
FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
|
|
|
|
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
|
|
|
|
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
|
|
|
|
FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
|
|
|
|
|
2011-11-10 22:55:07 +08:00
|
|
|
IWL_DEBUG_FW(bus, "%s uCode section being loaded...\n", name);
|
|
|
|
ret = wait_event_timeout(trans->shrd->wait_command_queue,
|
|
|
|
trans->ucode_write_complete, 5 * HZ);
|
2010-03-17 01:23:30 +08:00
|
|
|
if (!ret) {
|
2011-11-10 22:55:07 +08:00
|
|
|
IWL_ERR(trans, "Could not load the %s uCode section\n",
|
2010-03-17 01:23:30 +08:00
|
|
|
name);
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-10 22:55:10 +08:00
|
|
|
static inline struct fw_img *iwl_get_ucode_image(struct iwl_trans *trans,
|
|
|
|
enum iwl_ucode_type ucode_type)
|
2011-11-10 22:55:08 +08:00
|
|
|
{
|
|
|
|
switch (ucode_type) {
|
|
|
|
case IWL_UCODE_INIT:
|
2011-11-10 22:55:10 +08:00
|
|
|
return &trans->ucode_init;
|
2011-11-10 22:55:08 +08:00
|
|
|
case IWL_UCODE_WOWLAN:
|
2011-11-10 22:55:10 +08:00
|
|
|
return &trans->ucode_wowlan;
|
2011-11-10 22:55:08 +08:00
|
|
|
case IWL_UCODE_REGULAR:
|
2011-11-10 22:55:10 +08:00
|
|
|
return &trans->ucode_rt;
|
2011-11-10 22:55:08 +08:00
|
|
|
case IWL_UCODE_NONE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
static int iwl_load_given_ucode(struct iwl_trans *trans,
|
2011-11-10 22:55:10 +08:00
|
|
|
enum iwl_ucode_type ucode_type)
|
2010-03-17 01:23:30 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2011-11-10 22:55:10 +08:00
|
|
|
struct fw_img *image = iwl_get_ucode_image(trans, ucode_type);
|
2011-11-10 22:55:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
if (!image) {
|
2011-11-10 22:55:10 +08:00
|
|
|
IWL_ERR(trans, "Invalid ucode requested (%d)\n",
|
|
|
|
ucode_type);
|
2011-11-10 22:55:09 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-03-17 01:23:30 +08:00
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
ret = iwl_load_section(trans, "INST", &image->code,
|
2010-03-17 08:41:23 +08:00
|
|
|
IWLAGN_RTC_INST_LOWER_BOUND);
|
2010-03-17 01:23:30 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
return iwl_load_section(trans, "DATA", &image->data,
|
2010-03-17 08:41:23 +08:00
|
|
|
IWLAGN_RTC_DATA_LOWER_BOUND);
|
2010-03-17 01:23:30 +08:00
|
|
|
}
|
|
|
|
|
2010-03-17 03:37:24 +08:00
|
|
|
/*
|
|
|
|
* Calibration
|
|
|
|
*/
|
2011-12-03 00:48:38 +08:00
|
|
|
static int iwl_set_Xtal_calib(struct iwl_priv *priv)
|
2010-03-17 03:37:24 +08:00
|
|
|
{
|
|
|
|
struct iwl_calib_xtal_freq_cmd cmd;
|
|
|
|
__le16 *xtal_calib =
|
2010-04-07 12:10:33 +08:00
|
|
|
(__le16 *)iwl_eeprom_query_addr(priv, EEPROM_XTAL);
|
2010-03-17 03:37:24 +08:00
|
|
|
|
2011-06-07 05:26:43 +08:00
|
|
|
iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD);
|
2010-03-17 03:37:24 +08:00
|
|
|
cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]);
|
|
|
|
cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]);
|
2011-11-26 03:11:44 +08:00
|
|
|
return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
|
2010-03-17 03:37:24 +08:00
|
|
|
}
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
static int iwl_set_temperature_offset_calib(struct iwl_priv *priv)
|
2010-09-22 07:54:01 +08:00
|
|
|
{
|
|
|
|
struct iwl_calib_temperature_offset_cmd cmd;
|
|
|
|
__le16 *offset_calib =
|
2011-09-16 02:46:50 +08:00
|
|
|
(__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE);
|
2011-06-07 05:26:43 +08:00
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
|
|
iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
|
2011-09-13 03:08:25 +08:00
|
|
|
memcpy(&cmd.radio_sensor_offset, offset_calib, sizeof(*offset_calib));
|
2010-09-22 07:54:01 +08:00
|
|
|
if (!(cmd.radio_sensor_offset))
|
|
|
|
cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET;
|
2011-06-07 05:26:43 +08:00
|
|
|
|
2010-09-22 07:54:01 +08:00
|
|
|
IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n",
|
2011-07-09 05:29:48 +08:00
|
|
|
le16_to_cpu(cmd.radio_sensor_offset));
|
2011-11-26 03:11:44 +08:00
|
|
|
return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
|
2010-09-22 07:54:01 +08:00
|
|
|
}
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv)
|
2011-09-16 02:46:50 +08:00
|
|
|
{
|
|
|
|
struct iwl_calib_temperature_offset_v2_cmd cmd;
|
|
|
|
__le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(priv,
|
|
|
|
EEPROM_KELVIN_TEMPERATURE);
|
|
|
|
__le16 *offset_calib_low =
|
|
|
|
(__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE);
|
2011-09-16 02:46:51 +08:00
|
|
|
struct iwl_eeprom_calib_hdr *hdr;
|
2011-09-16 02:46:50 +08:00
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
|
|
iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
|
2011-09-16 02:46:51 +08:00
|
|
|
hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv,
|
|
|
|
EEPROM_CALIB_ALL);
|
2011-09-16 02:46:50 +08:00
|
|
|
memcpy(&cmd.radio_sensor_offset_high, offset_calib_high,
|
2011-09-16 02:46:53 +08:00
|
|
|
sizeof(*offset_calib_high));
|
2011-09-16 02:46:50 +08:00
|
|
|
memcpy(&cmd.radio_sensor_offset_low, offset_calib_low,
|
2011-09-16 02:46:53 +08:00
|
|
|
sizeof(*offset_calib_low));
|
2011-09-16 02:46:50 +08:00
|
|
|
if (!(cmd.radio_sensor_offset_low)) {
|
|
|
|
IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n");
|
|
|
|
cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET;
|
|
|
|
cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET;
|
|
|
|
}
|
2011-09-16 02:46:51 +08:00
|
|
|
memcpy(&cmd.burntVoltageRef, &hdr->voltage,
|
|
|
|
sizeof(hdr->voltage));
|
2011-09-16 02:46:50 +08:00
|
|
|
|
|
|
|
IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n",
|
|
|
|
le16_to_cpu(cmd.radio_sensor_offset_high));
|
|
|
|
IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n",
|
|
|
|
le16_to_cpu(cmd.radio_sensor_offset_low));
|
|
|
|
IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n",
|
|
|
|
le16_to_cpu(cmd.burntVoltageRef));
|
|
|
|
|
2011-11-26 03:11:44 +08:00
|
|
|
return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
|
2011-09-16 02:46:50 +08:00
|
|
|
}
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
static int iwl_send_calib_cfg(struct iwl_trans *trans)
|
2010-03-17 03:37:24 +08:00
|
|
|
{
|
|
|
|
struct iwl_calib_cfg_cmd calib_cfg_cmd;
|
|
|
|
struct iwl_host_cmd cmd = {
|
|
|
|
.id = CALIBRATION_CFG_CMD,
|
2011-05-04 22:50:38 +08:00
|
|
|
.len = { sizeof(struct iwl_calib_cfg_cmd), },
|
|
|
|
.data = { &calib_cfg_cmd, },
|
2010-03-17 03:37:24 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
2011-07-09 05:29:45 +08:00
|
|
|
calib_cfg_cmd.ucd_calib_cfg.flags =
|
|
|
|
IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK;
|
2010-03-17 03:37:24 +08:00
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
return iwl_trans_send_cmd(trans, &cmd);
|
2010-03-17 03:37:24 +08:00
|
|
|
}
|
|
|
|
|
2011-09-21 06:37:23 +08:00
|
|
|
int iwlagn_rx_calib_result(struct iwl_priv *priv,
|
|
|
|
struct iwl_rx_mem_buffer *rxb,
|
|
|
|
struct iwl_device_cmd *cmd)
|
2010-03-17 03:37:24 +08:00
|
|
|
{
|
|
|
|
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
|
|
|
struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw;
|
|
|
|
int len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
|
|
|
|
|
|
|
|
/* reduce the size of the length field itself */
|
|
|
|
len -= 4;
|
|
|
|
|
2011-11-26 03:11:44 +08:00
|
|
|
if (iwl_calib_set(priv, hdr, len))
|
|
|
|
IWL_ERR(priv, "Failed to record calibration data %d\n",
|
|
|
|
hdr->op_code);
|
|
|
|
|
2011-09-21 06:37:23 +08:00
|
|
|
return 0;
|
2010-03-17 03:37:24 +08:00
|
|
|
}
|
|
|
|
|
iwlwifi: support the svtool messages interactions through nl80211 test mode
This patch adds the feature to support the test mode operation through
the generic netlink channel NL80211_CMD_TESTMODE between intel
wireless device iwlwifi and the user space application svtool.
The main purpose is to create a transportation layer between the iwlwifi
device and the user space application so that the interaction between the
user space application svtool and the iwlwifi device in the kernel space is
in a way of generic netlink messaging.
The detail specific functions are:
1. The function iwl_testmode_cmd() is added to digest the svtool test command
from the user space application. The svtool test commands are categorized to
three types : commands to be processed by the device ucode, commands to access
the registers, and commands to be processed at the driver level(such as reload
the ucode). iwl_testmode_cmd() dispatches the commands the corresponding handlers
and reply to user space regarding the command execution status. Extra data is
returned to the user space application if there's any.
2. The function iwl_testmode_ucode_rx_pkt() is added to multicast all the spontaneous
messages from the iwlwifi device to the user space. Regardless the message types,
whenever there is a valid spontaneous message received by the iwlwifi ISR,
iwl_testmode_ucode_rx_pkt() is invoked to multicast the message content to user
space. The message content is not attacked and the message parsing is left to
the user space application.
Implementation guidelines:
1. The generic netlink messaging for iwliwif test mode is through NL80211_CMD_TESTMODE
channel, therefore, the codes need to follow the regulations set by cfg80211.ko
to get the actual device instance ieee80211_ops via cfg80211.ko, so that the iwlwifi
device is indicated with ieee80211_ops and can be actually accessed.
Therefore, a callback iwl_testmode_cmd() is added to the structure
iwlagn_hw_ops in iwl-agn.c.
2. It intends to utilize those low level device access APIs from iwlwifi device driver
(ie. iwlagn.ko) rather than creating it's own set of device access functions.
For example, iwl_send_cmd(), iwl_read32(), iwl_write8(), and iwl_write32() are reused.
3. The main functions are maintained in new files instead of spreading all over the
existing iwlwifi driver files.
The new files added are :
drivers/net/wireless/iwlwifi/iwl-sv-open.c
- to handle the user space test mode application command
and reply the respective command status to the user space application.
- to multicast the spontaneous messages from device to user space.
drivers/net/wireless/iwlwifi/iwl-testmode.h
- the commonly referenced definitions for the TLVs used in
the generic netlink messages
Signed-off-by: Cindy H. Kao <cindy.h.kao@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
2011-05-07 01:40:15 +08:00
|
|
|
int iwlagn_init_alive_start(struct iwl_priv *priv)
|
2010-03-17 03:37:24 +08:00
|
|
|
{
|
2011-04-23 01:15:23 +08:00
|
|
|
int ret;
|
2010-03-17 03:37:24 +08:00
|
|
|
|
2010-10-06 23:10:00 +08:00
|
|
|
if (priv->cfg->bt_params &&
|
|
|
|
priv->cfg->bt_params->advanced_bt_coexist) {
|
2010-08-24 06:24:49 +08:00
|
|
|
/*
|
|
|
|
* Tell uCode we are ready to perform calibration
|
|
|
|
* need to perform this before any calibration
|
|
|
|
* no need to close the envlope since we are going
|
|
|
|
* to load the runtime uCode later.
|
|
|
|
*/
|
2011-12-03 00:48:38 +08:00
|
|
|
ret = iwl_send_bt_env(trans(priv), IWL_BT_COEX_ENV_OPEN,
|
2010-08-24 06:24:49 +08:00
|
|
|
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
|
2011-04-23 01:15:23 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2010-08-24 06:24:49 +08:00
|
|
|
|
|
|
|
}
|
2011-04-23 01:15:23 +08:00
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
ret = iwl_send_calib_cfg(trans(priv));
|
2011-04-23 01:15:23 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2010-09-22 07:54:01 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* temperature offset calibration is only needed for runtime ucode,
|
|
|
|
* so prepare the value now.
|
|
|
|
*/
|
2011-09-16 02:46:50 +08:00
|
|
|
if (priv->cfg->need_temp_offset_calib) {
|
|
|
|
if (priv->cfg->temp_offset_v2)
|
2011-12-03 00:48:38 +08:00
|
|
|
return iwl_set_temperature_offset_calib_v2(priv);
|
2011-09-16 02:46:50 +08:00
|
|
|
else
|
2011-12-03 00:48:38 +08:00
|
|
|
return iwl_set_temperature_offset_calib(priv);
|
2011-09-16 02:46:50 +08:00
|
|
|
}
|
2010-03-17 03:37:24 +08:00
|
|
|
|
2011-04-23 01:15:23 +08:00
|
|
|
return 0;
|
2010-03-17 03:37:24 +08:00
|
|
|
}
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
static int iwl_send_wimax_coex(struct iwl_priv *priv)
|
2010-04-28 05:10:00 +08:00
|
|
|
{
|
|
|
|
struct iwl_wimax_coex_cmd coex_cmd;
|
|
|
|
|
2010-10-06 23:10:00 +08:00
|
|
|
if (priv->cfg->base_params->support_wimax_coexist) {
|
2010-04-28 05:10:00 +08:00
|
|
|
/* UnMask wake up src at associated sleep */
|
|
|
|
coex_cmd.flags = COEX_FLAGS_ASSOC_WA_UNMASK_MSK;
|
|
|
|
|
|
|
|
/* UnMask wake up src at unassociated sleep */
|
|
|
|
coex_cmd.flags |= COEX_FLAGS_UNASSOC_WA_UNMASK_MSK;
|
|
|
|
memcpy(coex_cmd.sta_prio, cu_priorities,
|
|
|
|
sizeof(struct iwl_wimax_coex_event_entry) *
|
|
|
|
COEX_NUM_OF_EVENTS);
|
|
|
|
|
|
|
|
/* enabling the coexistence feature */
|
|
|
|
coex_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK;
|
|
|
|
|
|
|
|
/* enabling the priorities tables */
|
|
|
|
coex_cmd.flags |= COEX_FLAGS_STA_TABLE_VALID_MSK;
|
|
|
|
} else {
|
|
|
|
/* coexistence is disabled */
|
|
|
|
memset(&coex_cmd, 0, sizeof(coex_cmd));
|
|
|
|
}
|
iwlagn: bus layer chooses its transport layer
Remove iwl_transport_register which was a W/A. The bus layer knows what
transport to use. So now, the bus layer gives the upper layer a pointer to the
iwl_trans_ops struct that it wants to use. The upper layer then, allocates the
desired transport layer using iwl_trans_ops->alloc function.
As a result of this, priv->trans, no longer exists, priv holds a pointer to
iwl_shared, which holds a pointer to iwl_trans. This required to change all the
calls to the transport layer from upper layer. While we were at it, trans_X
inlines have been renamed to iwl_trans_X to avoid confusions, which of course
required to rename the functions inside the transport layer because of
conflicts in names. So the static API functions inside the transport layer
implementation have been renamed to iwl_trans_pcie_X.
Until now, the IRQ / Tasklet were initialized in iwl_transport_layer. This is
confusing since the registration doesn't mean to request IRQ, so I added a
handler for that.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2011-08-26 14:10:48 +08:00
|
|
|
return iwl_trans_send_cmd_pdu(trans(priv),
|
2011-07-08 23:46:14 +08:00
|
|
|
COEX_PRIORITY_TABLE_CMD, CMD_SYNC,
|
2010-04-28 05:10:00 +08:00
|
|
|
sizeof(coex_cmd), &coex_cmd);
|
|
|
|
}
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
|
2010-08-23 22:57:05 +08:00
|
|
|
((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
|
|
|
|
(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
|
|
|
|
0, 0, 0, 0, 0, 0, 0
|
|
|
|
};
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
void iwl_send_prio_tbl(struct iwl_trans *trans)
|
2010-08-23 22:57:05 +08:00
|
|
|
{
|
|
|
|
struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd;
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl,
|
|
|
|
sizeof(iwl_bt_prio_tbl));
|
|
|
|
if (iwl_trans_send_cmd_pdu(trans,
|
2011-07-08 23:46:14 +08:00
|
|
|
REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC,
|
2010-08-23 22:57:05 +08:00
|
|
|
sizeof(prio_tbl_cmd), &prio_tbl_cmd))
|
2011-12-03 00:48:38 +08:00
|
|
|
IWL_ERR(trans, "failed to send BT prio tbl command\n");
|
2010-08-23 22:57:05 +08:00
|
|
|
}
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
int iwl_send_bt_env(struct iwl_trans *trans, u8 action, u8 type)
|
2010-08-23 22:57:05 +08:00
|
|
|
{
|
|
|
|
struct iwl_bt_coex_prot_env_cmd env_cmd;
|
2011-04-23 01:15:23 +08:00
|
|
|
int ret;
|
2010-08-23 22:57:05 +08:00
|
|
|
|
|
|
|
env_cmd.action = action;
|
|
|
|
env_cmd.type = type;
|
2011-12-03 00:48:38 +08:00
|
|
|
ret = iwl_trans_send_cmd_pdu(trans,
|
2011-07-08 23:46:14 +08:00
|
|
|
REPLY_BT_COEX_PROT_ENV, CMD_SYNC,
|
2011-04-23 01:15:23 +08:00
|
|
|
sizeof(env_cmd), &env_cmd);
|
|
|
|
if (ret)
|
2011-12-03 00:48:38 +08:00
|
|
|
IWL_ERR(trans, "failed to send BT env command\n");
|
2011-04-23 01:15:23 +08:00
|
|
|
return ret;
|
2010-08-23 22:57:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
static int iwl_alive_notify(struct iwl_priv *priv)
|
2010-03-17 03:37:24 +08:00
|
|
|
{
|
2011-09-07 00:31:21 +08:00
|
|
|
struct iwl_rxon_context *ctx;
|
2011-04-06 00:42:01 +08:00
|
|
|
int ret;
|
2010-03-17 03:37:24 +08:00
|
|
|
|
2011-08-26 14:11:23 +08:00
|
|
|
if (!priv->tx_cmd_pool)
|
|
|
|
priv->tx_cmd_pool =
|
2011-12-03 00:48:38 +08:00
|
|
|
kmem_cache_create("iwl_dev_cmd",
|
2011-08-26 14:11:23 +08:00
|
|
|
sizeof(struct iwl_device_cmd),
|
|
|
|
sizeof(void *), 0, NULL);
|
|
|
|
|
|
|
|
if (!priv->tx_cmd_pool)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
iwlagn: bus layer chooses its transport layer
Remove iwl_transport_register which was a W/A. The bus layer knows what
transport to use. So now, the bus layer gives the upper layer a pointer to the
iwl_trans_ops struct that it wants to use. The upper layer then, allocates the
desired transport layer using iwl_trans_ops->alloc function.
As a result of this, priv->trans, no longer exists, priv holds a pointer to
iwl_shared, which holds a pointer to iwl_trans. This required to change all the
calls to the transport layer from upper layer. While we were at it, trans_X
inlines have been renamed to iwl_trans_X to avoid confusions, which of course
required to rename the functions inside the transport layer because of
conflicts in names. So the static API functions inside the transport layer
implementation have been renamed to iwl_trans_pcie_X.
Until now, the IRQ / Tasklet were initialized in iwl_transport_layer. This is
confusing since the registration doesn't mean to request IRQ, so I added a
handler for that.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2011-08-26 14:10:48 +08:00
|
|
|
iwl_trans_tx_start(trans(priv));
|
2011-09-07 00:31:21 +08:00
|
|
|
for_each_context(priv, ctx)
|
|
|
|
ctx->last_tx_rejected = false;
|
2010-11-18 19:47:38 +08:00
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
ret = iwl_send_wimax_coex(priv);
|
2011-04-06 00:42:01 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2011-11-26 03:11:43 +08:00
|
|
|
if (!priv->cfg->no_xtal_calib) {
|
2011-12-03 00:48:38 +08:00
|
|
|
ret = iwl_set_Xtal_calib(priv);
|
2011-11-26 03:11:43 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2010-03-17 03:37:24 +08:00
|
|
|
|
2011-04-06 00:41:54 +08:00
|
|
|
return iwl_send_calib_results(priv);
|
2010-03-17 03:37:24 +08:00
|
|
|
}
|
2010-05-11 05:15:25 +08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* iwl_verify_inst_sparse - verify runtime uCode image in card vs. host,
|
|
|
|
* using sample data 100 bytes apart. If these sample points are good,
|
|
|
|
* it's a pretty good bet that everything between them is good, too.
|
|
|
|
*/
|
2011-11-10 22:55:09 +08:00
|
|
|
static int iwl_verify_inst_sparse(struct iwl_bus *bus,
|
2011-04-06 00:41:56 +08:00
|
|
|
struct fw_desc *fw_desc)
|
2010-05-11 05:15:25 +08:00
|
|
|
{
|
2011-04-06 00:41:56 +08:00
|
|
|
__le32 *image = (__le32 *)fw_desc->v_addr;
|
|
|
|
u32 len = fw_desc->len;
|
2010-05-11 05:15:25 +08:00
|
|
|
u32 val;
|
|
|
|
u32 i;
|
|
|
|
|
2011-11-10 22:55:09 +08:00
|
|
|
IWL_DEBUG_FW(bus, "ucode inst image size is %u\n", len);
|
2010-05-11 05:15:25 +08:00
|
|
|
|
|
|
|
for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
|
|
|
|
/* read data comes through single port, auto-incr addr */
|
|
|
|
/* NOTE: Use the debugless read so we don't flood kernel log
|
|
|
|
* if IWL_DL_IO is set */
|
2011-11-10 22:55:09 +08:00
|
|
|
iwl_write_direct32(bus, HBUS_TARG_MEM_RADDR,
|
2010-05-11 05:15:25 +08:00
|
|
|
i + IWLAGN_RTC_INST_LOWER_BOUND);
|
2011-11-10 22:55:09 +08:00
|
|
|
val = iwl_read32(bus, HBUS_TARG_MEM_RDAT);
|
2011-04-06 00:41:55 +08:00
|
|
|
if (val != le32_to_cpu(*image))
|
|
|
|
return -EIO;
|
2010-05-11 05:15:25 +08:00
|
|
|
}
|
|
|
|
|
2011-04-06 00:41:55 +08:00
|
|
|
return 0;
|
2010-05-11 05:15:25 +08:00
|
|
|
}
|
|
|
|
|
2011-11-10 22:55:09 +08:00
|
|
|
static void iwl_print_mismatch_inst(struct iwl_bus *bus,
|
2011-04-06 00:41:56 +08:00
|
|
|
struct fw_desc *fw_desc)
|
2010-05-11 05:15:25 +08:00
|
|
|
{
|
2011-04-06 00:41:56 +08:00
|
|
|
__le32 *image = (__le32 *)fw_desc->v_addr;
|
|
|
|
u32 len = fw_desc->len;
|
2010-05-11 05:15:25 +08:00
|
|
|
u32 val;
|
2011-04-06 00:41:55 +08:00
|
|
|
u32 offs;
|
|
|
|
int errors = 0;
|
2010-05-11 05:15:25 +08:00
|
|
|
|
2011-11-10 22:55:09 +08:00
|
|
|
IWL_DEBUG_FW(bus, "ucode inst image size is %u\n", len);
|
2010-05-11 05:15:25 +08:00
|
|
|
|
2011-11-10 22:55:09 +08:00
|
|
|
iwl_write_direct32(bus, HBUS_TARG_MEM_RADDR,
|
2010-05-11 05:15:25 +08:00
|
|
|
IWLAGN_RTC_INST_LOWER_BOUND);
|
|
|
|
|
2011-04-06 00:41:55 +08:00
|
|
|
for (offs = 0;
|
|
|
|
offs < len && errors < 20;
|
|
|
|
offs += sizeof(u32), image++) {
|
2010-05-11 05:15:25 +08:00
|
|
|
/* read data comes through single port, auto-incr addr */
|
2011-11-10 22:55:09 +08:00
|
|
|
val = iwl_read32(bus, HBUS_TARG_MEM_RDAT);
|
2010-05-11 05:15:25 +08:00
|
|
|
if (val != le32_to_cpu(*image)) {
|
2011-11-10 22:55:09 +08:00
|
|
|
IWL_ERR(bus, "uCode INST section at "
|
2011-04-06 00:41:55 +08:00
|
|
|
"offset 0x%x, is 0x%x, s/b 0x%x\n",
|
|
|
|
offs, val, le32_to_cpu(*image));
|
|
|
|
errors++;
|
2010-05-11 05:15:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* iwl_verify_ucode - determine which instruction image is in SRAM,
|
|
|
|
* and verify its contents
|
|
|
|
*/
|
2011-11-10 22:55:10 +08:00
|
|
|
static int iwl_verify_ucode(struct iwl_trans *trans,
|
|
|
|
enum iwl_ucode_type ucode_type)
|
2010-05-11 05:15:25 +08:00
|
|
|
{
|
2011-11-10 22:55:10 +08:00
|
|
|
struct fw_img *img = iwl_get_ucode_image(trans, ucode_type);
|
2011-11-10 22:55:09 +08:00
|
|
|
|
|
|
|
if (!img) {
|
2011-11-10 22:55:10 +08:00
|
|
|
IWL_ERR(trans, "Invalid ucode requested (%d)\n", ucode_type);
|
2011-11-10 22:55:09 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-11-10 22:55:10 +08:00
|
|
|
if (!iwl_verify_inst_sparse(bus(trans), &img->code)) {
|
|
|
|
IWL_DEBUG_FW(trans, "uCode is good in inst SRAM\n");
|
2010-05-11 05:15:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-10 22:55:10 +08:00
|
|
|
IWL_ERR(trans, "UCODE IMAGE IN INSTRUCTION SRAM NOT VALID!!\n");
|
2011-04-06 00:41:55 +08:00
|
|
|
|
2011-11-10 22:55:10 +08:00
|
|
|
iwl_print_mismatch_inst(bus(trans), &img->code);
|
2011-04-06 00:41:55 +08:00
|
|
|
return -EIO;
|
2010-05-11 05:15:25 +08:00
|
|
|
}
|
2011-04-23 01:15:23 +08:00
|
|
|
|
|
|
|
struct iwlagn_alive_data {
|
|
|
|
bool valid;
|
|
|
|
u8 subtype;
|
|
|
|
};
|
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
static void iwl_alive_fn(struct iwl_priv *priv,
|
2011-04-23 01:15:23 +08:00
|
|
|
struct iwl_rx_packet *pkt,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct iwlagn_alive_data *alive_data = data;
|
|
|
|
struct iwl_alive_resp *palive;
|
|
|
|
|
|
|
|
palive = &pkt->u.alive_frame;
|
|
|
|
|
2011-06-07 05:26:38 +08:00
|
|
|
IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision "
|
2011-04-23 01:15:23 +08:00
|
|
|
"0x%01X 0x%01X\n",
|
|
|
|
palive->is_valid, palive->ver_type,
|
|
|
|
palive->ver_subtype);
|
|
|
|
|
|
|
|
priv->device_pointers.error_event_table =
|
|
|
|
le32_to_cpu(palive->error_event_table_ptr);
|
|
|
|
priv->device_pointers.log_event_table =
|
|
|
|
le32_to_cpu(palive->log_event_table_ptr);
|
|
|
|
|
|
|
|
alive_data->subtype = palive->ver_subtype;
|
|
|
|
alive_data->valid = palive->is_valid == UCODE_VALID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define UCODE_ALIVE_TIMEOUT HZ
|
|
|
|
#define UCODE_CALIB_TIMEOUT (2*HZ)
|
|
|
|
|
|
|
|
int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
|
2011-11-10 22:55:10 +08:00
|
|
|
enum iwl_ucode_type ucode_type)
|
2011-04-23 01:15:23 +08:00
|
|
|
{
|
|
|
|
struct iwl_notification_wait alive_wait;
|
|
|
|
struct iwlagn_alive_data alive_data;
|
2011-12-03 00:48:39 +08:00
|
|
|
struct iwl_trans *trans = trans(priv);
|
2011-04-23 01:15:23 +08:00
|
|
|
int ret;
|
2011-11-10 22:55:10 +08:00
|
|
|
enum iwl_ucode_type old_type;
|
2011-04-23 01:15:23 +08:00
|
|
|
|
2011-12-03 00:48:39 +08:00
|
|
|
ret = iwl_trans_start_device(trans);
|
2011-04-23 01:15:23 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
iwlagn_init_notification_wait(priv, &alive_wait, REPLY_ALIVE,
|
2011-12-03 00:48:38 +08:00
|
|
|
iwl_alive_fn, &alive_data);
|
2011-04-23 01:15:23 +08:00
|
|
|
|
2011-12-03 00:48:39 +08:00
|
|
|
old_type = trans->shrd->ucode_type;
|
|
|
|
trans->shrd->ucode_type = ucode_type;
|
2011-04-23 01:15:23 +08:00
|
|
|
|
2011-12-03 00:48:39 +08:00
|
|
|
ret = iwl_load_given_ucode(trans, ucode_type);
|
2011-04-23 01:15:23 +08:00
|
|
|
if (ret) {
|
2011-12-03 00:48:39 +08:00
|
|
|
trans->shrd->ucode_type = old_type;
|
2011-04-23 01:15:23 +08:00
|
|
|
iwlagn_remove_notification(priv, &alive_wait);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-12-03 00:48:39 +08:00
|
|
|
iwl_trans_kick_nic(trans);
|
2011-04-23 01:15:23 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Some things may run in the background now, but we
|
|
|
|
* just wait for the ALIVE notification here.
|
|
|
|
*/
|
|
|
|
ret = iwlagn_wait_notification(priv, &alive_wait, UCODE_ALIVE_TIMEOUT);
|
|
|
|
if (ret) {
|
2011-12-03 00:48:39 +08:00
|
|
|
trans->shrd->ucode_type = old_type;
|
2011-04-23 01:15:23 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!alive_data.valid) {
|
|
|
|
IWL_ERR(priv, "Loaded ucode is not valid!\n");
|
2011-12-03 00:48:39 +08:00
|
|
|
trans->shrd->ucode_type = old_type;
|
2011-04-23 01:15:23 +08:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2011-07-16 04:23:45 +08:00
|
|
|
/*
|
|
|
|
* This step takes a long time (60-80ms!!) and
|
|
|
|
* WoWLAN image should be loaded quickly, so
|
|
|
|
* skip it for WoWLAN.
|
|
|
|
*/
|
|
|
|
if (ucode_type != IWL_UCODE_WOWLAN) {
|
2011-12-03 00:48:39 +08:00
|
|
|
ret = iwl_verify_ucode(trans, ucode_type);
|
2011-07-16 04:23:45 +08:00
|
|
|
if (ret) {
|
2011-12-03 00:48:39 +08:00
|
|
|
trans->shrd->ucode_type = old_type;
|
2011-07-16 04:23:45 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2011-04-23 01:15:23 +08:00
|
|
|
|
2011-07-16 04:23:45 +08:00
|
|
|
/* delay a bit to give rfkill time to run */
|
|
|
|
msleep(5);
|
|
|
|
}
|
2011-04-23 01:15:23 +08:00
|
|
|
|
2011-12-03 00:48:38 +08:00
|
|
|
ret = iwl_alive_notify(priv);
|
2011-04-23 01:15:23 +08:00
|
|
|
if (ret) {
|
|
|
|
IWL_WARN(priv,
|
|
|
|
"Could not complete ALIVE transition: %d\n", ret);
|
2011-12-03 00:48:39 +08:00
|
|
|
trans->shrd->ucode_type = old_type;
|
2011-04-23 01:15:23 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int iwlagn_run_init_ucode(struct iwl_priv *priv)
|
|
|
|
{
|
|
|
|
struct iwl_notification_wait calib_wait;
|
|
|
|
int ret;
|
|
|
|
|
2011-08-26 14:10:44 +08:00
|
|
|
lockdep_assert_held(&priv->shrd->mutex);
|
2011-04-23 01:15:23 +08:00
|
|
|
|
|
|
|
/* No init ucode required? Curious, but maybe ok */
|
2011-11-10 22:55:10 +08:00
|
|
|
if (!trans(priv)->ucode_init.code.len)
|
2011-04-23 01:15:23 +08:00
|
|
|
return 0;
|
|
|
|
|
2011-12-03 00:48:39 +08:00
|
|
|
if (priv->shrd->ucode_type != IWL_UCODE_NONE)
|
2011-04-23 01:15:23 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
iwlagn_init_notification_wait(priv, &calib_wait,
|
|
|
|
CALIBRATION_COMPLETE_NOTIFICATION,
|
|
|
|
NULL, NULL);
|
|
|
|
|
|
|
|
/* Will also start the device */
|
2011-11-10 22:55:08 +08:00
|
|
|
ret = iwlagn_load_ucode_wait_alive(priv, IWL_UCODE_INIT);
|
2011-04-23 01:15:23 +08:00
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
ret = iwlagn_init_alive_start(priv);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some things may run in the background now, but we
|
|
|
|
* just wait for the calibration complete notification.
|
|
|
|
*/
|
|
|
|
ret = iwlagn_wait_notification(priv, &calib_wait, UCODE_CALIB_TIMEOUT);
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
error:
|
|
|
|
iwlagn_remove_notification(priv, &calib_wait);
|
|
|
|
out:
|
|
|
|
/* Whatever happened, stop the device */
|
iwlagn: bus layer chooses its transport layer
Remove iwl_transport_register which was a W/A. The bus layer knows what
transport to use. So now, the bus layer gives the upper layer a pointer to the
iwl_trans_ops struct that it wants to use. The upper layer then, allocates the
desired transport layer using iwl_trans_ops->alloc function.
As a result of this, priv->trans, no longer exists, priv holds a pointer to
iwl_shared, which holds a pointer to iwl_trans. This required to change all the
calls to the transport layer from upper layer. While we were at it, trans_X
inlines have been renamed to iwl_trans_X to avoid confusions, which of course
required to rename the functions inside the transport layer because of
conflicts in names. So the static API functions inside the transport layer
implementation have been renamed to iwl_trans_pcie_X.
Until now, the IRQ / Tasklet were initialized in iwl_transport_layer. This is
confusing since the registration doesn't mean to request IRQ, so I added a
handler for that.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2011-08-26 14:10:48 +08:00
|
|
|
iwl_trans_stop_device(trans(priv));
|
2011-04-23 01:15:23 +08:00
|
|
|
return ret;
|
|
|
|
}
|