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