diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/sdio.c')
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/sdio.c | 140 |
1 files changed, 87 insertions, 53 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 9475e2d0d0b7..4febee723495 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c | |||
@@ -49,11 +49,13 @@ struct ath6kl_sdio { | |||
49 | /* scatter request list head */ | 49 | /* scatter request list head */ |
50 | struct list_head scat_req; | 50 | struct list_head scat_req; |
51 | 51 | ||
52 | /* Avoids disabling irq while the interrupts being handled */ | ||
53 | struct mutex mtx_irq; | ||
54 | |||
52 | spinlock_t scat_lock; | 55 | spinlock_t scat_lock; |
53 | bool scatter_enabled; | 56 | bool scatter_enabled; |
54 | 57 | ||
55 | bool is_disabled; | 58 | bool is_disabled; |
56 | atomic_t irq_handling; | ||
57 | const struct sdio_device_id *id; | 59 | const struct sdio_device_id *id; |
58 | struct work_struct wr_async_work; | 60 | struct work_struct wr_async_work; |
59 | struct list_head wr_asyncq; | 61 | struct list_head wr_asyncq; |
@@ -460,8 +462,7 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func) | |||
460 | ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n"); | 462 | ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n"); |
461 | 463 | ||
462 | ar_sdio = sdio_get_drvdata(func); | 464 | ar_sdio = sdio_get_drvdata(func); |
463 | atomic_set(&ar_sdio->irq_handling, 1); | 465 | mutex_lock(&ar_sdio->mtx_irq); |
464 | |||
465 | /* | 466 | /* |
466 | * Release the host during interrups so we can pick it back up when | 467 | * Release the host during interrups so we can pick it back up when |
467 | * we process commands. | 468 | * we process commands. |
@@ -470,7 +471,7 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func) | |||
470 | 471 | ||
471 | status = ath6kl_hif_intr_bh_handler(ar_sdio->ar); | 472 | status = ath6kl_hif_intr_bh_handler(ar_sdio->ar); |
472 | sdio_claim_host(ar_sdio->func); | 473 | sdio_claim_host(ar_sdio->func); |
473 | atomic_set(&ar_sdio->irq_handling, 0); | 474 | mutex_unlock(&ar_sdio->mtx_irq); |
474 | WARN_ON(status && status != -ECANCELED); | 475 | WARN_ON(status && status != -ECANCELED); |
475 | } | 476 | } |
476 | 477 | ||
@@ -578,17 +579,14 @@ static void ath6kl_sdio_irq_disable(struct ath6kl *ar) | |||
578 | 579 | ||
579 | sdio_claim_host(ar_sdio->func); | 580 | sdio_claim_host(ar_sdio->func); |
580 | 581 | ||
581 | /* Mask our function IRQ */ | 582 | mutex_lock(&ar_sdio->mtx_irq); |
582 | while (atomic_read(&ar_sdio->irq_handling)) { | ||
583 | sdio_release_host(ar_sdio->func); | ||
584 | schedule_timeout(HZ / 10); | ||
585 | sdio_claim_host(ar_sdio->func); | ||
586 | } | ||
587 | 583 | ||
588 | ret = sdio_release_irq(ar_sdio->func); | 584 | ret = sdio_release_irq(ar_sdio->func); |
589 | if (ret) | 585 | if (ret) |
590 | ath6kl_err("Failed to release sdio irq: %d\n", ret); | 586 | ath6kl_err("Failed to release sdio irq: %d\n", ret); |
591 | 587 | ||
588 | mutex_unlock(&ar_sdio->mtx_irq); | ||
589 | |||
592 | sdio_release_host(ar_sdio->func); | 590 | sdio_release_host(ar_sdio->func); |
593 | } | 591 | } |
594 | 592 | ||
@@ -772,7 +770,6 @@ static int ath6kl_sdio_config(struct ath6kl *ar) | |||
772 | if (ret) { | 770 | if (ret) { |
773 | ath6kl_err("Set sdio block size %d failed: %d)\n", | 771 | ath6kl_err("Set sdio block size %d failed: %d)\n", |
774 | HIF_MBOX_BLOCK_SIZE, ret); | 772 | HIF_MBOX_BLOCK_SIZE, ret); |
775 | sdio_release_host(func); | ||
776 | goto out; | 773 | goto out; |
777 | } | 774 | } |
778 | 775 | ||
@@ -782,7 +779,7 @@ out: | |||
782 | return ret; | 779 | return ret; |
783 | } | 780 | } |
784 | 781 | ||
785 | static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) | 782 | static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar) |
786 | { | 783 | { |
787 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | 784 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); |
788 | struct sdio_func *func = ar_sdio->func; | 785 | struct sdio_func *func = ar_sdio->func; |
@@ -793,60 +790,95 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) | |||
793 | 790 | ||
794 | ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags); | 791 | ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags); |
795 | 792 | ||
796 | if (!(flags & MMC_PM_KEEP_POWER) || | 793 | if (!(flags & MMC_PM_WAKE_SDIO_IRQ) || |
797 | (ar->conf_flags & ATH6KL_CONF_SUSPEND_CUTPOWER)) { | 794 | !(flags & MMC_PM_KEEP_POWER)) |
798 | /* as host doesn't support keep power we need to cut power */ | 795 | return -EINVAL; |
799 | return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, | ||
800 | NULL); | ||
801 | } | ||
802 | 796 | ||
803 | ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); | 797 | ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); |
804 | if (ret) { | 798 | if (ret) { |
805 | printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n", | 799 | ath6kl_err("set sdio keep pwr flag failed: %d\n", ret); |
806 | ret); | ||
807 | return ret; | 800 | return ret; |
808 | } | 801 | } |
809 | 802 | ||
810 | if (!(flags & MMC_PM_WAKE_SDIO_IRQ)) | ||
811 | goto deepsleep; | ||
812 | |||
813 | /* sdio irq wakes up host */ | 803 | /* sdio irq wakes up host */ |
804 | ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); | ||
805 | if (ret) | ||
806 | ath6kl_err("set sdio wake irq flag failed: %d\n", ret); | ||
807 | |||
808 | return ret; | ||
809 | } | ||
810 | |||
811 | static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) | ||
812 | { | ||
813 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
814 | struct sdio_func *func = ar_sdio->func; | ||
815 | mmc_pm_flag_t flags; | ||
816 | int ret; | ||
814 | 817 | ||
815 | if (ar->state == ATH6KL_STATE_SCHED_SCAN) { | 818 | if (ar->state == ATH6KL_STATE_SCHED_SCAN) { |
819 | ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan is in progress\n"); | ||
820 | |||
821 | ret = ath6kl_set_sdio_pm_caps(ar); | ||
822 | if (ret) | ||
823 | goto cut_pwr; | ||
824 | |||
816 | ret = ath6kl_cfg80211_suspend(ar, | 825 | ret = ath6kl_cfg80211_suspend(ar, |
817 | ATH6KL_CFG_SUSPEND_SCHED_SCAN, | 826 | ATH6KL_CFG_SUSPEND_SCHED_SCAN, |
818 | NULL); | 827 | NULL); |
819 | if (ret) { | 828 | if (ret) |
820 | ath6kl_warn("Schedule scan suspend failed: %d", ret); | 829 | goto cut_pwr; |
821 | return ret; | 830 | |
822 | } | 831 | return 0; |
832 | } | ||
833 | |||
834 | if (ar->suspend_mode == WLAN_POWER_STATE_WOW || | ||
835 | (!ar->suspend_mode && wow)) { | ||
823 | 836 | ||
824 | ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); | 837 | ret = ath6kl_set_sdio_pm_caps(ar); |
825 | if (ret) | 838 | if (ret) |
826 | ath6kl_warn("set sdio wake irq flag failed: %d\n", ret); | 839 | goto cut_pwr; |
827 | 840 | ||
828 | return ret; | 841 | ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow); |
842 | if (ret) | ||
843 | goto cut_pwr; | ||
844 | |||
845 | return 0; | ||
829 | } | 846 | } |
830 | 847 | ||
831 | if (wow) { | 848 | if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP || |
849 | !ar->suspend_mode) { | ||
850 | |||
851 | flags = sdio_get_host_pm_caps(func); | ||
852 | if (!(flags & MMC_PM_KEEP_POWER)) | ||
853 | goto cut_pwr; | ||
854 | |||
855 | ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); | ||
856 | if (ret) | ||
857 | goto cut_pwr; | ||
858 | |||
832 | /* | 859 | /* |
833 | * The host sdio controller is capable of keep power and | 860 | * Workaround to support Deep Sleep with MSM, set the host pm |
834 | * sdio irq wake up at this point. It's fine to continue | 861 | * flag as MMC_PM_WAKE_SDIO_IRQ to allow SDCC deiver to disable |
835 | * wow suspend operation. | 862 | * the sdc2_clock and internally allows MSM to enter |
863 | * TCXO shutdown properly. | ||
836 | */ | 864 | */ |
837 | ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow); | 865 | if ((flags & MMC_PM_WAKE_SDIO_IRQ)) { |
838 | if (ret) | 866 | ret = sdio_set_host_pm_flags(func, |
839 | return ret; | 867 | MMC_PM_WAKE_SDIO_IRQ); |
868 | if (ret) | ||
869 | goto cut_pwr; | ||
870 | } | ||
840 | 871 | ||
841 | ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); | 872 | ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, |
873 | NULL); | ||
842 | if (ret) | 874 | if (ret) |
843 | ath6kl_err("set sdio wake irq flag failed: %d\n", ret); | 875 | goto cut_pwr; |
844 | 876 | ||
845 | return ret; | 877 | return 0; |
846 | } | 878 | } |
847 | 879 | ||
848 | deepsleep: | 880 | cut_pwr: |
849 | return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL); | 881 | return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL); |
850 | } | 882 | } |
851 | 883 | ||
852 | static int ath6kl_sdio_resume(struct ath6kl *ar) | 884 | static int ath6kl_sdio_resume(struct ath6kl *ar) |
@@ -1253,6 +1285,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, | |||
1253 | spin_lock_init(&ar_sdio->scat_lock); | 1285 | spin_lock_init(&ar_sdio->scat_lock); |
1254 | spin_lock_init(&ar_sdio->wr_async_lock); | 1286 | spin_lock_init(&ar_sdio->wr_async_lock); |
1255 | mutex_init(&ar_sdio->dma_buffer_mutex); | 1287 | mutex_init(&ar_sdio->dma_buffer_mutex); |
1288 | mutex_init(&ar_sdio->mtx_irq); | ||
1256 | 1289 | ||
1257 | INIT_LIST_HEAD(&ar_sdio->scat_req); | 1290 | INIT_LIST_HEAD(&ar_sdio->scat_req); |
1258 | INIT_LIST_HEAD(&ar_sdio->bus_req_freeq); | 1291 | INIT_LIST_HEAD(&ar_sdio->bus_req_freeq); |
@@ -1263,7 +1296,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, | |||
1263 | for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) | 1296 | for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) |
1264 | ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]); | 1297 | ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]); |
1265 | 1298 | ||
1266 | ar = ath6kl_core_alloc(&ar_sdio->func->dev); | 1299 | ar = ath6kl_core_create(&ar_sdio->func->dev); |
1267 | if (!ar) { | 1300 | if (!ar) { |
1268 | ath6kl_err("Failed to alloc ath6kl core\n"); | 1301 | ath6kl_err("Failed to alloc ath6kl core\n"); |
1269 | ret = -ENOMEM; | 1302 | ret = -ENOMEM; |
@@ -1293,7 +1326,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, | |||
1293 | return ret; | 1326 | return ret; |
1294 | 1327 | ||
1295 | err_core_alloc: | 1328 | err_core_alloc: |
1296 | ath6kl_core_free(ar_sdio->ar); | 1329 | ath6kl_core_destroy(ar_sdio->ar); |
1297 | err_dma: | 1330 | err_dma: |
1298 | kfree(ar_sdio->dma_buffer); | 1331 | kfree(ar_sdio->dma_buffer); |
1299 | err_hif: | 1332 | err_hif: |
@@ -1316,6 +1349,7 @@ static void ath6kl_sdio_remove(struct sdio_func *func) | |||
1316 | cancel_work_sync(&ar_sdio->wr_async_work); | 1349 | cancel_work_sync(&ar_sdio->wr_async_work); |
1317 | 1350 | ||
1318 | ath6kl_core_cleanup(ar_sdio->ar); | 1351 | ath6kl_core_cleanup(ar_sdio->ar); |
1352 | ath6kl_core_destroy(ar_sdio->ar); | ||
1319 | 1353 | ||
1320 | kfree(ar_sdio->dma_buffer); | 1354 | kfree(ar_sdio->dma_buffer); |
1321 | kfree(ar_sdio); | 1355 | kfree(ar_sdio); |
@@ -1332,7 +1366,7 @@ static const struct sdio_device_id ath6kl_sdio_devices[] = { | |||
1332 | MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices); | 1366 | MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices); |
1333 | 1367 | ||
1334 | static struct sdio_driver ath6kl_sdio_driver = { | 1368 | static struct sdio_driver ath6kl_sdio_driver = { |
1335 | .name = "ath6kl", | 1369 | .name = "ath6kl_sdio", |
1336 | .id_table = ath6kl_sdio_devices, | 1370 | .id_table = ath6kl_sdio_devices, |
1337 | .probe = ath6kl_sdio_probe, | 1371 | .probe = ath6kl_sdio_probe, |
1338 | .remove = ath6kl_sdio_remove, | 1372 | .remove = ath6kl_sdio_remove, |
@@ -1362,19 +1396,19 @@ MODULE_AUTHOR("Atheros Communications, Inc."); | |||
1362 | MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices"); | 1396 | MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices"); |
1363 | MODULE_LICENSE("Dual BSD/GPL"); | 1397 | MODULE_LICENSE("Dual BSD/GPL"); |
1364 | 1398 | ||
1365 | MODULE_FIRMWARE(AR6003_HW_2_0_OTP_FILE); | 1399 | MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_OTP_FILE); |
1366 | MODULE_FIRMWARE(AR6003_HW_2_0_FIRMWARE_FILE); | 1400 | MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_FIRMWARE_FILE); |
1367 | MODULE_FIRMWARE(AR6003_HW_2_0_PATCH_FILE); | 1401 | MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_PATCH_FILE); |
1368 | MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE); | 1402 | MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE); |
1369 | MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE); | 1403 | MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE); |
1370 | MODULE_FIRMWARE(AR6003_HW_2_1_1_OTP_FILE); | 1404 | MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_OTP_FILE); |
1371 | MODULE_FIRMWARE(AR6003_HW_2_1_1_FIRMWARE_FILE); | 1405 | MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_FIRMWARE_FILE); |
1372 | MODULE_FIRMWARE(AR6003_HW_2_1_1_PATCH_FILE); | 1406 | MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_PATCH_FILE); |
1373 | MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE); | 1407 | MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE); |
1374 | MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE); | 1408 | MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE); |
1375 | MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE); | 1409 | MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_FIRMWARE_FILE); |
1376 | MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); | 1410 | MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); |
1377 | MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); | 1411 | MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); |
1378 | MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE); | 1412 | MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE); |
1379 | MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); | 1413 | MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); |
1380 | MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); | 1414 | MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); |