diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-01-08 05:25:44 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-01-16 08:19:54 -0500 |
commit | ddaf5a5b300b8f9d3591b509fd8bedab1c9887be (patch) | |
tree | c78ebe39d33bf334e120d08c3206a5b048f8fcea /drivers/net/wireless/iwlwifi/pcie | |
parent | 22dc3c9561825a7c2cd18d01b01358c2141a8e16 (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/wireless/iwlwifi/pcie')
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/internal.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/rx.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/trans.c | 91 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/tx.c | 23 |
4 files changed, 104 insertions, 18 deletions
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); |
358 | void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, | 358 | void 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); |
360 | void 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 | ||
78 | static void iwl_pcie_set_pwr_vmain(struct iwl_trans *trans) | 78 | static 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 | ||
548 | static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) | 543 | static 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 | |||
567 | static 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 | ||
559 | static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) | 615 | static 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 | ||
695 | void 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 | */ |