ps.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * This file is part of wl1251
  4. *
  5. * Copyright (C) 2008 Nokia Corporation
  6. */
  7. #include "reg.h"
  8. #include "ps.h"
  9. #include "cmd.h"
  10. #include "io.h"
  11. /* in ms */
  12. #define WL1251_WAKEUP_TIMEOUT 100
  13. void wl1251_elp_work(struct work_struct *work)
  14. {
  15. struct delayed_work *dwork;
  16. struct wl1251 *wl;
  17. dwork = to_delayed_work(work);
  18. wl = container_of(dwork, struct wl1251, elp_work);
  19. wl1251_debug(DEBUG_PSM, "elp work");
  20. mutex_lock(&wl->mutex);
  21. if (wl->elp || wl->station_mode == STATION_ACTIVE_MODE)
  22. goto out;
  23. wl1251_debug(DEBUG_PSM, "chip to elp");
  24. wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
  25. wl->elp = true;
  26. out:
  27. mutex_unlock(&wl->mutex);
  28. }
  29. #define ELP_ENTRY_DELAY 5
  30. /* Routines to toggle sleep mode while in ELP */
  31. void wl1251_ps_elp_sleep(struct wl1251 *wl)
  32. {
  33. unsigned long delay;
  34. if (wl->station_mode != STATION_ACTIVE_MODE) {
  35. delay = msecs_to_jiffies(ELP_ENTRY_DELAY);
  36. ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay);
  37. }
  38. }
  39. int wl1251_ps_elp_wakeup(struct wl1251 *wl)
  40. {
  41. unsigned long timeout, start;
  42. u32 elp_reg;
  43. cancel_delayed_work(&wl->elp_work);
  44. if (!wl->elp)
  45. return 0;
  46. wl1251_debug(DEBUG_PSM, "waking up chip from elp");
  47. start = jiffies;
  48. timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT);
  49. wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
  50. elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
  51. /*
  52. * FIXME: we should wait for irq from chip but, as a temporary
  53. * solution to simplify locking, let's poll instead
  54. */
  55. while (!(elp_reg & ELPCTRL_WLAN_READY)) {
  56. if (time_after(jiffies, timeout)) {
  57. wl1251_error("elp wakeup timeout");
  58. return -ETIMEDOUT;
  59. }
  60. msleep(1);
  61. elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
  62. }
  63. wl1251_debug(DEBUG_PSM, "wakeup time: %u ms",
  64. jiffies_to_msecs(jiffies - start));
  65. wl->elp = false;
  66. return 0;
  67. }
  68. int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_station_mode mode)
  69. {
  70. int ret;
  71. switch (mode) {
  72. case STATION_POWER_SAVE_MODE:
  73. wl1251_debug(DEBUG_PSM, "entering psm");
  74. /* enable beacon filtering */
  75. ret = wl1251_acx_beacon_filter_opt(wl, true);
  76. if (ret < 0)
  77. return ret;
  78. ret = wl1251_acx_wake_up_conditions(wl,
  79. WAKE_UP_EVENT_DTIM_BITMAP,
  80. wl->listen_int);
  81. if (ret < 0)
  82. return ret;
  83. ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_ENABLE,
  84. WL1251_DEFAULT_BET_CONSECUTIVE);
  85. if (ret < 0)
  86. return ret;
  87. ret = wl1251_cmd_ps_mode(wl, CHIP_POWER_SAVE_MODE);
  88. if (ret < 0)
  89. return ret;
  90. ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
  91. if (ret < 0)
  92. return ret;
  93. break;
  94. case STATION_IDLE:
  95. wl1251_debug(DEBUG_PSM, "entering idle");
  96. ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
  97. if (ret < 0)
  98. return ret;
  99. ret = wl1251_cmd_template_set(wl, CMD_DISCONNECT, NULL, 0);
  100. if (ret < 0)
  101. return ret;
  102. break;
  103. case STATION_ACTIVE_MODE:
  104. default:
  105. wl1251_debug(DEBUG_PSM, "leaving psm");
  106. ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
  107. if (ret < 0)
  108. return ret;
  109. /* disable BET */
  110. ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_DISABLE,
  111. WL1251_DEFAULT_BET_CONSECUTIVE);
  112. if (ret < 0)
  113. return ret;
  114. /* disable beacon filtering */
  115. ret = wl1251_acx_beacon_filter_opt(wl, false);
  116. if (ret < 0)
  117. return ret;
  118. ret = wl1251_acx_wake_up_conditions(wl,
  119. WAKE_UP_EVENT_DTIM_BITMAP,
  120. wl->listen_int);
  121. if (ret < 0)
  122. return ret;
  123. ret = wl1251_cmd_ps_mode(wl, CHIP_ACTIVE_MODE);
  124. if (ret < 0)
  125. return ret;
  126. break;
  127. }
  128. wl->station_mode = mode;
  129. return ret;
  130. }