diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/pcie')
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/1000.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/2000.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/5000.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/6000.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/7000.c | 111 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/cfg.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/drv.c | 10 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/internal.h | 17 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/rx.c | 57 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/trans.c | 361 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/tx.c | 89 |
11 files changed, 526 insertions, 133 deletions
diff --git a/drivers/net/wireless/iwlwifi/pcie/1000.c b/drivers/net/wireless/iwlwifi/pcie/1000.c index f8620ecae6b..ff338975728 100644 --- a/drivers/net/wireless/iwlwifi/pcie/1000.c +++ b/drivers/net/wireless/iwlwifi/pcie/1000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. * * 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 diff --git a/drivers/net/wireless/iwlwifi/pcie/2000.c b/drivers/net/wireless/iwlwifi/pcie/2000.c index 244019cec3e..e7de33128b1 100644 --- a/drivers/net/wireless/iwlwifi/pcie/2000.c +++ b/drivers/net/wireless/iwlwifi/pcie/2000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. * * 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 diff --git a/drivers/net/wireless/iwlwifi/pcie/5000.c b/drivers/net/wireless/iwlwifi/pcie/5000.c index 83ca40321ff..5096f7c96ab 100644 --- a/drivers/net/wireless/iwlwifi/pcie/5000.c +++ b/drivers/net/wireless/iwlwifi/pcie/5000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. * * 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 diff --git a/drivers/net/wireless/iwlwifi/pcie/6000.c b/drivers/net/wireless/iwlwifi/pcie/6000.c index d4df976d470..801ff49796d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/6000.c +++ b/drivers/net/wireless/iwlwifi/pcie/6000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. * * 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 diff --git a/drivers/net/wireless/iwlwifi/pcie/7000.c b/drivers/net/wireless/iwlwifi/pcie/7000.c new file mode 100644 index 00000000000..6e35b2b7233 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/7000.c @@ -0,0 +1,111 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * + * 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. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/module.h> +#include <linux/stringify.h> +#include "iwl-config.h" +#include "iwl-agn-hw.h" +#include "cfg.h" + +/* Highest firmware API version supported */ +#define IWL7260_UCODE_API_MAX 6 +#define IWL3160_UCODE_API_MAX 6 + +/* Oldest version we won't warn about */ +#define IWL7260_UCODE_API_OK 6 +#define IWL3160_UCODE_API_OK 6 + +/* Lowest firmware API version supported */ +#define IWL7260_UCODE_API_MIN 6 +#define IWL3160_UCODE_API_MIN 6 + +/* NVM versions */ +#define IWL7260_NVM_VERSION 0x0a1d +#define IWL7260_TX_POWER_VERSION 0xffff /* meaningless */ +#define IWL3160_NVM_VERSION 0x709 +#define IWL3160_TX_POWER_VERSION 0xffff /* meaningless */ + +#define IWL7260_FW_PRE "iwlwifi-7260-" +#define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode" + +#define IWL3160_FW_PRE "iwlwifi-3160-" +#define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode" + +static const struct iwl_base_params iwl7000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .shadow_ram_support = true, + .led_compensation = 57, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ +}; + +static const struct iwl_ht_params iwl7000_ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), +}; + +#define IWL_DEVICE_7000 \ + .ucode_api_max = IWL7260_UCODE_API_MAX, \ + .ucode_api_ok = IWL7260_UCODE_API_OK, \ + .ucode_api_min = IWL7260_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_7000, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .base_params = &iwl7000_base_params, \ + /* TODO: .bt_params? */ \ + .need_temp_offset_calib = true, \ + .led_mode = IWL_LED_RF_STATE, \ + .adv_pm = true \ + + +const struct iwl_cfg iwl7260_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, +}; + +MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); +MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/pcie/cfg.h b/drivers/net/wireless/iwlwifi/pcie/cfg.h index 82152311d73..c6f8e83c355 100644 --- a/drivers/net/wireless/iwlwifi/pcie/cfg.h +++ b/drivers/net/wireless/iwlwifi/pcie/cfg.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. * * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -109,5 +109,7 @@ extern const struct iwl_cfg iwl6035_2agn_cfg; extern const struct iwl_cfg iwl105_bgn_cfg; extern const struct iwl_cfg iwl105_bgn_d_cfg; extern const struct iwl_cfg iwl135_bgn_cfg; +extern const struct iwl_cfg iwl7260_2ac_cfg; +extern const struct iwl_cfg iwl3160_ac_cfg; #endif /* __iwl_pci_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index c2e141af353..7bc0fb9128d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. * * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -255,6 +255,12 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, +/* 7000 Series */ + {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)}, + {0} }; MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index d91d2e8c62f..aa2a39a637d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2013 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. @@ -222,8 +222,6 @@ struct iwl_txq { * @rx_replenish: work that will be called when buffers need to be allocated * @drv - pointer to iwl_drv * @trans: pointer to the generic transport area - * @irq - the irq number for the device - * @irq_requested: true when the irq has been requested * @scd_base_addr: scheduler sram base address in SRAM * @scd_bc_tbls: pointer to the byte count table of the scheduler * @kw: keep warm address @@ -234,8 +232,10 @@ struct iwl_txq { * @status - transport specific status flags * @cmd_queue - command queue number * @rx_buf_size_8k: 8 kB RX buffer size + * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @rx_page_order: page order for receive buffer size * @wd_timeout: queue watchdog timeout (jiffies) + * @reg_lock: protect hw register access */ struct iwl_trans_pcie { struct iwl_rxq rxq; @@ -249,11 +249,8 @@ struct iwl_trans_pcie { int ict_index; u32 inta; bool use_ict; - bool irq_requested; - struct tasklet_struct irq_tasklet; struct isr_statistics isr_stats; - unsigned int irq; spinlock_t irq_lock; u32 inta_mask; u32 scd_base_addr; @@ -279,12 +276,16 @@ struct iwl_trans_pcie { u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; bool rx_buf_size_8k; + bool bc_table_dword; u32 rx_page_order; const char **command_names; /* queue watchdog */ unsigned long wd_timeout; + + /*protect hw register */ + spinlock_t reg_lock; }; /** @@ -328,7 +329,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans); * RX ******************************************************/ int iwl_pcie_rx_init(struct iwl_trans *trans); -void iwl_pcie_tasklet(struct iwl_trans *trans); +irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id); int iwl_pcie_rx_stop(struct iwl_trans *trans); void iwl_pcie_rx_free(struct iwl_trans *trans); @@ -359,6 +360,8 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, int handler_status); void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs); +void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); + /***************************************************** * Error handling ******************************************************/ diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 8389cd38338..b0ae06d2456 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2013 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. @@ -81,10 +81,10 @@ * 'processed' and 'read' driver indexes as well) * + A received packet is processed and handed to the kernel network stack, * detached from the iwl->rxq. The driver 'processed' index is updated. - * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free - * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ - * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there - * were enough free buffers and RX_STALLED is set it is cleared. + * + The Host/Firmware iwl->rxq is replenished at irq thread time from the + * rx_free list. If there are no allocated buffers in iwl->rxq->rx_free, + * the READ INDEX is not incremented and iwl->status(RX_STALLED) is set. + * If there were enough free buffers and RX_STALLED is set it is cleared. * * * Driver sequence: @@ -214,9 +214,9 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) /* * If the device isn't enabled - not need to try to add buffers... * This can happen when we stop the device and still have an interrupt - * pending. We stop the APM before we sync the interrupts / tasklets - * because we have to (see comment there). On the other hand, since - * the APM is stopped, we cannot access the HW (in particular not prph). + * pending. We stop the APM before we sync the interrupts because we + * have to (see comment there). On the other hand, since the APM is + * stopped, we cannot access the HW (in particular not prph). * So don't try to restock if the APM has been already stopped. */ if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) @@ -436,7 +436,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans) err_rb_stts: dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, rxq->bd, rxq->bd_dma); - memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); + rxq->bd_dma = 0; rxq->bd = NULL; err_bd: return -ENOMEM; @@ -455,6 +455,10 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) /* Stop Rx DMA */ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + /* reset and flush pointers */ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0); + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0); + iwl_write_direct32(trans, FH_RSCSR_CHNL0_RDPTR, 0); /* Reset driver's Rx queue write index */ iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); @@ -491,7 +495,6 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rxq *rxq = &trans_pcie->rxq; - int i, err; unsigned long flags; @@ -518,6 +521,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) rxq->read = rxq->write = 0; rxq->write_actual = 0; rxq->free_count = 0; + memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); spin_unlock_irqrestore(&rxq->lock, flags); iwl_pcie_rx_replenish(trans); @@ -545,13 +549,15 @@ void iwl_pcie_rx_free(struct iwl_trans *trans) return; } + cancel_work_sync(&trans_pcie->rx_replenish); + spin_lock_irqsave(&rxq->lock, flags); iwl_pcie_rxq_free_rbs(trans); spin_unlock_irqrestore(&rxq->lock, flags); dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, rxq->bd, rxq->bd_dma); - memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); + rxq->bd_dma = 0; rxq->bd = NULL; if (rxq->rb_stts) @@ -560,7 +566,7 @@ void iwl_pcie_rx_free(struct iwl_trans *trans) rxq->rb_stts, rxq->rb_stts_dma); else IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); - memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma)); + rxq->rb_stts_dma = 0; rxq->rb_stts = NULL; } @@ -588,6 +594,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, int index, cmd_index, err, len; struct iwl_rx_cmd_buffer rxcb = { ._offset = offset, + ._rx_page_order = trans_pcie->rx_page_order, ._page = rxb->page, ._page_stolen = false, .truesize = max_len, @@ -789,11 +796,14 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); wake_up(&trans_pcie->wait_command_queue); + local_bh_disable(); iwl_op_mode_nic_error(trans->op_mode); + local_bh_enable(); } -void iwl_pcie_tasklet(struct iwl_trans *trans) +irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) { + struct iwl_trans *trans = dev_id; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct isr_statistics *isr_stats = &trans_pcie->isr_stats; u32 inta = 0; @@ -804,6 +814,8 @@ void iwl_pcie_tasklet(struct iwl_trans *trans) u32 inta_mask; #endif + lock_map_acquire(&trans->sync_cmd_lockdep_map); + spin_lock_irqsave(&trans_pcie->irq_lock, flags); /* Ack/clear/reset pending uCode interrupts. @@ -848,7 +860,7 @@ void iwl_pcie_tasklet(struct iwl_trans *trans) handled |= CSR_INT_BIT_HW_ERR; - return; + goto out; } #ifdef CONFIG_IWLWIFI_DEBUG @@ -998,6 +1010,10 @@ void iwl_pcie_tasklet(struct iwl_trans *trans) /* Re-enable RF_KILL if it occurred */ else if (handled & CSR_INT_BIT_RF_KILL) iwl_enable_rfkill_int(trans); + +out: + lock_map_release(&trans->sync_cmd_lockdep_map); + return IRQ_HANDLED; } /****************************************************************************** @@ -1120,7 +1136,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) /* Disable (but don't clear!) interrupts here to avoid * back-to-back ISRs and sporadic interrupts from our NIC. - * If we have something to service, the tasklet will re-enable ints. + * If we have something to service, the irq thread will re-enable ints. * If we *don't* have something, we'll re-enable before leaving here. */ inta_mask = iwl_read32(trans, CSR_INT_MASK); iwl_write32(trans, CSR_INT_MASK, 0x00000000); @@ -1160,9 +1176,9 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) #endif trans_pcie->inta |= inta; - /* iwl_pcie_tasklet() will service interrupts and re-enable them */ + /* the thread will service interrupts and re-enable them */ if (likely(inta)) - tasklet_schedule(&trans_pcie->irq_tasklet); + return IRQ_WAKE_THREAD; else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); @@ -1270,9 +1286,10 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) trans_pcie->inta |= inta; /* iwl_pcie_tasklet() will service interrupts and re-enable them */ - if (likely(inta)) - tasklet_schedule(&trans_pcie->irq_tasklet); - else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && + if (likely(inta)) { + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + return IRQ_WAKE_THREAD; + } else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) { /* Allow interrupt if was disabled by this handler and * no tasklet was schedules, We should not enable interrupt, diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 35708b959ad..17bedc50e75 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. * * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -75,21 +75,43 @@ #include "iwl-agn-hw.h" #include "internal.h" -static void iwl_pcie_set_pwr_vmain(struct iwl_trans *trans) +static void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, + u32 reg, u32 mask, u32 value) { -/* - * (for documentation purposes) - * to set power to V_AUX, do: + u32 v; - if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) - iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VAUX, - ~APMG_PS_CTRL_MSK_PWR_SRC); - */ +#ifdef CONFIG_IWLWIFI_DEBUG + WARN_ON_ONCE(value & ~mask); +#endif - iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, - ~APMG_PS_CTRL_MSK_PWR_SRC); + v = iwl_read32(trans, reg); + v &= ~mask; + v |= value; + iwl_write32(trans, reg, v); +} + +static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans, + u32 reg, u32 mask) +{ + __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0); +} + +static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, + u32 reg, u32 mask) +{ + __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask); +} + +static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) +{ + if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) + iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + else + iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); } /* PCI registers */ @@ -259,7 +281,7 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans) spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - iwl_pcie_set_pwr_vmain(trans); + iwl_pcie_set_pwr(trans, false); iwl_op_mode_nic_config(trans->op_mode); @@ -435,7 +457,7 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, } static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, - const struct fw_img *fw) + const struct fw_img *fw, bool run_in_rfkill) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; @@ -454,7 +476,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, /* If platform's RF_KILL switch is NOT set to KILL */ hw_rfkill = iwl_is_rfkill_set(trans); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - if (hw_rfkill) + if (hw_rfkill && !run_in_rfkill) return -ERFKILL; iwl_write32(trans, CSR_INT, 0xFFFFFFFF); @@ -534,12 +556,6 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) iwl_enable_rfkill_int(trans); - /* wait to make sure we flush pending tasklet*/ - synchronize_irq(trans_pcie->irq); - tasklet_kill(&trans_pcie->irq_tasklet); - - cancel_work_sync(&trans_pcie->rx_replenish); - /* stop and reset the on-board processor */ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); @@ -551,46 +567,87 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) clear_bit(STATUS_RFKILL, &trans_pcie->status); } -static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) +static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans) { /* let the ucode operate on its own */ iwl_write32(trans, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); iwl_disable_interrupts(trans); + iwl_pcie_disable_ict(trans); + iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * reset TX queues -- some of their registers reset during S3 + * so if we don't reset everything here the D3 image would try + * to execute some invalid memory upon resume + */ + iwl_trans_pcie_tx_reset(trans); + + iwl_pcie_set_pwr(trans, true); } -static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) +static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, + enum iwl_d3_status *status) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int err; - bool hw_rfkill; + u32 val; + int ret; - trans_pcie->inta_mask = CSR_INI_SET_MASK; + iwl_pcie_set_pwr(trans, false); - if (!trans_pcie->irq_requested) { - tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long)) - iwl_pcie_tasklet, (unsigned long)trans); + val = iwl_read32(trans, CSR_RESET); + if (val & CSR_RESET_REG_FLAG_NEVO_RESET) { + *status = IWL_D3_STATUS_RESET; + return 0; + } - iwl_pcie_alloc_ict(trans); + /* + * Also enables interrupts - none will happen as the device doesn't + * know we're waking it up, only when the opmode actually tells it + * after this call. + */ + iwl_pcie_reset_ict(trans); - err = request_irq(trans_pcie->irq, iwl_pcie_isr_ict, - IRQF_SHARED, DRV_NAME, trans); - if (err) { - IWL_ERR(trans, "Error allocating IRQ %d\n", - trans_pcie->irq); - goto error; - } + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret) { + IWL_ERR(trans, "Failed to resume the device (mac ready)\n"); + return ret; + } - trans_pcie->irq_requested = true; + iwl_trans_pcie_tx_reset(trans); + + ret = iwl_pcie_rx_init(trans); + if (ret) { + IWL_ERR(trans, "Failed to resume the device (RX reset)\n"); + return ret; } + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + + *status = IWL_D3_STATUS_ALIVE; + return 0; +} + +static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) +{ + bool hw_rfkill; + int err; + err = iwl_pcie_prepare_card_hw(trans); if (err) { IWL_ERR(trans, "Error while preparing HW: %d\n", err); - goto err_free_irq; + return err; } iwl_pcie_apm_init(trans); @@ -601,15 +658,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) hw_rfkill = iwl_is_rfkill_set(trans); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - return err; - -err_free_irq: - trans_pcie->irq_requested = false; - free_irq(trans_pcie->irq, trans); -error: - iwl_pcie_free_ict(trans); - tasklet_kill(&trans_pcie->irq_tasklet); - return err; + return 0; } static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, @@ -703,19 +752,20 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, msecs_to_jiffies(trans_cfg->queue_watchdog_timeout); trans_pcie->command_names = trans_cfg->command_names; + trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; } void iwl_trans_pcie_free(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + synchronize_irq(trans_pcie->pci_dev->irq); + iwl_pcie_tx_free(trans); iwl_pcie_rx_free(trans); - if (trans_pcie->irq_requested == true) { - free_irq(trans_pcie->irq, trans); - iwl_pcie_free_ict(trans); - } + free_irq(trans_pcie->pci_dev->irq, trans); + iwl_pcie_free_ict(trans); pci_disable_msi(trans_pcie->pci_dev); iounmap(trans_pcie->hw_base); @@ -751,13 +801,126 @@ static int iwl_trans_pcie_resume(struct iwl_trans *trans) hw_rfkill = iwl_is_rfkill_set(trans); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - if (!hw_rfkill) - iwl_enable_interrupts(trans); - return 0; } #endif /* CONFIG_PM_SLEEP */ +static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, + unsigned long *flags) +{ + int ret; + struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans); + spin_lock_irqsave(&pcie_trans->reg_lock, *flags); + + /* this bit wakes up the NIC */ + __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* + * These bits say the device is running, and should keep running for + * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), + * but they do not indicate that embedded SRAM is restored yet; + * 3945 and 4965 have volatile SRAM, and must save/restore contents + * to/from host DRAM when sleeping/waking for power-saving. + * Each direction takes approximately 1/4 millisecond; with this + * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a + * series of register accesses are expected (e.g. reading Event Log), + * to keep device from sleeping. + * + * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that + * SRAM is okay/restored. We don't check that here because this call + * is just for hardware register access; but GP1 MAC_SLEEP check is a + * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). + * + * 5000 series and later (including 1000 series) have non-volatile SRAM, + * and do not save/restore SRAM when power cycling. + */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, + (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | + CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); + if (unlikely(ret < 0)) { + iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); + if (!silent) { + u32 val = iwl_read32(trans, CSR_GP_CNTRL); + WARN_ONCE(1, + "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", + val); + spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags); + return false; + } + } + + /* + * Fool sparse by faking we release the lock - sparse will + * track nic_access anyway. + */ + __release(&pcie_trans->reg_lock); + return true; +} + +static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, + unsigned long *flags) +{ + struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans); + + lockdep_assert_held(&pcie_trans->reg_lock); + + /* + * Fool sparse by faking we acquiring the lock - sparse will + * track nic_access anyway. + */ + __acquire(&pcie_trans->reg_lock); + + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + /* + * Above we read the CSR_GP_CNTRL register, which will flush + * any previous writes, but we need the write that clears the + * MAC_ACCESS_REQ bit to be performed before any other writes + * scheduled on different CPUs (after we drop reg_lock). + */ + mmiowb(); + spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags); +} + +static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, + void *buf, int dwords) +{ + unsigned long flags; + int offs, ret = 0; + u32 *vals = buf; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr); + for (offs = 0; offs < dwords; offs++) + vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + iwl_trans_release_nic_access(trans, &flags); + } else { + ret = -EBUSY; + } + return ret; +} + +static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, + void *buf, int dwords) +{ + unsigned long flags; + int offs, ret = 0; + u32 *vals = buf; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); + for (offs = 0; offs < dwords; offs++) + iwl_write32(trans, HBUS_TARG_MEM_WDAT, + vals ? vals[offs] : 0); + iwl_trans_release_nic_access(trans, &flags); + } else { + ret = -EBUSY; + } + return ret; +} + #define IWL_FLUSH_WAIT_MS 2000 static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans) @@ -767,6 +930,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans) struct iwl_queue *q; int cnt; unsigned long now = jiffies; + u32 scd_sram_addr; + u8 buf[16]; int ret = 0; /* waiting for all the tx frames complete might take a while */ @@ -780,14 +945,64 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans) msleep(1); if (q->read_ptr != q->write_ptr) { - IWL_ERR(trans, "fail to flush all tx fifo queues\n"); + IWL_ERR(trans, + "fail to flush all tx fifo queues Q %d\n", cnt); ret = -ETIMEDOUT; break; } } + + if (!ret) + return 0; + + IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", + txq->q.read_ptr, txq->q.write_ptr); + + scd_sram_addr = trans_pcie->scd_base_addr + + SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); + iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); + + iwl_print_hex_error(trans, buf, sizeof(buf)); + + for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++) + IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt, + iwl_read_direct32(trans, FH_TX_TRB_REG(cnt))); + + for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { + u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt)); + u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; + bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); + u32 tbl_dw = + iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(cnt)); + + if (cnt & 0x1) + tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; + else + tbl_dw = tbl_dw & 0x0000FFFF; + + IWL_ERR(trans, + "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", + cnt, active ? "" : "in", fifo, tbl_dw, + iwl_read_prph(trans, + SCD_QUEUE_RDPTR(cnt)) & (txq->q.n_bd - 1), + iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); + } + return ret; } +static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, + u32 mask, u32 value) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value); + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); +} + static const char *get_fh_string(int cmd) { #define IWL_CMD(x) case x: return #x @@ -1212,7 +1427,8 @@ static const struct iwl_trans_ops trans_ops_pcie = { .start_fw = iwl_trans_pcie_start_fw, .stop_device = iwl_trans_pcie_stop_device, - .wowlan_suspend = iwl_trans_pcie_wowlan_suspend, + .d3_suspend = iwl_trans_pcie_d3_suspend, + .d3_resume = iwl_trans_pcie_d3_resume, .send_cmd = iwl_trans_pcie_send_hcmd, @@ -1235,8 +1451,13 @@ static const struct iwl_trans_ops trans_ops_pcie = { .read32 = iwl_trans_pcie_read32, .read_prph = iwl_trans_pcie_read_prph, .write_prph = iwl_trans_pcie_write_prph, + .read_mem = iwl_trans_pcie_read_mem, + .write_mem = iwl_trans_pcie_write_mem, .configure = iwl_trans_pcie_configure, .set_pmi = iwl_trans_pcie_set_pmi, + .grab_nic_access = iwl_trans_pcie_grab_nic_access, + .release_nic_access = iwl_trans_pcie_release_nic_access, + .set_bits_mask = iwl_trans_pcie_set_bits_mask, }; struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, @@ -1258,8 +1479,10 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans->ops = &trans_ops_pcie; trans->cfg = cfg; + trans_lockdep_init(trans); trans_pcie->trans = trans; spin_lock_init(&trans_pcie->irq_lock); + spin_lock_init(&trans_pcie->reg_lock); init_waitqueue_head(&trans_pcie->ucode_write_waitq); /* W/A - seems to solve weird behavior. We need to remove this if we @@ -1318,7 +1541,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } trans->dev = &pdev->dev; - trans_pcie->irq = pdev->irq; trans_pcie->pci_dev = pdev; trans->hw_rev = iwl_read32(trans, CSR_HW_REV); trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; @@ -1327,7 +1549,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, /* Initialize the wait queue for commands */ init_waitqueue_head(&trans_pcie->wait_command_queue); - spin_lock_init(&trans->reg_lock); snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), "iwl_cmd_pool:%s", dev_name(trans->dev)); @@ -1344,8 +1565,24 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, if (!trans->dev_cmd_pool) goto out_pci_disable_msi; + trans_pcie->inta_mask = CSR_INI_SET_MASK; + + if (iwl_pcie_alloc_ict(trans)) + goto out_free_cmd_pool; + + if (request_threaded_irq(pdev->irq, iwl_pcie_isr_ict, + iwl_pcie_irq_handler, + IRQF_SHARED, DRV_NAME, trans)) { + IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq); + goto out_free_ict; + } + return trans; +out_free_ict: + iwl_pcie_free_ict(trans); +out_free_cmd_pool: + kmem_cache_destroy(trans->dev_cmd_pool); out_pci_disable_msi: pci_disable_msi(pdev); out_pci_release_regions: diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 6c5b867c353..8e9e3212fe7 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2013 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. @@ -160,7 +160,7 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", txq->q.read_ptr, txq->q.write_ptr); - iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); + iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); iwl_print_hex_error(trans, buf, sizeof(buf)); @@ -173,9 +173,9 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); u32 tbl_dw = - iwl_read_targ_mem(trans, - trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(i)); + iwl_trans_read_mem32(trans, + trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(i)); if (i & 0x1) tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; @@ -237,7 +237,10 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, break; } - bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12)); + if (trans_pcie->bc_table_dword) + len = DIV_ROUND_UP(len, 4); + + bc_ent = cpu_to_le16(len | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; @@ -306,6 +309,9 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq) return; } + IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, + txq->q.write_ptr); + iwl_write_direct32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); @@ -612,7 +618,7 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) if (txq->q.n_bd) { dma_free_coherent(dev, sizeof(struct iwl_tfd) * txq->q.n_bd, txq->tfds, txq->q.dma_addr); - memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr)); + txq->q.dma_addr = 0; } kfree(txq->entries); @@ -638,9 +644,11 @@ static void iwl_pcie_txq_set_sched(struct iwl_trans *trans, u32 mask) void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 a; + int nq = trans->cfg->base_params->num_of_queues; int chan; u32 reg_val; + int clear_dwords = (SCD_TRANS_TBL_OFFSET_QUEUE(nq) - + SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(u32); /* make sure all queue are not stopped/used */ memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); @@ -652,20 +660,10 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) WARN_ON(scd_base_addr != 0 && scd_base_addr != trans_pcie->scd_base_addr); - a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; - /* reset conext data memory */ - for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; - a += 4) - iwl_write_targ_mem(trans, a, 0); - /* reset tx status memory */ - for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND; - a += 4) - iwl_write_targ_mem(trans, a, 0); - for (; a < trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE( - trans->cfg->base_params->num_of_queues); - a += 4) - iwl_write_targ_mem(trans, a, 0); + /* reset context data, TX status and translation data */ + iwl_trans_write_mem(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_MEM_LOWER_BOUND, + NULL, clear_dwords); iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, trans_pcie->scd_bc_tbls.dma >> 10); @@ -697,6 +695,29 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) APMG_PCIDEV_STT_VAL_L1_ACT_DIS); } +void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int txq_id; + + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + + iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), + txq->q.dma_addr >> 8); + iwl_pcie_txq_unmap(trans, txq_id); + txq->q.read_ptr = 0; + txq->q.write_ptr = 0; + } + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, + trans_pcie->kw.dma >> 4); + + iwl_pcie_tx_start(trans, trans_pcie->scd_base_addr); +} + /* * iwl_pcie_tx_stop - Stop all Tx DMA channels */ @@ -905,7 +926,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, if (WARN_ON(txq_id == trans_pcie->cmd_queue)) return; - spin_lock(&txq->lock); + spin_lock_bh(&txq->lock); if (txq->q.read_ptr == tfd_num) goto out; @@ -949,7 +970,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, if (iwl_queue_space(&txq->q) > txq->q.low_mark) iwl_wake_queue(trans, txq); out: - spin_unlock(&txq->lock); + spin_unlock_bh(&txq->lock); } /* @@ -1002,14 +1023,14 @@ static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, tbl_dw_addr = trans_pcie->scd_base_addr + SCD_TRANS_TBL_OFFSET_QUEUE(txq_id); - tbl_dw = iwl_read_targ_mem(trans, tbl_dw_addr); + tbl_dw = iwl_trans_read_mem32(trans, tbl_dw_addr); if (txq_id & 0x1) tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); else tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); - iwl_write_targ_mem(trans, tbl_dw_addr, tbl_dw); + iwl_trans_write_mem32(trans, tbl_dw_addr, tbl_dw); return 0; } @@ -1068,9 +1089,9 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn); /* Set up Tx window size and frame limit for this queue */ - iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + + iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0); - iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + + iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | @@ -1101,8 +1122,8 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) iwl_pcie_txq_set_inactive(trans, txq_id); - _iwl_write_targ_mem_dwords(trans, stts_addr, - zero_val, ARRAY_SIZE(zero_val)); + iwl_trans_write_mem(trans, stts_addr, (void *)zero_val, + ARRAY_SIZE(zero_val)); iwl_pcie_txq_unmap(trans, txq_id); @@ -1350,7 +1371,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, return; } - spin_lock(&txq->lock); + spin_lock_bh(&txq->lock); cmd_index = get_cmd_index(&txq->q, index); cmd = txq->entries[cmd_index].cmd; @@ -1384,7 +1405,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, meta->flags = 0; - spin_unlock(&txq->lock); + spin_unlock_bh(&txq->lock); } #define HOST_COMPLETE_TIMEOUT (2 * HZ) @@ -1642,10 +1663,6 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); - IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n", - le16_to_cpu(dev_cmd->hdr.sequence)); - IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); - /* Set up entry for this TFD in Tx byte-count array */ iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); |