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