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