ath6kl: fix open parenthesis alignment in ath6kl_sdio_suspend()
[cascardo/linux.git] / drivers / net / wireless / ath / ath6kl / sdio.c
index 9475e2d..5352864 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -31,6 +32,7 @@
 struct ath6kl_sdio {
        struct sdio_func *func;
 
+       /* protects access to bus_req_freeq */
        spinlock_t lock;
 
        /* free list */
@@ -49,14 +51,20 @@ struct ath6kl_sdio {
        /* scatter request list head */
        struct list_head scat_req;
 
+       atomic_t irq_handling;
+       wait_queue_head_t irq_wq;
+
+       /* protects access to scat_req */
        spinlock_t scat_lock;
+
        bool scatter_enabled;
 
        bool is_disabled;
-       atomic_t irq_handling;
        const struct sdio_device_id *id;
        struct work_struct wr_async_work;
        struct list_head wr_asyncq;
+
+       /* protects access to wr_asyncq */
        spinlock_t wr_async_lock;
 };
 
@@ -402,7 +410,10 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
                        return -ENOMEM;
                mutex_lock(&ar_sdio->dma_buffer_mutex);
                tbuf = ar_sdio->dma_buffer;
-               memcpy(tbuf, buf, len);
+
+               if (request & HIF_WRITE)
+                       memcpy(tbuf, buf, len);
+
                bounced = true;
        } else
                tbuf = buf;
@@ -461,7 +472,6 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func)
 
        ar_sdio = sdio_get_drvdata(func);
        atomic_set(&ar_sdio->irq_handling, 1);
-
        /*
         * Release the host during interrups so we can pick it back up when
         * we process commands.
@@ -470,7 +480,10 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func)
 
        status = ath6kl_hif_intr_bh_handler(ar_sdio->ar);
        sdio_claim_host(ar_sdio->func);
+
        atomic_set(&ar_sdio->irq_handling, 0);
+       wake_up(&ar_sdio->irq_wq);
+
        WARN_ON(status && status != -ECANCELED);
 }
 
@@ -571,6 +584,13 @@ static void ath6kl_sdio_irq_enable(struct ath6kl *ar)
        sdio_release_host(ar_sdio->func);
 }
 
+static bool ath6kl_sdio_is_on_irq(struct ath6kl *ar)
+{
+       struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+
+       return !atomic_read(&ar_sdio->irq_handling);
+}
+
 static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
 {
        struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
@@ -578,10 +598,14 @@ static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
 
        sdio_claim_host(ar_sdio->func);
 
-       /* Mask our function IRQ */
-       while (atomic_read(&ar_sdio->irq_handling)) {
+       if (atomic_read(&ar_sdio->irq_handling)) {
                sdio_release_host(ar_sdio->func);
-               schedule_timeout(HZ / 10);
+
+               ret = wait_event_interruptible(ar_sdio->irq_wq,
+                                              ath6kl_sdio_is_on_irq(ar));
+               if (ret)
+                       return;
+
                sdio_claim_host(ar_sdio->func);
        }
 
@@ -603,6 +627,8 @@ static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar)
                node = list_first_entry(&ar_sdio->scat_req,
                                        struct hif_scatter_req, list);
                list_del(&node->list);
+
+               node->scat_q_depth = get_queue_depth(&ar_sdio->scat_req);
        }
 
        spin_unlock_bh(&ar_sdio->scat_lock);
@@ -635,8 +661,8 @@ static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar,
                return -EINVAL;
 
        ath6kl_dbg(ATH6KL_DBG_SCATTER,
-               "hif-scatter: total len: %d scatter entries: %d\n",
-               scat_req->len, scat_req->scat_entries);
+                  "hif-scatter: total len: %d scatter entries: %d\n",
+                  scat_req->len, scat_req->scat_entries);
 
        if (request & HIF_SYNCHRONOUS)
                status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest);
@@ -772,7 +798,6 @@ static int ath6kl_sdio_config(struct ath6kl *ar)
        if (ret) {
                ath6kl_err("Set sdio block size %d failed: %d)\n",
                           HIF_MBOX_BLOCK_SIZE, ret);
-               sdio_release_host(func);
                goto out;
        }
 
@@ -782,7 +807,7 @@ out:
        return ret;
 }
 
-static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar)
 {
        struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
        struct sdio_func *func = ar_sdio->func;
@@ -793,60 +818,104 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
 
        ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags);
 
-       if (!(flags & MMC_PM_KEEP_POWER) ||
-           (ar->conf_flags & ATH6KL_CONF_SUSPEND_CUTPOWER)) {
-               /* as host doesn't support keep power we need to cut power */
-               return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER,
-                                              NULL);
-       }
+       if (!(flags & MMC_PM_WAKE_SDIO_IRQ) ||
+           !(flags & MMC_PM_KEEP_POWER))
+               return -EINVAL;
 
        ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
        if (ret) {
-               printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n",
-                      ret);
+               ath6kl_err("set sdio keep pwr flag failed: %d\n", ret);
                return ret;
        }
 
-       if (!(flags & MMC_PM_WAKE_SDIO_IRQ))
-               goto deepsleep;
-
        /* sdio irq wakes up host */
+       ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+       if (ret)
+               ath6kl_err("set sdio wake irq flag failed: %d\n", ret);
+
+       return ret;
+}
+
+static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+       struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+       struct sdio_func *func = ar_sdio->func;
+       mmc_pm_flag_t flags;
+       bool try_deepsleep = false;
+       int ret;
 
        if (ar->state == ATH6KL_STATE_SCHED_SCAN) {
+               ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan is in progress\n");
+
+               ret = ath6kl_set_sdio_pm_caps(ar);
+               if (ret)
+                       goto cut_pwr;
+
                ret =  ath6kl_cfg80211_suspend(ar,
                                               ATH6KL_CFG_SUSPEND_SCHED_SCAN,
                                               NULL);
-               if (ret) {
-                       ath6kl_warn("Schedule scan suspend failed: %d", ret);
-                       return ret;
-               }
+               if (ret)
+                       goto cut_pwr;
+
+               return 0;
+       }
+
+       if (ar->suspend_mode == WLAN_POWER_STATE_WOW ||
+           (!ar->suspend_mode && wow)) {
 
-               ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+               ret = ath6kl_set_sdio_pm_caps(ar);
                if (ret)
-                       ath6kl_warn("set sdio wake irq flag failed: %d\n", ret);
+                       goto cut_pwr;
 
-               return ret;
+               ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow);
+               if (ret && ret != -ENOTCONN)
+                       ath6kl_err("wow suspend failed: %d\n", ret);
+
+               if (ret &&
+                   (!ar->wow_suspend_mode ||
+                    ar->wow_suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP))
+                       try_deepsleep = true;
+               else if (ret &&
+                        ar->wow_suspend_mode == WLAN_POWER_STATE_CUT_PWR)
+                       goto cut_pwr;
+               if (!ret)
+                       return 0;
        }
 
-       if (wow) {
+       if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP ||
+           !ar->suspend_mode || try_deepsleep) {
+
+               flags = sdio_get_host_pm_caps(func);
+               if (!(flags & MMC_PM_KEEP_POWER))
+                       goto cut_pwr;
+
+               ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+               if (ret)
+                       goto cut_pwr;
+
                /*
-                * The host sdio controller is capable of keep power and
-                * sdio irq wake up at this point. It's fine to continue
-                * wow suspend operation.
+                * Workaround to support Deep Sleep with MSM, set the host pm
+                * flag as MMC_PM_WAKE_SDIO_IRQ to allow SDCC deiver to disable
+                * the sdc2_clock and internally allows MSM to enter
+                * TCXO shutdown properly.
                 */
-               ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow);
-               if (ret)
-                       return ret;
+               if ((flags & MMC_PM_WAKE_SDIO_IRQ)) {
+                       ret = sdio_set_host_pm_flags(func,
+                                               MMC_PM_WAKE_SDIO_IRQ);
+                       if (ret)
+                               goto cut_pwr;
+               }
 
-               ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+               ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP,
+                                             NULL);
                if (ret)
-                       ath6kl_err("set sdio wake irq flag failed: %d\n", ret);
+                       goto cut_pwr;
 
-               return ret;
+               return 0;
        }
 
-deepsleep:
-       return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL);
+cut_pwr:
+       return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL);
 }
 
 static int ath6kl_sdio_resume(struct ath6kl *ar)
@@ -869,8 +938,15 @@ static int ath6kl_sdio_resume(struct ath6kl *ar)
 
        case ATH6KL_STATE_WOW:
                break;
+
        case ATH6KL_STATE_SCHED_SCAN:
                break;
+
+       case ATH6KL_STATE_SUSPENDING:
+               break;
+
+       case ATH6KL_STATE_RESUMING:
+               break;
        }
 
        ath6kl_cfg80211_resume(ar);
@@ -949,7 +1025,7 @@ static int ath6kl_sdio_diag_read32(struct ath6kl *ar, u32 address, u32 *data)
                                (u8 *)data, sizeof(u32), HIF_RD_SYNC_BYTE_INC);
        if (status) {
                ath6kl_err("%s: failed to read from window data addr\n",
-                       __func__);
+                          __func__);
                return status;
        }
 
@@ -1260,10 +1336,12 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
 
        INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work);
 
+       init_waitqueue_head(&ar_sdio->irq_wq);
+
        for (count = 0; count < BUS_REQUEST_MAX_NUM; count++)
                ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]);
 
-       ar = ath6kl_core_alloc(&ar_sdio->func->dev);
+       ar = ath6kl_core_create(&ar_sdio->func->dev);
        if (!ar) {
                ath6kl_err("Failed to alloc ath6kl core\n");
                ret = -ENOMEM;
@@ -1293,7 +1371,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
        return ret;
 
 err_core_alloc:
-       ath6kl_core_free(ar_sdio->ar);
+       ath6kl_core_destroy(ar_sdio->ar);
 err_dma:
        kfree(ar_sdio->dma_buffer);
 err_hif:
@@ -1316,6 +1394,7 @@ static void ath6kl_sdio_remove(struct sdio_func *func)
        cancel_work_sync(&ar_sdio->wr_async_work);
 
        ath6kl_core_cleanup(ar_sdio->ar);
+       ath6kl_core_destroy(ar_sdio->ar);
 
        kfree(ar_sdio->dma_buffer);
        kfree(ar_sdio);
@@ -1332,7 +1411,7 @@ static const struct sdio_device_id ath6kl_sdio_devices[] = {
 MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices);
 
 static struct sdio_driver ath6kl_sdio_driver = {
-       .name = "ath6kl",
+       .name = "ath6kl_sdio",
        .id_table = ath6kl_sdio_devices,
        .probe = ath6kl_sdio_probe,
        .remove = ath6kl_sdio_remove,
@@ -1362,19 +1441,19 @@ MODULE_AUTHOR("Atheros Communications, Inc.");
 MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices");
 MODULE_LICENSE("Dual BSD/GPL");
 
-MODULE_FIRMWARE(AR6003_HW_2_0_OTP_FILE);
-MODULE_FIRMWARE(AR6003_HW_2_0_FIRMWARE_FILE);
-MODULE_FIRMWARE(AR6003_HW_2_0_PATCH_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_OTP_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_PATCH_FILE);
 MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE);
 MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE);
-MODULE_FIRMWARE(AR6003_HW_2_1_1_OTP_FILE);
-MODULE_FIRMWARE(AR6003_HW_2_1_1_FIRMWARE_FILE);
-MODULE_FIRMWARE(AR6003_HW_2_1_1_PATCH_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_OTP_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_PATCH_FILE);
 MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE);
 MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE);
-MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_FIRMWARE_FILE);
 MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE);
 MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE);
-MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE);
 MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE);
 MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE);