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