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 ×tamp, 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