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