12f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo/*
280301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valo * This file is part of wl1251
32f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo *
42f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * Copyright (C) 2008 Nokia Corporation
52f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo *
62f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * This program is free software; you can redistribute it and/or
72f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * modify it under the terms of the GNU General Public License
82f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * version 2 as published by the Free Software Foundation.
92f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo *
102f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * This program is distributed in the hope that it will be useful, but
112f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * WITHOUT ANY WARRANTY; without even the implied warranty of
122f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
132f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * General Public License for more details.
142f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo *
152f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * You should have received a copy of the GNU General Public License
162f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * along with this program; if not, write to the Free Software
172f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
182f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo * 02110-1301 USA
192f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo *
202f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo */
212f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
229bc6772e15d25f58c1be638031280e04514287d4Kalle Valo#include "reg.h"
239bc6772e15d25f58c1be638031280e04514287d4Kalle Valo#include "ps.h"
249bc6772e15d25f58c1be638031280e04514287d4Kalle Valo#include "cmd.h"
259bc6772e15d25f58c1be638031280e04514287d4Kalle Valo#include "io.h"
262f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
27b5a167942cb388cc5f1a6085cb3506d5bf47517dKalle Valo/* in ms */
28b5a167942cb388cc5f1a6085cb3506d5bf47517dKalle Valo#define WL1251_WAKEUP_TIMEOUT 100
292f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
30d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinenvoid wl1251_elp_work(struct work_struct *work)
312f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo{
32d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	struct delayed_work *dwork;
33d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	struct wl1251 *wl;
34d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen
35d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	dwork = container_of(work, struct delayed_work, work);
36d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	wl = container_of(dwork, struct wl1251, elp_work);
37d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen
38d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	wl1251_debug(DEBUG_PSM, "elp work");
39d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen
40d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	mutex_lock(&wl->mutex);
41d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen
42a0bbb58bcb70295ff05f870c93d34f9fbe614204Jarkko Nikula	if (wl->elp || wl->station_mode == STATION_ACTIVE_MODE)
43d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen		goto out;
442f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
4580301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valo	wl1251_debug(DEBUG_PSM, "chip to elp");
463f9e750d130b4a4d9f8226642b46ed17d8357f40Grazvydas Ignotas	wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
472f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	wl->elp = true;
48d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen
49d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinenout:
50d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	mutex_unlock(&wl->mutex);
51d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen}
52d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen
53d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen#define ELP_ENTRY_DELAY  5
54d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen
55d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen/* Routines to toggle sleep mode while in ELP */
56d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinenvoid wl1251_ps_elp_sleep(struct wl1251 *wl)
57d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen{
58d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	unsigned long delay;
59d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen
60a0bbb58bcb70295ff05f870c93d34f9fbe614204Jarkko Nikula	if (wl->station_mode != STATION_ACTIVE_MODE) {
61d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen		delay = msecs_to_jiffies(ELP_ENTRY_DELAY);
62d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen		ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay);
63d5da79ac1f5050cccaa68d814ccce292371f25faJuuso Oikarinen	}
642f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo}
652f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
6680301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valoint wl1251_ps_elp_wakeup(struct wl1251 *wl)
672f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo{
687fa6282a1775bd41508220e65ba0cb92235b67d4Kalle Valo	unsigned long timeout, start;
692f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	u32 elp_reg;
702f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
7102957f92bcc96be5c84a4000f9d22c592158602eTejun Heo	cancel_delayed_work(&wl->elp_work);
725f6722ee63a45d4ad3412743d161ec54d6c32cccGrazvydas Ignotas
732f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	if (!wl->elp)
742f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		return 0;
752f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
7680301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valo	wl1251_debug(DEBUG_PSM, "waking up chip from elp");
772f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
787fa6282a1775bd41508220e65ba0cb92235b67d4Kalle Valo	start = jiffies;
7980301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valo	timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT);
802f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
813f9e750d130b4a4d9f8226642b46ed17d8357f40Grazvydas Ignotas	wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
822f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
833f9e750d130b4a4d9f8226642b46ed17d8357f40Grazvydas Ignotas	elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
842f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
852f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	/*
862f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	 * FIXME: we should wait for irq from chip but, as a temporary
872f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	 * solution to simplify locking, let's poll instead
882f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	 */
892f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	while (!(elp_reg & ELPCTRL_WLAN_READY)) {
902f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		if (time_after(jiffies, timeout)) {
9180301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valo			wl1251_error("elp wakeup timeout");
922f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo			return -ETIMEDOUT;
932f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		}
942f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		msleep(1);
953f9e750d130b4a4d9f8226642b46ed17d8357f40Grazvydas Ignotas		elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
962f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	}
972f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
9880301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valo	wl1251_debug(DEBUG_PSM, "wakeup time: %u ms",
997fa6282a1775bd41508220e65ba0cb92235b67d4Kalle Valo		     jiffies_to_msecs(jiffies - start));
1002f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
1012f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	wl->elp = false;
1022f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
1032f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	return 0;
1042f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo}
1052f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
106a0bbb58bcb70295ff05f870c93d34f9fbe614204Jarkko Nikulaint wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_station_mode mode)
1072f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo{
1082f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	int ret;
1092f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
1102f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	switch (mode) {
1112f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	case STATION_POWER_SAVE_MODE:
11280301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valo		wl1251_debug(DEBUG_PSM, "entering psm");
1134a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo
1146b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen		/* enable beacon filtering */
1156b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen		ret = wl1251_acx_beacon_filter_opt(wl, true);
1166b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen		if (ret < 0)
1176b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen			return ret;
1186b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen
1194a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo		ret = wl1251_acx_wake_up_conditions(wl,
1204a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo						    WAKE_UP_EVENT_DTIM_BITMAP,
1214a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo						    wl->listen_int);
1224a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo		if (ret < 0)
1234a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo			return ret;
1244a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo
125c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt		ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_ENABLE,
126c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt					    WL1251_DEFAULT_BET_CONSECUTIVE);
127c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt		if (ret < 0)
128c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt			return ret;
129c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt
130a0bbb58bcb70295ff05f870c93d34f9fbe614204Jarkko Nikula		ret = wl1251_cmd_ps_mode(wl, CHIP_POWER_SAVE_MODE);
1312f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		if (ret < 0)
1322f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo			return ret;
1332f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
134d0c331aff99ca75f9aa0f794cf68b572d8ec7c5aGrazvydas Ignotas		ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
1352f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		if (ret < 0)
1362f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo			return ret;
1372f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		break;
1381e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula	case STATION_IDLE:
1391e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula		wl1251_debug(DEBUG_PSM, "entering idle");
1401e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula
1411e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula		ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
1421e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula		if (ret < 0)
1431e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula			return ret;
1441e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula
1451e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula		ret = wl1251_cmd_template_set(wl, CMD_DISCONNECT, NULL, 0);
1461e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula		if (ret < 0)
1471e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula			return ret;
1481e5f52de216a32986a5c3cbc358dbb2620a03047Jarkko Nikula		break;
1492f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	case STATION_ACTIVE_MODE:
1502f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	default:
15180301cdcfe44e3533175be23d7d52a9fc8c3fdb0Kalle Valo		wl1251_debug(DEBUG_PSM, "leaving psm");
152d0c331aff99ca75f9aa0f794cf68b572d8ec7c5aGrazvydas Ignotas
153d0c331aff99ca75f9aa0f794cf68b572d8ec7c5aGrazvydas Ignotas		ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
1542f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		if (ret < 0)
1552f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo			return ret;
1562f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
157c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt		/* disable BET */
158c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt		ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_DISABLE,
159c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt					    WL1251_DEFAULT_BET_CONSECUTIVE);
160c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt		if (ret < 0)
161c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt			return ret;
162c3e334d29484423e78009790a3d3fa78da8b43a1David Gnedt
1636b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen		/* disable beacon filtering */
1646b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen		ret = wl1251_acx_beacon_filter_opt(wl, false);
1656b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen		if (ret < 0)
1666b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen			return ret;
1676b21a2cd315e2e56a1748bd3ef9d910fe4f2e711Juuso Oikarinen
1684a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo		ret = wl1251_acx_wake_up_conditions(wl,
1694a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo						    WAKE_UP_EVENT_DTIM_BITMAP,
1704a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo						    wl->listen_int);
1714a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo		if (ret < 0)
1724a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo			return ret;
1734a8189227fc4718a767ffca74d13a7d552e42189Kalle Valo
174a0bbb58bcb70295ff05f870c93d34f9fbe614204Jarkko Nikula		ret = wl1251_cmd_ps_mode(wl, CHIP_ACTIVE_MODE);
1752f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		if (ret < 0)
1762f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo			return ret;
1772f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
1782f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo		break;
1792f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	}
180a0bbb58bcb70295ff05f870c93d34f9fbe614204Jarkko Nikula	wl->station_mode = mode;
1812f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
1822f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo	return ret;
1832f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo}
1842f01a1f58889fbfeb68b1bc1b52e4197f3333490Kalle Valo
185