aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-01-08 05:25:44 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-01-16 08:19:54 -0500
commitddaf5a5b300b8f9d3591b509fd8bedab1c9887be (patch)
treec78ebe39d33bf334e120d08c3206a5b048f8fcea /drivers/net
parent22dc3c9561825a7c2cd18d01b01358c2141a8e16 (diff)
iwlwifi: enable communication with WoWLAN firmware
On resuming, the opmode may have to be able to talk to the WoWLAN/D3 firmware in order to query it about its status and wakeup reasons. To do that, the opmode has to call the new d3_resume() transport API which will set up the device for command communcation. Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fh.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h29
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/rx.c6
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c91
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c23
7 files changed, 136 insertions, 24 deletions
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index 1ed4bc694d29..fef17c15562b 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -206,7 +206,8 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
206 206
207#ifdef CONFIG_PM_SLEEP 207#ifdef CONFIG_PM_SLEEP
208 if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len && 208 if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
209 priv->trans->ops->wowlan_suspend && 209 priv->trans->ops->d3_suspend &&
210 priv->trans->ops->d3_resume &&
210 device_can_wakeup(priv->trans->dev)) { 211 device_can_wakeup(priv->trans->dev)) {
211 hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | 212 hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
212 WIPHY_WOWLAN_DISCONNECT | 213 WIPHY_WOWLAN_DISCONNECT |
@@ -426,7 +427,7 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
426 if (ret) 427 if (ret)
427 goto error; 428 goto error;
428 429
429 iwl_trans_wowlan_suspend(priv->trans); 430 iwl_trans_d3_suspend(priv->trans);
430 431
431 goto out; 432 goto out;
432 433
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h
index ec48563d3c6a..c646a90b725e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fh.h
@@ -225,6 +225,8 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
225#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008) 225#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008)
226#define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG) 226#define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG)
227 227
228#define FW_RSCSR_CHNL0_RXDCB_RDPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x00c)
229#define FH_RSCSR_CHNL0_RDPTR FW_RSCSR_CHNL0_RXDCB_RDPTR_REG
228 230
229/** 231/**
230 * Rx Config/Status Registers (RCSR) 232 * Rx Config/Status Registers (RCSR)
@@ -257,6 +259,8 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
257#define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND) 259#define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND)
258 260
259#define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0) 261#define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0)
262#define FH_MEM_RCSR_CHNL0_RBDCB_WPTR (FH_MEM_RCSR_CHNL0 + 0x8)
263#define FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ (FH_MEM_RCSR_CHNL0 + 0x10)
260 264
261#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ 265#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */
262#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ 266#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 55a140ade5a7..0f85eb305878 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -308,6 +308,16 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
308#define IWL_FRAME_LIMIT 64 308#define IWL_FRAME_LIMIT 64
309 309
310/** 310/**
311 * enum iwl_wowlan_status - WoWLAN image/device status
312 * @IWL_D3_STATUS_ALIVE: firmware is still running after resume
313 * @IWL_D3_STATUS_RESET: device was reset while suspended
314 */
315enum iwl_d3_status {
316 IWL_D3_STATUS_ALIVE,
317 IWL_D3_STATUS_RESET,
318};
319
320/**
311 * struct iwl_trans_config - transport configuration 321 * struct iwl_trans_config - transport configuration
312 * 322 *
313 * @op_mode: pointer to the upper layer. 323 * @op_mode: pointer to the upper layer.
@@ -363,9 +373,12 @@ struct iwl_trans;
363 * May sleep 373 * May sleep
364 * @stop_device:stops the whole device (embedded CPU put to reset) 374 * @stop_device:stops the whole device (embedded CPU put to reset)
365 * May sleep 375 * May sleep
366 * @wowlan_suspend: put the device into the correct mode for WoWLAN during 376 * @d3_suspend: put the device into the correct mode for WoWLAN during
367 * suspend. This is optional, if not implemented WoWLAN will not be 377 * suspend. This is optional, if not implemented WoWLAN will not be
368 * supported. This callback may sleep. 378 * supported. This callback may sleep.
379 * @d3_resume: resume the device after WoWLAN, enabling the opmode to
380 * talk to the WoWLAN image to get its status. This is optional, if not
381 * implemented WoWLAN will not be supported. This callback may sleep.
369 * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted. 382 * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted.
370 * If RFkill is asserted in the middle of a SYNC host command, it must 383 * If RFkill is asserted in the middle of a SYNC host command, it must
371 * return -ERFKILL straight away. 384 * return -ERFKILL straight away.
@@ -409,7 +422,8 @@ struct iwl_trans_ops {
409 void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); 422 void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
410 void (*stop_device)(struct iwl_trans *trans); 423 void (*stop_device)(struct iwl_trans *trans);
411 424
412 void (*wowlan_suspend)(struct iwl_trans *trans); 425 void (*d3_suspend)(struct iwl_trans *trans);
426 int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status);
413 427
414 int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); 428 int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
415 429
@@ -562,10 +576,17 @@ static inline void iwl_trans_stop_device(struct iwl_trans *trans)
562 trans->state = IWL_TRANS_NO_FW; 576 trans->state = IWL_TRANS_NO_FW;
563} 577}
564 578
565static inline void iwl_trans_wowlan_suspend(struct iwl_trans *trans) 579static inline void iwl_trans_d3_suspend(struct iwl_trans *trans)
580{
581 might_sleep();
582 trans->ops->d3_suspend(trans);
583}
584
585static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
586 enum iwl_d3_status *status)
566{ 587{
567 might_sleep(); 588 might_sleep();
568 trans->ops->wowlan_suspend(trans); 589 return trans->ops->d3_resume(trans, status);
569} 590}
570 591
571static inline int iwl_trans_send_cmd(struct iwl_trans *trans, 592static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 8f017c34ab2b..20735a008cab 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -357,6 +357,8 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
357 struct iwl_rx_cmd_buffer *rxb, int handler_status); 357 struct iwl_rx_cmd_buffer *rxb, int handler_status);
358void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, 358void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
359 struct sk_buff_head *skbs); 359 struct sk_buff_head *skbs);
360void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
361
360/***************************************************** 362/*****************************************************
361* Error handling 363* Error handling
362******************************************************/ 364******************************************************/
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
index ea9e9915ad35..96013030fcc7 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/rx.c
@@ -455,6 +455,10 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
455 455
456 /* Stop Rx DMA */ 456 /* Stop Rx DMA */
457 iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); 457 iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
458 /* reset and flush pointers */
459 iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0);
460 iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0);
461 iwl_write_direct32(trans, FH_RSCSR_CHNL0_RDPTR, 0);
458 462
459 /* Reset driver's Rx queue write index */ 463 /* Reset driver's Rx queue write index */
460 iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); 464 iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
@@ -491,7 +495,6 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
491{ 495{
492 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 496 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
493 struct iwl_rxq *rxq = &trans_pcie->rxq; 497 struct iwl_rxq *rxq = &trans_pcie->rxq;
494
495 int i, err; 498 int i, err;
496 unsigned long flags; 499 unsigned long flags;
497 500
@@ -518,6 +521,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
518 rxq->read = rxq->write = 0; 521 rxq->read = rxq->write = 0;
519 rxq->write_actual = 0; 522 rxq->write_actual = 0;
520 rxq->free_count = 0; 523 rxq->free_count = 0;
524 memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
521 spin_unlock_irqrestore(&rxq->lock, flags); 525 spin_unlock_irqrestore(&rxq->lock, flags);
522 526
523 iwl_pcie_rx_replenish(trans); 527 iwl_pcie_rx_replenish(trans);
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 4028c03eff5f..59340ff13a64 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -75,21 +75,16 @@
75#include "iwl-agn-hw.h" 75#include "iwl-agn-hw.h"
76#include "internal.h" 76#include "internal.h"
77 77
78static void iwl_pcie_set_pwr_vmain(struct iwl_trans *trans) 78static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
79{ 79{
80/* 80 if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
81 * (for documentation purposes) 81 iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
82 * to set power to V_AUX, do: 82 APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
83 83 ~APMG_PS_CTRL_MSK_PWR_SRC);
84 if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) 84 else
85 iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, 85 iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
86 APMG_PS_CTRL_VAL_PWR_SRC_VAUX, 86 APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
87 ~APMG_PS_CTRL_MSK_PWR_SRC); 87 ~APMG_PS_CTRL_MSK_PWR_SRC);
88 */
89
90 iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
91 APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
92 ~APMG_PS_CTRL_MSK_PWR_SRC);
93} 88}
94 89
95/* PCI registers */ 90/* PCI registers */
@@ -259,7 +254,7 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans)
259 254
260 spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); 255 spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
261 256
262 iwl_pcie_set_pwr_vmain(trans); 257 iwl_pcie_set_pwr(trans, false);
263 258
264 iwl_op_mode_nic_config(trans->op_mode); 259 iwl_op_mode_nic_config(trans->op_mode);
265 260
@@ -545,15 +540,76 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
545 clear_bit(STATUS_RFKILL, &trans_pcie->status); 540 clear_bit(STATUS_RFKILL, &trans_pcie->status);
546} 541}
547 542
548static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) 543static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans)
549{ 544{
550 /* let the ucode operate on its own */ 545 /* let the ucode operate on its own */
551 iwl_write32(trans, CSR_UCODE_DRV_GP1_SET, 546 iwl_write32(trans, CSR_UCODE_DRV_GP1_SET,
552 CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); 547 CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
553 548
554 iwl_disable_interrupts(trans); 549 iwl_disable_interrupts(trans);
550 iwl_pcie_disable_ict(trans);
551
555 iwl_clear_bit(trans, CSR_GP_CNTRL, 552 iwl_clear_bit(trans, CSR_GP_CNTRL,
556 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 553 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
554 iwl_clear_bit(trans, CSR_GP_CNTRL,
555 CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
556
557 /*
558 * reset TX queues -- some of their registers reset during S3
559 * so if we don't reset everything here the D3 image would try
560 * to execute some invalid memory upon resume
561 */
562 iwl_trans_pcie_tx_reset(trans);
563
564 iwl_pcie_set_pwr(trans, true);
565}
566
567static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
568 enum iwl_d3_status *status)
569{
570 u32 val;
571 int ret;
572
573 iwl_pcie_set_pwr(trans, false);
574
575 val = iwl_read32(trans, CSR_RESET);
576 if (val & CSR_RESET_REG_FLAG_NEVO_RESET) {
577 *status = IWL_D3_STATUS_RESET;
578 return 0;
579 }
580
581 /*
582 * Also enables interrupts - none will happen as the device doesn't
583 * know we're waking it up, only when the opmode actually tells it
584 * after this call.
585 */
586 iwl_pcie_reset_ict(trans);
587
588 iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
589 iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
590
591 ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
592 CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
593 CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
594 25000);
595 if (ret) {
596 IWL_ERR(trans, "Failed to resume the device (mac ready)\n");
597 return ret;
598 }
599
600 iwl_trans_pcie_tx_reset(trans);
601
602 ret = iwl_pcie_rx_init(trans);
603 if (ret) {
604 IWL_ERR(trans, "Failed to resume the device (RX reset)\n");
605 return ret;
606 }
607
608 iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
609 CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
610
611 *status = IWL_D3_STATUS_ALIVE;
612 return 0;
557} 613}
558 614
559static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) 615static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
@@ -1279,7 +1335,8 @@ static const struct iwl_trans_ops trans_ops_pcie = {
1279 .start_fw = iwl_trans_pcie_start_fw, 1335 .start_fw = iwl_trans_pcie_start_fw,
1280 .stop_device = iwl_trans_pcie_stop_device, 1336 .stop_device = iwl_trans_pcie_stop_device,
1281 1337
1282 .wowlan_suspend = iwl_trans_pcie_wowlan_suspend, 1338 .d3_suspend = iwl_trans_pcie_d3_suspend,
1339 .d3_resume = iwl_trans_pcie_d3_resume,
1283 1340
1284 .send_cmd = iwl_trans_pcie_send_hcmd, 1341 .send_cmd = iwl_trans_pcie_send_hcmd,
1285 1342
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index d3c4e8f017ef..fd3280fca445 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -692,6 +692,29 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
692 APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 692 APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
693} 693}
694 694
695void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)
696{
697 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
698 int txq_id;
699
700 for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
701 txq_id++) {
702 struct iwl_txq *txq = &trans_pcie->txq[txq_id];
703
704 iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id),
705 txq->q.dma_addr >> 8);
706 iwl_pcie_txq_unmap(trans, txq_id);
707 txq->q.read_ptr = 0;
708 txq->q.write_ptr = 0;
709 }
710
711 /* Tell NIC where to find the "keep warm" buffer */
712 iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG,
713 trans_pcie->kw.dma >> 4);
714
715 iwl_pcie_tx_start(trans, trans_pcie->scd_base_addr);
716}
717
695/* 718/*
696 * iwl_pcie_tx_stop - Stop all Tx DMA channels 719 * iwl_pcie_tx_stop - Stop all Tx DMA channels
697 */ 720 */