iwlwifi: add very first D0i3 support
authorArik Nemtsov <arik@wizery.com>
Wed, 2 Oct 2013 13:58:09 +0000 (16:58 +0300)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Mon, 3 Feb 2014 20:23:39 +0000 (22:23 +0200)
When the bus is in D0i3, we can't send regular commands to
the firmware. This means that we need to add a state to
remember what is our d0i3 state and make sure that only
d0i3 exit commands can be sent.
Add flags to CMD_ flags and transport status for this
purpose.

Commands with CMD_HIGH_PRIO set are queued at the head of
the command queue, behind other high priority commands.

Commands with CMD_SEND_IN_IDLE set can be sent while the
transport is idle (without taking rpm reference).

Commands with CMD_MAKE_TRANS_IDLE set indicate that command
completion should mark the transport as idle (and release
the bus).

Commands with CMD_WAKE_UP_TRANS set instruct the transport
to exit from idle when this command is completed.

The transport is marked as idle (STATUS_TRANS_IDLE) when
the FW enters D0i3 state. This bit is cleared when it
enters D0 state again.

Process only commands with CMD_SEND_IN_IDLE flag while the
transport is idle. Other enqueued commands will be
processed only later, right after exiting D0i3.

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Eliad Peller <eliadx.peller@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/ops.c

index 1b2ac31..7b19274 100644 (file)
@@ -193,12 +193,23 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
  * @CMD_ASYNC: Return right away and don't wait for the response
  * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the
  *     response. The caller needs to call iwl_free_resp when done.
+ * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
+ *     command queue, but after other high priority commands. valid only
+ *     with CMD_ASYNC.
+ * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
+ * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
+ * @CMD_WAKE_UP_TRANS: The command response should wake up the trans
+ *     (i.e. mark it as non-idle).
  */
 enum CMD_MODE {
        CMD_SYNC                = 0,
        CMD_ASYNC               = BIT(0),
        CMD_WANT_SKB            = BIT(1),
        CMD_SEND_IN_RFKILL      = BIT(2),
+       CMD_HIGH_PRIO           = BIT(3),
+       CMD_SEND_IN_IDLE        = BIT(4),
+       CMD_MAKE_TRANS_IDLE     = BIT(5),
+       CMD_WAKE_UP_TRANS       = BIT(6),
 };
 
 #define DEF_CMD_PAYLOAD_SIZE 320
@@ -335,6 +346,9 @@ enum iwl_d3_status {
  * @STATUS_INT_ENABLED: interrupts are enabled
  * @STATUS_RFKILL: the HW RFkill switch is in KILL position
  * @STATUS_FW_ERROR: the fw is in error state
+ * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands
+ *     are sent
+ * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
  */
 enum iwl_trans_status {
        STATUS_SYNC_HCMD_ACTIVE,
@@ -343,6 +357,8 @@ enum iwl_trans_status {
        STATUS_INT_ENABLED,
        STATUS_RFKILL,
        STATUS_FW_ERROR,
+       STATUS_TRANS_GOING_IDLE,
+       STATUS_TRANS_IDLE,
 };
 
 /**
index 32844e3..3bf5f82 100644 (file)
@@ -199,6 +199,7 @@ enum {
        PROT_OFFLOAD_CONFIG_CMD = 0xd4,
        OFFLOADS_QUERY_CMD = 0xd5,
        REMOTE_WAKE_CONFIG_CMD = 0xd6,
+       D0I3_END_CMD = 0xed,
 
        /* for WoWLAN in particular */
        WOWLAN_PATTERNS = 0xe0,
index 0f87bc3..6e46b7c 100644 (file)
@@ -291,6 +291,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(REDUCE_TX_POWER_CMD),
        CMD(TX_ANT_CONFIGURATION_CMD),
        CMD(D3_CONFIG_CMD),
+       CMD(D0I3_END_CMD),
        CMD(PROT_OFFLOAD_CONFIG_CMD),
        CMD(OFFLOADS_QUERY_CMD),
        CMD(REMOTE_WAKE_CONFIG_CMD),
@@ -815,17 +816,27 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
 static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
+       struct iwl_d3_manager_config d3_cfg_cmd = {
+               .min_sleep_time = cpu_to_le32(1000),
+       };
 
        IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n");
-       return 0;
+
+       return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD,
+                                   flags | CMD_MAKE_TRANS_IDLE,
+                                   sizeof(d3_cfg_cmd), &d3_cfg_cmd);
 }
 
 static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE |
+                   CMD_WAKE_UP_TRANS;
 
        IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
-       return 0;
+
+       return iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
 }
 
 static const struct iwl_op_mode_ops iwl_mvm_ops = {