bh.c revision 911373cca1b45571b62938f8f19cec24cb102471
1a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy/*
2a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers
3a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy *
4a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Copyright (c) 2010, ST-Ericsson
5a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
6a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy *
7a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Based on:
8a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * ST-Ericsson UMAC CW1200 driver, which is
9a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Copyright (c) 2010, ST-Ericsson
10a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
11a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy *
12a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * This program is free software; you can redistribute it and/or modify
13a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * it under the terms of the GNU General Public License version 2 as
14a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * published by the Free Software Foundation.
15a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy */
16a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
17a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/module.h>
18a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <net/mac80211.h>
19a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/kthread.h>
20a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/timer.h>
21a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
22a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include "cw1200.h"
23a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include "bh.h"
24a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include "hwio.h"
25a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include "wsm.h"
26911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy#include "hwbus.h"
27a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include "debug.h"
28a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include "fwio.h"
29a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
30a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_bh(void *arg);
31a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
32a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#define DOWNLOAD_BLOCK_SIZE_WR	(0x1000 - 4)
33a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy/* an SPI message cannot be bigger than (2"12-1)*2 bytes
34a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * "*2" to cvt to bytes */
35a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#define MAX_SZ_RD_WR_BUFFERS	(DOWNLOAD_BLOCK_SIZE_WR*2)
36a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#define PIGGYBACK_CTRL_REG	(2)
37a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#define EFFECTIVE_BUF_SIZE	(MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
38a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
39a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy/* Suspend state privates */
40a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyenum cw1200_bh_pm_state {
41a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	CW1200_BH_RESUMED = 0,
42a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	CW1200_BH_SUSPEND,
43a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	CW1200_BH_SUSPENDED,
44a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	CW1200_BH_RESUME,
45a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy};
46a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
47a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachytypedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
48a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	u8 *data, size_t size);
49a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
50a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic void cw1200_bh_work(struct work_struct *work)
51a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
52a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct cw1200_common *priv =
53a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	container_of(work, struct cw1200_common, bh_work);
54a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	cw1200_bh(priv);
55a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
56a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
57a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyint cw1200_register_bh(struct cw1200_common *priv)
58a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
59a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int err = 0;
60a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Realtime workqueue */
61a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->bh_workqueue = alloc_workqueue("cw1200_bh",
62a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				WQ_MEM_RECLAIM | WQ_HIGHPRI
63a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				| WQ_CPU_INTENSIVE, 1);
64a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
65a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (!priv->bh_workqueue)
66a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return -ENOMEM;
67a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
68a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	INIT_WORK(&priv->bh_work, cw1200_bh_work);
69a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
70a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("[BH] register.\n");
71a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
72a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	atomic_set(&priv->bh_rx, 0);
73a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	atomic_set(&priv->bh_tx, 0);
74a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	atomic_set(&priv->bh_term, 0);
75a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
76a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->bh_error = 0;
77a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->hw_bufs_used = 0;
78a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->buf_id_tx = 0;
79a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->buf_id_rx = 0;
80a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	init_waitqueue_head(&priv->bh_wq);
81a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	init_waitqueue_head(&priv->bh_evt_wq);
82a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
83a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	err = !queue_work(priv->bh_workqueue, &priv->bh_work);
84a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	WARN_ON(err);
85a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return err;
86a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
87a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
88a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyvoid cw1200_unregister_bh(struct cw1200_common *priv)
89a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
90a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	atomic_add(1, &priv->bh_term);
91a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wake_up(&priv->bh_wq);
92a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
93a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	flush_workqueue(priv->bh_workqueue);
94a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
95a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	destroy_workqueue(priv->bh_workqueue);
96a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->bh_workqueue = NULL;
97a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
98a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("[BH] unregistered.\n");
99a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
100a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
101a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyvoid cw1200_irq_handler(struct cw1200_common *priv)
102a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
103a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("[BH] irq.\n");
104a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
105a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Disable Interrupts! */
106911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	/* NOTE:  hwbus_ops->lock already held */
107a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	__cw1200_irq_enable(priv, 0);
108a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
109a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (/* WARN_ON */(priv->bh_error))
110a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return;
111a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
112a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (atomic_add_return(1, &priv->bh_rx) == 1)
113a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		wake_up(&priv->bh_wq);
114a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
115a910e4a94f6923c8c988565525f017f687bf7205Solomon PeachyEXPORT_SYMBOL_GPL(cw1200_irq_handler);
116a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
117a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyvoid cw1200_bh_wakeup(struct cw1200_common *priv)
118a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
119a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("[BH] wakeup.\n");
120a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (priv->bh_error) {
121a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_err("[BH] wakeup failed (BH error)\n");
122a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return;
123a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
124a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
125a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (atomic_add_return(1, &priv->bh_tx) == 1)
126a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		wake_up(&priv->bh_wq);
127a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
128a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
129a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyint cw1200_bh_suspend(struct cw1200_common *priv)
130a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
131a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("[BH] suspend.\n");
132a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (priv->bh_error) {
133a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n");
134a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return -EINVAL;
135a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
136a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
137a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
138a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wake_up(&priv->bh_wq);
139a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
140a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		(CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)),
141a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		 1 * HZ) ? 0 : -ETIMEDOUT;
142a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
143a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
144a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyint cw1200_bh_resume(struct cw1200_common *priv)
145a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
146a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("[BH] resume.\n");
147a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (priv->bh_error) {
148a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n");
149a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return -EINVAL;
150a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
151a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
152a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
153a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wake_up(&priv->bh_wq);
154a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
155a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		(CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)),
156a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		1 * HZ) ? 0 : -ETIMEDOUT;
157a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
158a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
159a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
160a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
161a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	++priv->hw_bufs_used;
162a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
163a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
164a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyint wsm_release_tx_buffer(struct cw1200_common *priv, int count)
165a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
166a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int ret = 0;
167a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int hw_bufs_used = priv->hw_bufs_used;
168a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
169a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->hw_bufs_used -= count;
170a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(priv->hw_bufs_used < 0))
171a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		ret = -1;
172a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	else if (hw_bufs_used >= priv->wsm_caps.input_buffers)
173a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		ret = 1;
174a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (!priv->hw_bufs_used)
175a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		wake_up(&priv->bh_evt_wq);
176a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return ret;
177a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
178a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
179a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv,
180a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					  u16 *ctrl_reg)
181a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
182a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int ret;
183a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
184a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	ret = cw1200_reg_read_16(priv,
185a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			ST90TDS_CONTROL_REG_ID, ctrl_reg);
186a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (ret) {
187a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		ret = cw1200_reg_read_16(priv,
188a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				ST90TDS_CONTROL_REG_ID, ctrl_reg);
189a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (ret)
190a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			pr_err("[BH] Failed to read control register.\n");
191a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
192a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
193a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return ret;
194a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
195a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
196a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_device_wakeup(struct cw1200_common *priv)
197a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
198a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	u16 ctrl_reg;
199a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int ret;
200a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
201a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("[BH] Device wakeup.\n");
202a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
203a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* First, set the dpll register */
204a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
205a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				  cw1200_dpll_from_clk(priv->hw_refclk));
206a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(ret))
207a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return ret;
208a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
209a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* To force the device to be always-on, the host sets WLAN_UP to 1 */
210a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
211a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			ST90TDS_CONT_WUP_BIT);
212a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(ret))
213a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return ret;
214a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
215a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg);
216a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(ret))
217a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return ret;
218a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
219a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* If the device returns WLAN_RDY as 1, the device is active and will
220a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	 * remain active. */
221a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
222a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_debug("[BH] Device awake.\n");
223a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return 1;
224a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
225a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
226a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
227a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
228a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
229a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy/* Must be called from BH thraed. */
230a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyvoid cw1200_enable_powersave(struct cw1200_common *priv,
231a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			     bool enable)
232a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
233a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("[BH] Powerave is %s.\n",
234a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		 enable ? "enabled" : "disabled");
235a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->powersave_enabled = enable;
236a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
237a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
238a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_bh_rx_helper(struct cw1200_common *priv,
239a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			       uint16_t *ctrl_reg,
240a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			       int *tx)
241a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
242a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	size_t read_len = 0;
243a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct sk_buff *skb_rx = NULL;
244a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct wsm_hdr *wsm;
245a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	size_t wsm_len;
246a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	u16 wsm_id;
247a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	u8 wsm_seq;
248a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int rx_resync = 1;
249a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
250a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	size_t alloc_len;
251a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	u8 *data;
252a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
253a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
254a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (!read_len)
255a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return 0; /* No more work */
256a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
257a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
258a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		    (read_len > EFFECTIVE_BUF_SIZE))) {
259a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_debug("Invalid read len: %zu (%04x)",
260a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			 read_len, *ctrl_reg);
261a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto err;
262a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
263a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
264a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Add SIZE of PIGGYBACK reg (CONTROL Reg)
265a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	 * to the NEXT Message length + 2 Bytes for SKB */
266a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	read_len = read_len + 2;
267a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
268911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	alloc_len = priv->hwbus_ops->align_size(
269911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy		priv->hwbus_priv, read_len);
270a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
271a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Check if not exceeding CW1200 capabilities */
272a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
273a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_debug("Read aligned len: %zu\n",
274a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			 alloc_len);
275a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
276a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
277a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	skb_rx = dev_alloc_skb(alloc_len);
278a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(!skb_rx))
279a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto err;
280a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
281a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	skb_trim(skb_rx, 0);
282a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	skb_put(skb_rx, read_len);
283a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	data = skb_rx->data;
284a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(!data))
285a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto err;
286a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
287a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) {
288a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_err("rx blew up, len %zu\n", alloc_len);
289a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto err;
290a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
291a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
292a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Piggyback */
293a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	*ctrl_reg = __le16_to_cpu(
294a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		((__le16 *)data)[alloc_len / 2 - 1]);
295a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
296a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm = (struct wsm_hdr *)data;
297a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm_len = __le16_to_cpu(wsm->len);
298a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(wsm_len > read_len))
299a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto err;
300a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
301a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (priv->wsm_enable_wsm_dumps)
302a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		print_hex_dump_bytes("<-- ",
303a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     DUMP_PREFIX_NONE,
304a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     data, wsm_len);
305a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
306a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm_id  = __le16_to_cpu(wsm->id) & 0xFFF;
307a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
308a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
309a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	skb_trim(skb_rx, wsm_len);
310a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
311a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (wsm_id == 0x0800) {
312a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		wsm_handle_exception(priv,
313a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     &data[sizeof(*wsm)],
314a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     wsm_len - sizeof(*wsm));
315a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto err;
316a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	} else if (!rx_resync) {
317a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (WARN_ON(wsm_seq != priv->wsm_rx_seq))
318a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			goto err;
319a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
320a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->wsm_rx_seq = (wsm_seq + 1) & 7;
321a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	rx_resync = 0;
322a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
323a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (wsm_id & 0x0400) {
324a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		int rc = wsm_release_tx_buffer(priv, 1);
325a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (WARN_ON(rc < 0))
326a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			return rc;
327a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		else if (rc > 0)
328a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			*tx = 1;
329a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
330a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
331a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* cw1200_wsm_rx takes care on SKB livetime */
332a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx)))
333a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto err;
334a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
335a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (skb_rx) {
336a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		dev_kfree_skb(skb_rx);
337a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		skb_rx = NULL;
338a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
339a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
340a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
341a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
342a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyerr:
343a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (skb_rx) {
344a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		dev_kfree_skb(skb_rx);
345a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		skb_rx = NULL;
346a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
347a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return -1;
348a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
349a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
350a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_bh_tx_helper(struct cw1200_common *priv,
351a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			       int *pending_tx,
352a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			       int *tx_burst)
353a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
354a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	size_t tx_len;
355a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	u8 *data;
356a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int ret;
357a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct wsm_hdr *wsm;
358a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
359a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (priv->device_can_sleep) {
360a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		ret = cw1200_device_wakeup(priv);
361a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (WARN_ON(ret < 0)) { /* Error in wakeup */
362a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			*pending_tx = 1;
363a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			return 0;
364a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		} else if (ret) { /* Woke up */
365a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			priv->device_can_sleep = false;
366a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		} else { /* Did not awake */
367a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			*pending_tx = 1;
368a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			return 0;
369a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
370a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
371a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
372a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm_alloc_tx_buffer(priv);
373a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	ret = wsm_get_tx(priv, &data, &tx_len, tx_burst);
374a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (ret <= 0) {
375a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		wsm_release_tx_buffer(priv, 1);
376a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (WARN_ON(ret < 0))
377a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			return ret; /* Error */
378a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return 0; /* No work */
379a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
380a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
381a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm = (struct wsm_hdr *)data;
382a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	BUG_ON(tx_len < sizeof(*wsm));
383a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
384a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
385a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	atomic_add(1, &priv->bh_tx);
386a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
387911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	tx_len = priv->hwbus_ops->align_size(
388911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy		priv->hwbus_priv, tx_len);
389a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
390a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Check if not exceeding CW1200 capabilities */
391a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
392a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_debug("Write aligned len: %zu\n", tx_len);
393a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
394a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
395a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq));
396a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
397a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(cw1200_data_write(priv, data, tx_len))) {
398a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_err("tx blew up, len %zu\n", tx_len);
399a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		wsm_release_tx_buffer(priv, 1);
400a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return -1; /* Error */
401a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
402a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
403a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (priv->wsm_enable_wsm_dumps)
404a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		print_hex_dump_bytes("--> ",
405a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     DUMP_PREFIX_NONE,
406a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     data,
407a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     __le16_to_cpu(wsm->len));
408a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
409a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	wsm_txed(priv, data);
410a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
411a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
412a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (*tx_burst > 1) {
413a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		cw1200_debug_tx_burst(priv);
414a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return 1; /* Work remains */
415a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
416a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
417a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
418a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
419a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
420a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_bh(void *arg)
421a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
422a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct cw1200_common *priv = arg;
423a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int rx, tx, term, suspend;
424a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	u16 ctrl_reg = 0;
425a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int tx_allowed;
426a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int pending_tx = 0;
427a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int tx_burst;
428a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	long status;
429a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	u32 dummy;
430a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int ret;
431a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
432a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	for (;;) {
433a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (!priv->hw_bufs_used &&
434a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		    priv->powersave_enabled &&
435a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		    !priv->device_can_sleep &&
436a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		    !atomic_read(&priv->recent_scan)) {
437a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			status = 1 * HZ;
438a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			pr_debug("[BH] Device wakedown. No data.\n");
439a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0);
440a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			priv->device_can_sleep = true;
441a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		} else if (priv->hw_bufs_used) {
442a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			/* Interrupt loss detection */
443a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			status = 1 * HZ;
444a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		} else {
445a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			status = MAX_SCHEDULE_TIMEOUT;
446a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
447a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
448a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		/* Dummy Read for SDIO retry mechanism*/
449a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if ((priv->hw_type != -1) &&
450a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		    (atomic_read(&priv->bh_rx) == 0) &&
451a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		    (atomic_read(&priv->bh_tx) == 0))
452a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID,
453a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					&dummy, sizeof(dummy));
454a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
455a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_debug("[BH] waiting ...\n");
456a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		status = wait_event_interruptible_timeout(priv->bh_wq, ({
457a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				rx = atomic_xchg(&priv->bh_rx, 0);
458a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				tx = atomic_xchg(&priv->bh_tx, 0);
459a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				term = atomic_xchg(&priv->bh_term, 0);
460a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				suspend = pending_tx ?
461a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					0 : atomic_read(&priv->bh_suspend);
462a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				(rx || tx || term || suspend || priv->bh_error);
463a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			}), status);
464a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
465a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n",
466a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			 rx, tx, term, suspend, status);
467a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
468a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		/* Did an error occur? */
469a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if ((status < 0 && status != -ERESTARTSYS) ||
470a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		    term || priv->bh_error) {
471a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			break;
472a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
473a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (!status) {  /* wait_event timed out */
474a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			unsigned long timestamp = jiffies;
475a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			long timeout;
476a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			int pending = 0;
477a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			int i;
478a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
479a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			/* Check to see if we have any outstanding frames */
480a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (priv->hw_bufs_used && (!rx || !tx)) {
481a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				wiphy_warn(priv->hw->wiphy,
482a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					   "Missed interrupt? (%d frames outstanding)\n",
483a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					   priv->hw_bufs_used);
484a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				rx = 1;
485a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
486a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				/* Get a timestamp of "oldest" frame */
487a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				for (i = 0; i < 4; ++i)
488a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					pending += cw1200_queue_get_xmit_timestamp(
489a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy						&priv->tx_queue[i],
490a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy						&timestamp,
491a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy						priv->pending_frame_id);
492a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
493a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				/* Check if frame transmission is timed out.
494a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				 * Add an extra second with respect to possible
495a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				 * interrupt loss.
496a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				 */
497a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				timeout = timestamp +
498a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					WSM_CMD_LAST_CHANCE_TIMEOUT +
499a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					1 * HZ  -
500a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					jiffies;
501a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
502a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				/* And terminate BH thread if the frame is "stuck" */
503a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				if (pending && timeout < 0) {
504a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					wiphy_warn(priv->hw->wiphy,
505a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy						   "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
506a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy						   priv->hw_bufs_used, pending,
507a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy						   timestamp, jiffies);
508a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					break;
509a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				}
510a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			} else if (!priv->device_can_sleep &&
511a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				   !atomic_read(&priv->recent_scan)) {
512a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				pr_debug("[BH] Device wakedown. Timeout.\n");
513a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				cw1200_reg_write_16(priv,
514a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy						    ST90TDS_CONTROL_REG_ID, 0);
515a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				priv->device_can_sleep = true;
516a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			}
517a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			goto done;
518a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		} else if (suspend) {
519a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			pr_debug("[BH] Device suspend.\n");
520a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (priv->powersave_enabled) {
521a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				pr_debug("[BH] Device wakedown. Suspend.\n");
522a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				cw1200_reg_write_16(priv,
523a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy						    ST90TDS_CONTROL_REG_ID, 0);
524a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				priv->device_can_sleep = true;
525a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			}
526a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
527a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
528a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			wake_up(&priv->bh_evt_wq);
529a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			status = wait_event_interruptible(priv->bh_wq,
530a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy							  CW1200_BH_RESUME == atomic_read(&priv->bh_suspend));
531a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (status < 0) {
532a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				wiphy_err(priv->hw->wiphy,
533a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					  "Failed to wait for resume: %ld.\n",
534a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					  status);
535a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				break;
536a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			}
537a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			pr_debug("[BH] Device resume.\n");
538a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
539a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			wake_up(&priv->bh_evt_wq);
540a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			atomic_add(1, &priv->bh_rx);
541a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			goto done;
542a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
543a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
544a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	rx:
545a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		tx += pending_tx;
546a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pending_tx = 0;
547a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
548a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
549a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			break;
550a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
551a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		/* Don't bother trying to rx unless we have data to read */
552a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
553a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
554a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (ret < 0)
555a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				break;
556a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			/* Double up here if there's more data.. */
557a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
558a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
559a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				if (ret < 0)
560a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy					break;
561a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			}
562a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
563a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
564a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	tx:
565a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (tx) {
566a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			tx = 0;
567a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
568a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers);
569a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used;
570a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			tx_allowed = tx_burst > 0;
571a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
572a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (!tx_allowed) {
573a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				/* Buffers full.  Ensure we process tx
574a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				 * after we handle rx..
575a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				 */
576a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				pending_tx = tx;
577a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				goto done_rx;
578a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			}
579a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst);
580a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (ret < 0)
581a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				break;
582a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (ret > 0) /* More to transmit */
583a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				tx = ret;
584a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
585a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			/* Re-read ctrl reg */
586a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
587a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				break;
588a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
589a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
590a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	done_rx:
591a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (priv->bh_error)
592a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			break;
593a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
594a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			goto rx;
595a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (tx)
596a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			goto tx;
597a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
598a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	done:
599a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		/* Re-enable device interrupts */
600911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy		priv->hwbus_ops->lock(priv->hwbus_priv);
601a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		__cw1200_irq_enable(priv, 1);
602911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy		priv->hwbus_ops->unlock(priv->hwbus_priv);
603a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
604a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
605a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Explicitly disable device interrupts */
606911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	priv->hwbus_ops->lock(priv->hwbus_priv);
607a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	__cw1200_irq_enable(priv, 0);
608911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	priv->hwbus_ops->unlock(priv->hwbus_priv);
609a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
610a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (!term) {
611a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_err("[BH] Fatal error, exiting.\n");
612a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		priv->bh_error = 1;
613a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		/* TODO: schedule_work(recovery) */
614a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
615a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
616a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
617