iwlwifi: move apm_init to start_hw
[cascardo/linux.git] / drivers / net / wireless / iwlwifi / iwl-agn.c
index b5c7c5f..3aeaa89 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -315,7 +315,7 @@ static void iwl_bg_statistics_periodic(unsigned long data)
 
 static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
                                        u32 start_idx, u32 num_events,
-                                       u32 mode)
+                                       u32 capacity, u32 mode)
 {
        u32 i;
        u32 ptr;        /* SRAM byte address of log data */
@@ -328,87 +328,125 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
                ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
 
        /* Make sure device is powered up for SRAM reads */
-       spin_lock_irqsave(&bus(priv)->reg_lock, reg_flags);
-       if (iwl_grab_nic_access(bus(priv))) {
-               spin_unlock_irqrestore(&bus(priv)->reg_lock, reg_flags);
+       spin_lock_irqsave(&trans(priv)->reg_lock, reg_flags);
+       if (iwl_grab_nic_access(trans(priv))) {
+               spin_unlock_irqrestore(&trans(priv)->reg_lock, reg_flags);
                return;
        }
 
        /* Set starting address; reads will auto-increment */
-       iwl_write32(bus(priv), HBUS_TARG_MEM_RADDR, ptr);
+       iwl_write32(trans(priv), HBUS_TARG_MEM_RADDR, ptr);
        rmb();
 
+       /*
+        * Refuse to read more than would have fit into the log from
+        * the current start_idx. This used to happen due to the race
+        * described below, but now WARN because the code below should
+        * prevent it from happening here.
+        */
+       if (WARN_ON(num_events > capacity - start_idx))
+               num_events = capacity - start_idx;
+
        /*
         * "time" is actually "data" for mode 0 (no timestamp).
         * place event id # at far right for easier visual parsing.
         */
        for (i = 0; i < num_events; i++) {
-               ev = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
-               time = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
+               ev = iwl_read32(trans(priv), HBUS_TARG_MEM_RDAT);
+               time = iwl_read32(trans(priv), HBUS_TARG_MEM_RDAT);
                if (mode == 0) {
-                       trace_iwlwifi_dev_ucode_cont_event(priv,
-                                                       0, time, ev);
+                       trace_iwlwifi_dev_ucode_cont_event(priv, 0, time, ev);
                } else {
-                       data = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
-                       trace_iwlwifi_dev_ucode_cont_event(priv,
-                                               time, data, ev);
+                       data = iwl_read32(trans(priv), HBUS_TARG_MEM_RDAT);
+                       trace_iwlwifi_dev_ucode_cont_event(priv, time,
+                                                          data, ev);
                }
        }
        /* Allow device to power down */
-       iwl_release_nic_access(bus(priv));
-       spin_unlock_irqrestore(&bus(priv)->reg_lock, reg_flags);
+       iwl_release_nic_access(trans(priv));
+       spin_unlock_irqrestore(&trans(priv)->reg_lock, reg_flags);
 }
 
 static void iwl_continuous_event_trace(struct iwl_priv *priv)
 {
        u32 capacity;   /* event log capacity in # entries */
+       struct {
+               u32 capacity;
+               u32 mode;
+               u32 wrap_counter;
+               u32 write_counter;
+       } __packed read;
        u32 base;       /* SRAM byte address of event log header */
        u32 mode;       /* 0 - no timestamp, 1 - timestamp recorded */
        u32 num_wraps;  /* # times uCode wrapped to top of log */
        u32 next_entry; /* index of next entry to be written by uCode */
 
-       base = priv->shrd->device_pointers.error_event_table;
+       base = priv->shrd->device_pointers.log_event_table;
        if (iwlagn_hw_valid_rtc_data_addr(base)) {
-               capacity = iwl_read_targ_mem(bus(priv), base);
-               num_wraps = iwl_read_targ_mem(bus(priv),
-                                               base + (2 * sizeof(u32)));
-               mode = iwl_read_targ_mem(bus(priv), base + (1 * sizeof(u32)));
-               next_entry = iwl_read_targ_mem(bus(priv),
-                                               base + (3 * sizeof(u32)));
+               iwl_read_targ_mem_words(trans(priv), base, &read, sizeof(read));
+
+               capacity = read.capacity;
+               mode = read.mode;
+               num_wraps = read.wrap_counter;
+               next_entry = read.write_counter;
        } else
                return;
 
+       /*
+        * Unfortunately, the uCode doesn't use temporary variables.
+        * Therefore, it can happen that we read next_entry == capacity,
+        * which really means next_entry == 0.
+        */
+       if (unlikely(next_entry == capacity))
+               next_entry = 0;
+       /*
+        * Additionally, the uCode increases the write pointer before
+        * the wraps counter, so if the write pointer is smaller than
+        * the old write pointer (wrap occurred) but we read that no
+        * wrap occurred, we actually read between the next_entry and
+        * num_wraps update (this does happen in practice!!) -- take
+        * that into account by increasing num_wraps.
+        */
+       if (unlikely(next_entry < priv->event_log.next_entry &&
+                    num_wraps == priv->event_log.num_wraps))
+               num_wraps++;
+
        if (num_wraps == priv->event_log.num_wraps) {
-               iwl_print_cont_event_trace(priv,
-                                      base, priv->event_log.next_entry,
-                                      next_entry - priv->event_log.next_entry,
-                                      mode);
+               iwl_print_cont_event_trace(
+                       priv, base, priv->event_log.next_entry,
+                       next_entry - priv->event_log.next_entry,
+                       capacity, mode);
+
                priv->event_log.non_wraps_count++;
        } else {
-               if ((num_wraps - priv->event_log.num_wraps) > 1)
+               if (num_wraps - priv->event_log.num_wraps > 1)
                        priv->event_log.wraps_more_count++;
                else
                        priv->event_log.wraps_once_count++;
+
                trace_iwlwifi_dev_ucode_wrap_event(priv,
                                num_wraps - priv->event_log.num_wraps,
                                next_entry, priv->event_log.next_entry);
+
                if (next_entry < priv->event_log.next_entry) {
-                       iwl_print_cont_event_trace(priv, base,
-                              priv->event_log.next_entry,
-                              capacity - priv->event_log.next_entry,
-                              mode);
+                       iwl_print_cont_event_trace(
+                               priv, base, priv->event_log.next_entry,
+                               capacity - priv->event_log.next_entry,
+                               capacity, mode);
 
-                       iwl_print_cont_event_trace(priv, base, 0,
-                               next_entry, mode);
+                       iwl_print_cont_event_trace(
+                               priv, base, 0, next_entry, capacity, mode);
                } else {
-                       iwl_print_cont_event_trace(priv, base,
-                              next_entry, capacity - next_entry,
-                              mode);
+                       iwl_print_cont_event_trace(
+                               priv, base, next_entry,
+                               capacity - next_entry,
+                               capacity, mode);
 
-                       iwl_print_cont_event_trace(priv, base, 0,
-                               next_entry, mode);
+                       iwl_print_cont_event_trace(
+                               priv, base, 0, next_entry, capacity, mode);
                }
        }
+
        priv->event_log.num_wraps = num_wraps;
        priv->event_log.next_entry = next_entry;
 }
@@ -545,7 +583,7 @@ static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first)
                       priv->firmware_name);
 
        return request_firmware_nowait(THIS_MODULE, 1, priv->firmware_name,
-                                      bus(priv)->dev,
+                                      trans(priv)->dev,
                                       GFP_KERNEL, priv, iwl_ucode_callback);
 }
 
@@ -985,31 +1023,33 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
        /* Runtime instructions and 2 copies of data:
         * 1) unmodified from disk
         * 2) backup cache for save/restore during power-downs */
-       if (iwl_alloc_fw_desc(bus(priv), &trans(priv)->ucode_rt.code,
+       if (iwl_alloc_fw_desc(trans(priv), &trans(priv)->ucode_rt.code,
                              pieces.inst, pieces.inst_size))
                goto err_pci_alloc;
-       if (iwl_alloc_fw_desc(bus(priv), &trans(priv)->ucode_rt.data,
+       if (iwl_alloc_fw_desc(trans(priv), &trans(priv)->ucode_rt.data,
                              pieces.data, pieces.data_size))
                goto err_pci_alloc;
 
        /* Initialization instructions and data */
        if (pieces.init_size && pieces.init_data_size) {
-               if (iwl_alloc_fw_desc(bus(priv), &trans(priv)->ucode_init.code,
+               if (iwl_alloc_fw_desc(trans(priv),
+                                     &trans(priv)->ucode_init.code,
                                      pieces.init, pieces.init_size))
                        goto err_pci_alloc;
-               if (iwl_alloc_fw_desc(bus(priv), &trans(priv)->ucode_init.data,
+               if (iwl_alloc_fw_desc(trans(priv),
+                                     &trans(priv)->ucode_init.data,
                                      pieces.init_data, pieces.init_data_size))
                        goto err_pci_alloc;
        }
 
        /* WoWLAN instructions and data */
        if (pieces.wowlan_inst_size && pieces.wowlan_data_size) {
-               if (iwl_alloc_fw_desc(bus(priv),
+               if (iwl_alloc_fw_desc(trans(priv),
                                      &trans(priv)->ucode_wowlan.code,
                                      pieces.wowlan_inst,
                                      pieces.wowlan_inst_size))
                        goto err_pci_alloc;
-               if (iwl_alloc_fw_desc(bus(priv),
+               if (iwl_alloc_fw_desc(trans(priv),
                                      &trans(priv)->ucode_wowlan.data,
                                      pieces.wowlan_data,
                                      pieces.wowlan_data_size))
@@ -1108,7 +1148,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
        iwl_dealloc_ucode(trans(priv));
  out_unbind:
        complete(&priv->firmware_loading_complete);
-       device_release_driver(bus(priv)->dev);
+       device_release_driver(trans(priv)->dev);
        release_firmware(ucode_raw);
 }
 
@@ -1120,7 +1160,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
        int ret = 0;
 
        spin_lock_irqsave(&priv->shrd->lock, flags);
-       iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_CLR,
+       iwl_write32(trans(priv), CSR_UCODE_DRV_GP1_CLR,
                    CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
        spin_unlock_irqrestore(&priv->shrd->lock, flags);
        priv->thermal_throttle.ct_kill_toggle = false;
@@ -1205,9 +1245,6 @@ int iwl_alive_start(struct iwl_priv *priv)
        int ret = 0;
        struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
 
-       /*TODO: this should go to the transport layer */
-       iwl_reset_ict(trans(priv));
-
        IWL_DEBUG_INFO(priv, "Runtime Alive received.\n");
 
        /* After the ALIVE response, we can send host commands to the uCode */
@@ -1219,6 +1256,11 @@ int iwl_alive_start(struct iwl_priv *priv)
        if (iwl_is_rfkill(priv->shrd))
                return -ERFKILL;
 
+       if (priv->event_log.ucode_trace) {
+               /* start collecting data now */
+               mod_timer(&priv->ucode_trace, jiffies);
+       }
+
        /* download priority table before any calibration request */
        if (cfg(priv)->bt_params &&
            cfg(priv)->bt_params->advanced_bt_coexist) {
@@ -1653,7 +1695,7 @@ static void iwl_uninit_drv(struct iwl_priv *priv)
 
 static u32 iwl_hw_detect(struct iwl_priv *priv)
 {
-       return iwl_read32(bus(priv), CSR_HW_REV);
+       return iwl_read32(trans(priv), CSR_HW_REV);
 }
 
 /* Size of one Rx buffer in host DRAM */
@@ -1687,32 +1729,32 @@ static int iwl_set_hw_params(struct iwl_priv *priv)
 
 static void iwl_debug_config(struct iwl_priv *priv)
 {
-       dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_DEBUG "
+       dev_printk(KERN_INFO, trans(priv)->dev, "CONFIG_IWLWIFI_DEBUG "
 #ifdef CONFIG_IWLWIFI_DEBUG
                "enabled\n");
 #else
                "disabled\n");
 #endif
-       dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_DEBUGFS "
+       dev_printk(KERN_INFO, trans(priv)->dev, "CONFIG_IWLWIFI_DEBUGFS "
 #ifdef CONFIG_IWLWIFI_DEBUGFS
                "enabled\n");
 #else
                "disabled\n");
 #endif
-       dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_DEVICE_TRACING "
+       dev_printk(KERN_INFO, trans(priv)->dev, "CONFIG_IWLWIFI_DEVICE_TRACING "
 #ifdef CONFIG_IWLWIFI_DEVICE_TRACING
                "enabled\n");
 #else
                "disabled\n");
 #endif
 
-       dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_DEVICE_TESTMODE "
+       dev_printk(KERN_INFO, trans(priv)->dev, "CONFIG_IWLWIFI_DEVICE_TESTMODE "
 #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
                "enabled\n");
 #else
                "disabled\n");
 #endif
-       dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_P2P "
+       dev_printk(KERN_INFO, trans(priv)->dev, "CONFIG_IWLWIFI_P2P "
 #ifdef CONFIG_IWLWIFI_P2P
                "enabled\n");
 #else
@@ -1740,20 +1782,12 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops,
        }
 
        priv = hw->priv;
-       priv->shrd = &priv->_shrd;
-       bus->shrd = priv->shrd;
-       priv->shrd->bus = bus;
+       priv->shrd = bus->shrd;
        priv->shrd->priv = priv;
 
-       priv->shrd->trans = trans_ops->alloc(priv->shrd);
-       if (priv->shrd->trans == NULL) {
-               err = -ENOMEM;
-               goto out_free_traffic_mem;
-       }
-
        /* At this point both hw and priv are allocated. */
 
-       SET_IEEE80211_DEV(hw, bus(priv)->dev);
+       SET_IEEE80211_DEV(hw, trans(priv)->dev);
 
        /* what debugging capabilities we have */
        iwl_debug_config(priv);
@@ -1778,7 +1812,7 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops,
        /* these spin locks will be used in apm_ops.init and EEPROM access
         * we should init now
         */
-       spin_lock_init(&bus(priv)->reg_lock);
+       spin_lock_init(&trans(priv)->reg_lock);
        spin_lock_init(&priv->shrd->lock);
 
        /*
@@ -1786,7 +1820,7 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops,
         * strange state ... like being left stranded by a primary kernel
         * and this is now the kdump kernel trying to start up
         */
-       iwl_write32(bus(priv), CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+       iwl_write32(trans(priv), CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
 
        /***********************
         * 3. Read REV register
@@ -1795,24 +1829,20 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops,
        IWL_INFO(priv, "Detected %s, REV=0x%X\n",
                cfg(priv)->name, hw_rev);
 
-       err = iwl_trans_request_irq(trans(priv));
+       err = iwl_trans_start_hw(trans(priv));
        if (err)
-               goto out_free_trans;
-
-       if (iwl_trans_prepare_card_hw(trans(priv))) {
-               err = -EIO;
-               IWL_WARN(priv, "Failed, HW not ready\n");
-               goto out_free_trans;
-       }
+               goto out_free_traffic_mem;
 
        /*****************
         * 4. Read EEPROM
         *****************/
        /* Read the EEPROM */
        err = iwl_eeprom_init(priv, hw_rev);
+       /* Reset chip to save power until we load uCode during "up". */
+       iwl_apm_stop(priv);
        if (err) {
                IWL_ERR(priv, "Unable to init EEPROM\n");
-               goto out_free_trans;
+               goto out_free_traffic_mem;
        }
        err = iwl_eeprom_check_version(priv);
        if (err)
@@ -1867,7 +1897,7 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops,
        iwl_enable_rfkill_int(priv);
 
        /* If platform's RF_KILL switch is NOT set to KILL */
-       if (iwl_read32(bus(priv),
+       if (iwl_read32(trans(priv),
                        CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
                clear_bit(STATUS_RF_KILL_HW, &priv->shrd->status);
        else
@@ -1893,8 +1923,6 @@ out_destroy_workqueue:
        iwl_uninit_drv(priv);
 out_free_eeprom:
        iwl_eeprom_free(priv->shrd);
-out_free_trans:
-       iwl_trans_free(trans(priv));
 out_free_traffic_mem:
        iwl_free_traffic_mem(priv);
        ieee80211_free_hw(priv->hw);
@@ -1938,8 +1966,6 @@ void __devexit iwl_remove(struct iwl_priv * priv)
        priv->shrd->workqueue = NULL;
        iwl_free_traffic_mem(priv);
 
-       iwl_trans_free(trans(priv));
-
        iwl_uninit_drv(priv);
 
        dev_kfree_skb(priv->beacon_skb);
@@ -2054,7 +2080,7 @@ MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)");
 
 module_param_named(led_mode, iwlagn_mod_params.led_mode, int, S_IRUGO);
 MODULE_PARM_DESC(led_mode, "0=system default, "
-               "1=On(RF On)/Off(RF Off), 2=blinking (default: 0)");
+               "1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0)");
 
 module_param_named(power_save, iwlagn_mod_params.power_save,
                bool, S_IRUGO);