xref: /linux/drivers/net/wireless/ti/wl1251/event.c (revision 45474475)
12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
290921014SLuciano Coelho /*
390921014SLuciano Coelho  * This file is part of wl1251
490921014SLuciano Coelho  *
590921014SLuciano Coelho  * Copyright (c) 1998-2007 Texas Instruments Incorporated
690921014SLuciano Coelho  * Copyright (C) 2008 Nokia Corporation
790921014SLuciano Coelho  */
890921014SLuciano Coelho 
990921014SLuciano Coelho #include "wl1251.h"
1090921014SLuciano Coelho #include "reg.h"
1190921014SLuciano Coelho #include "io.h"
1290921014SLuciano Coelho #include "event.h"
1390921014SLuciano Coelho #include "ps.h"
1490921014SLuciano Coelho 
wl1251_event_scan_complete(struct wl1251 * wl,struct event_mailbox * mbox)1590921014SLuciano Coelho static int wl1251_event_scan_complete(struct wl1251 *wl,
1690921014SLuciano Coelho 				      struct event_mailbox *mbox)
1790921014SLuciano Coelho {
18f1e3e051SJohannes Berg 	int ret = 0;
19f1e3e051SJohannes Berg 
2090921014SLuciano Coelho 	wl1251_debug(DEBUG_EVENT, "status: 0x%x, channels: %d",
2190921014SLuciano Coelho 		     mbox->scheduled_scan_status,
2290921014SLuciano Coelho 		     mbox->scheduled_scan_channels);
2390921014SLuciano Coelho 
2490921014SLuciano Coelho 	if (wl->scanning) {
257947d3e0SAvraham Stern 		struct cfg80211_scan_info info = {
267947d3e0SAvraham Stern 			.aborted = false,
277947d3e0SAvraham Stern 		};
287947d3e0SAvraham Stern 
297947d3e0SAvraham Stern 		ieee80211_scan_completed(wl->hw, &info);
3090921014SLuciano Coelho 		wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan completed");
3190921014SLuciano Coelho 		wl->scanning = false;
32f1e3e051SJohannes Berg 		if (wl->hw->conf.flags & IEEE80211_CONF_IDLE)
33f1e3e051SJohannes Berg 			ret = wl1251_ps_set_mode(wl, STATION_IDLE);
3490921014SLuciano Coelho 	}
3590921014SLuciano Coelho 
36f1e3e051SJohannes Berg 	return ret;
3790921014SLuciano Coelho }
3890921014SLuciano Coelho 
39f7ad1eedSDavid Gnedt #define WL1251_PSM_ENTRY_RETRIES  3
wl1251_event_ps_report(struct wl1251 * wl,struct event_mailbox * mbox)40f7ad1eedSDavid Gnedt static int wl1251_event_ps_report(struct wl1251 *wl,
41f7ad1eedSDavid Gnedt 				  struct event_mailbox *mbox)
42f7ad1eedSDavid Gnedt {
43f7ad1eedSDavid Gnedt 	int ret = 0;
44f7ad1eedSDavid Gnedt 
45f7ad1eedSDavid Gnedt 	wl1251_debug(DEBUG_EVENT, "ps status: %x", mbox->ps_status);
46f7ad1eedSDavid Gnedt 
47f7ad1eedSDavid Gnedt 	switch (mbox->ps_status) {
48f7ad1eedSDavid Gnedt 	case EVENT_ENTER_POWER_SAVE_FAIL:
49f7ad1eedSDavid Gnedt 		wl1251_debug(DEBUG_PSM, "PSM entry failed");
50f7ad1eedSDavid Gnedt 
51f7ad1eedSDavid Gnedt 		if (wl->station_mode != STATION_POWER_SAVE_MODE) {
52f7ad1eedSDavid Gnedt 			/* remain in active mode */
53f7ad1eedSDavid Gnedt 			wl->psm_entry_retry = 0;
54f7ad1eedSDavid Gnedt 			break;
55f7ad1eedSDavid Gnedt 		}
56f7ad1eedSDavid Gnedt 
57f7ad1eedSDavid Gnedt 		if (wl->psm_entry_retry < WL1251_PSM_ENTRY_RETRIES) {
58f7ad1eedSDavid Gnedt 			wl->psm_entry_retry++;
59f7ad1eedSDavid Gnedt 			ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
60f7ad1eedSDavid Gnedt 		} else {
61f7ad1eedSDavid Gnedt 			wl1251_error("Power save entry failed, giving up");
62f7ad1eedSDavid Gnedt 			wl->psm_entry_retry = 0;
63f7ad1eedSDavid Gnedt 		}
64f7ad1eedSDavid Gnedt 		break;
65f7ad1eedSDavid Gnedt 	case EVENT_ENTER_POWER_SAVE_SUCCESS:
66f7ad1eedSDavid Gnedt 	case EVENT_EXIT_POWER_SAVE_FAIL:
67f7ad1eedSDavid Gnedt 	case EVENT_EXIT_POWER_SAVE_SUCCESS:
68f7ad1eedSDavid Gnedt 	default:
69f7ad1eedSDavid Gnedt 		wl->psm_entry_retry = 0;
70f7ad1eedSDavid Gnedt 		break;
71f7ad1eedSDavid Gnedt 	}
72f7ad1eedSDavid Gnedt 
7320e64213SWang Hai 	return ret;
74f7ad1eedSDavid Gnedt }
75f7ad1eedSDavid Gnedt 
wl1251_event_mbox_dump(struct event_mailbox * mbox)7690921014SLuciano Coelho static void wl1251_event_mbox_dump(struct event_mailbox *mbox)
7790921014SLuciano Coelho {
7890921014SLuciano Coelho 	wl1251_debug(DEBUG_EVENT, "MBOX DUMP:");
7990921014SLuciano Coelho 	wl1251_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
8090921014SLuciano Coelho 	wl1251_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
8190921014SLuciano Coelho }
8290921014SLuciano Coelho 
wl1251_event_process(struct wl1251 * wl,struct event_mailbox * mbox)8390921014SLuciano Coelho static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
8490921014SLuciano Coelho {
8590921014SLuciano Coelho 	int ret;
8690921014SLuciano Coelho 	u32 vector;
8790921014SLuciano Coelho 
8890921014SLuciano Coelho 	wl1251_event_mbox_dump(mbox);
8990921014SLuciano Coelho 
9090921014SLuciano Coelho 	vector = mbox->events_vector & ~(mbox->events_mask);
9190921014SLuciano Coelho 	wl1251_debug(DEBUG_EVENT, "vector: 0x%x", vector);
9290921014SLuciano Coelho 
9390921014SLuciano Coelho 	if (vector & SCAN_COMPLETE_EVENT_ID) {
9490921014SLuciano Coelho 		ret = wl1251_event_scan_complete(wl, mbox);
9590921014SLuciano Coelho 		if (ret < 0)
9690921014SLuciano Coelho 			return ret;
9790921014SLuciano Coelho 	}
9890921014SLuciano Coelho 
9990921014SLuciano Coelho 	if (vector & BSS_LOSE_EVENT_ID) {
10090921014SLuciano Coelho 		wl1251_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
10190921014SLuciano Coelho 
10290921014SLuciano Coelho 		if (wl->psm_requested &&
10390921014SLuciano Coelho 		    wl->station_mode != STATION_ACTIVE_MODE) {
10490921014SLuciano Coelho 			ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE);
10590921014SLuciano Coelho 			if (ret < 0)
10690921014SLuciano Coelho 				return ret;
10790921014SLuciano Coelho 		}
10890921014SLuciano Coelho 	}
10990921014SLuciano Coelho 
110f7ad1eedSDavid Gnedt 	if (vector & PS_REPORT_EVENT_ID) {
111f7ad1eedSDavid Gnedt 		wl1251_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
112f7ad1eedSDavid Gnedt 		ret = wl1251_event_ps_report(wl, mbox);
113f7ad1eedSDavid Gnedt 		if (ret < 0)
114f7ad1eedSDavid Gnedt 			return ret;
115f7ad1eedSDavid Gnedt 	}
116f7ad1eedSDavid Gnedt 
117e5b02f64SGrazvydas Ignotas 	if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) {
11890921014SLuciano Coelho 		wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT");
11990921014SLuciano Coelho 
12090921014SLuciano Coelho 		/* indicate to the stack, that beacons have been lost */
121e5b02f64SGrazvydas Ignotas 		if (wl->vif && wl->vif->type == NL80211_IFTYPE_STATION)
12290921014SLuciano Coelho 			ieee80211_beacon_loss(wl->vif);
12390921014SLuciano Coelho 	}
12490921014SLuciano Coelho 
12590921014SLuciano Coelho 	if (vector & REGAINED_BSS_EVENT_ID) {
12690921014SLuciano Coelho 		if (wl->psm_requested) {
12790921014SLuciano Coelho 			ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
12890921014SLuciano Coelho 			if (ret < 0)
12990921014SLuciano Coelho 				return ret;
13090921014SLuciano Coelho 		}
13190921014SLuciano Coelho 	}
13290921014SLuciano Coelho 
13390921014SLuciano Coelho 	if (wl->vif && wl->rssi_thold) {
13490921014SLuciano Coelho 		if (vector & ROAMING_TRIGGER_LOW_RSSI_EVENT_ID) {
13590921014SLuciano Coelho 			wl1251_debug(DEBUG_EVENT,
13690921014SLuciano Coelho 				     "ROAMING_TRIGGER_LOW_RSSI_EVENT");
13790921014SLuciano Coelho 			ieee80211_cqm_rssi_notify(wl->vif,
13890921014SLuciano Coelho 				NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
139769f07d8SAndrzej Zaborowski 				0, GFP_KERNEL);
14090921014SLuciano Coelho 		}
14190921014SLuciano Coelho 
14290921014SLuciano Coelho 		if (vector & ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID) {
14390921014SLuciano Coelho 			wl1251_debug(DEBUG_EVENT,
14490921014SLuciano Coelho 				     "ROAMING_TRIGGER_REGAINED_RSSI_EVENT");
14590921014SLuciano Coelho 			ieee80211_cqm_rssi_notify(wl->vif,
14690921014SLuciano Coelho 				NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
147769f07d8SAndrzej Zaborowski 				0, GFP_KERNEL);
14890921014SLuciano Coelho 		}
14990921014SLuciano Coelho 	}
15090921014SLuciano Coelho 
15190921014SLuciano Coelho 	return 0;
15290921014SLuciano Coelho }
15390921014SLuciano Coelho 
15490921014SLuciano Coelho /*
15590921014SLuciano Coelho  * Poll the mailbox event field until any of the bits in the mask is set or a
15690921014SLuciano Coelho  * timeout occurs (WL1251_EVENT_TIMEOUT in msecs)
15790921014SLuciano Coelho  */
wl1251_event_wait(struct wl1251 * wl,u32 mask,int timeout_ms)15890921014SLuciano Coelho int wl1251_event_wait(struct wl1251 *wl, u32 mask, int timeout_ms)
15990921014SLuciano Coelho {
16090921014SLuciano Coelho 	u32 events_vector, event;
16190921014SLuciano Coelho 	unsigned long timeout;
16290921014SLuciano Coelho 
16390921014SLuciano Coelho 	timeout = jiffies + msecs_to_jiffies(timeout_ms);
16490921014SLuciano Coelho 
16590921014SLuciano Coelho 	do {
16690921014SLuciano Coelho 		if (time_after(jiffies, timeout))
16790921014SLuciano Coelho 			return -ETIMEDOUT;
16890921014SLuciano Coelho 
16990921014SLuciano Coelho 		msleep(1);
17090921014SLuciano Coelho 
17190921014SLuciano Coelho 		/* read from both event fields */
17245474475SH. Nikolaus Schaller 		events_vector = wl1251_mem_read32(wl, wl->mbox_ptr[0]);
17390921014SLuciano Coelho 		event = events_vector & mask;
17445474475SH. Nikolaus Schaller 		events_vector = wl1251_mem_read32(wl, wl->mbox_ptr[1]);
17590921014SLuciano Coelho 		event |= events_vector & mask;
17690921014SLuciano Coelho 	} while (!event);
17790921014SLuciano Coelho 
17890921014SLuciano Coelho 	return 0;
17990921014SLuciano Coelho }
18090921014SLuciano Coelho 
wl1251_event_unmask(struct wl1251 * wl)18190921014SLuciano Coelho int wl1251_event_unmask(struct wl1251 *wl)
18290921014SLuciano Coelho {
18390921014SLuciano Coelho 	int ret;
18490921014SLuciano Coelho 
18590921014SLuciano Coelho 	ret = wl1251_acx_event_mbox_mask(wl, ~(wl->event_mask));
18690921014SLuciano Coelho 	if (ret < 0)
18790921014SLuciano Coelho 		return ret;
18890921014SLuciano Coelho 
18990921014SLuciano Coelho 	return 0;
19090921014SLuciano Coelho }
19190921014SLuciano Coelho 
wl1251_event_mbox_config(struct wl1251 * wl)19290921014SLuciano Coelho void wl1251_event_mbox_config(struct wl1251 *wl)
19390921014SLuciano Coelho {
19490921014SLuciano Coelho 	wl->mbox_ptr[0] = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
19590921014SLuciano Coelho 	wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
19690921014SLuciano Coelho 
19790921014SLuciano Coelho 	wl1251_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x",
19890921014SLuciano Coelho 		     wl->mbox_ptr[0], wl->mbox_ptr[1]);
19990921014SLuciano Coelho }
20090921014SLuciano Coelho 
wl1251_event_handle(struct wl1251 * wl,u8 mbox_num)20190921014SLuciano Coelho int wl1251_event_handle(struct wl1251 *wl, u8 mbox_num)
20290921014SLuciano Coelho {
20345474475SH. Nikolaus Schaller 	struct event_mailbox *mbox;
20490921014SLuciano Coelho 	int ret;
20590921014SLuciano Coelho 
20690921014SLuciano Coelho 	wl1251_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num);
20790921014SLuciano Coelho 
20890921014SLuciano Coelho 	if (mbox_num > 1)
20990921014SLuciano Coelho 		return -EINVAL;
21090921014SLuciano Coelho 
21145474475SH. Nikolaus Schaller 	mbox = kmalloc(sizeof(*mbox), GFP_KERNEL);
21245474475SH. Nikolaus Schaller 	if (!mbox) {
21345474475SH. Nikolaus Schaller 		wl1251_error("can not allocate mbox buffer");
21445474475SH. Nikolaus Schaller 		return -ENOMEM;
21545474475SH. Nikolaus Schaller 	}
21645474475SH. Nikolaus Schaller 
21790921014SLuciano Coelho 	/* first we read the mbox descriptor */
21845474475SH. Nikolaus Schaller 	wl1251_mem_read(wl, wl->mbox_ptr[mbox_num], mbox,
21945474475SH. Nikolaus Schaller 			sizeof(*mbox));
22090921014SLuciano Coelho 
22190921014SLuciano Coelho 	/* process the descriptor */
22245474475SH. Nikolaus Schaller 	ret = wl1251_event_process(wl, mbox);
22345474475SH. Nikolaus Schaller 	kfree(mbox);
22445474475SH. Nikolaus Schaller 
22590921014SLuciano Coelho 	if (ret < 0)
22690921014SLuciano Coelho 		return ret;
22790921014SLuciano Coelho 
22890921014SLuciano Coelho 	/* then we let the firmware know it can go on...*/
22990921014SLuciano Coelho 	wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK);
23090921014SLuciano Coelho 
23190921014SLuciano Coelho 	return 0;
23290921014SLuciano Coelho }
233