18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci* Filename: cregs.c 48c2ecf20Sopenharmony_ci* 58c2ecf20Sopenharmony_ci* Authors: Joshua Morris <josh.h.morris@us.ibm.com> 68c2ecf20Sopenharmony_ci* Philip Kelleher <pjk1939@linux.vnet.ibm.com> 78c2ecf20Sopenharmony_ci* 88c2ecf20Sopenharmony_ci* (C) Copyright 2013 IBM Corporation 98c2ecf20Sopenharmony_ci*/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/completion.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "rsxx_priv.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define CREG_TIMEOUT_MSEC 10000 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_citypedef void (*creg_cmd_cb)(struct rsxx_cardinfo *card, 198c2ecf20Sopenharmony_ci struct creg_cmd *cmd, 208c2ecf20Sopenharmony_ci int st); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct creg_cmd { 238c2ecf20Sopenharmony_ci struct list_head list; 248c2ecf20Sopenharmony_ci creg_cmd_cb cb; 258c2ecf20Sopenharmony_ci void *cb_private; 268c2ecf20Sopenharmony_ci unsigned int op; 278c2ecf20Sopenharmony_ci unsigned int addr; 288c2ecf20Sopenharmony_ci int cnt8; 298c2ecf20Sopenharmony_ci void *buf; 308c2ecf20Sopenharmony_ci unsigned int stream; 318c2ecf20Sopenharmony_ci unsigned int status; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic struct kmem_cache *creg_cmd_pool; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/*------------ Private Functions --------------*/ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#if defined(__LITTLE_ENDIAN) 408c2ecf20Sopenharmony_ci#define LITTLE_ENDIAN 1 418c2ecf20Sopenharmony_ci#elif defined(__BIG_ENDIAN) 428c2ecf20Sopenharmony_ci#define LITTLE_ENDIAN 0 438c2ecf20Sopenharmony_ci#else 448c2ecf20Sopenharmony_ci#error Unknown endianess!!! Aborting... 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int copy_to_creg_data(struct rsxx_cardinfo *card, 488c2ecf20Sopenharmony_ci int cnt8, 498c2ecf20Sopenharmony_ci void *buf, 508c2ecf20Sopenharmony_ci unsigned int stream) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int i = 0; 538c2ecf20Sopenharmony_ci u32 *data = buf; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (unlikely(card->eeh_state)) 568c2ecf20Sopenharmony_ci return -EIO; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci for (i = 0; cnt8 > 0; i++, cnt8 -= 4) { 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * Firmware implementation makes it necessary to byte swap on 618c2ecf20Sopenharmony_ci * little endian processors. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci if (LITTLE_ENDIAN && stream) 648c2ecf20Sopenharmony_ci iowrite32be(data[i], card->regmap + CREG_DATA(i)); 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci iowrite32(data[i], card->regmap + CREG_DATA(i)); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int copy_from_creg_data(struct rsxx_cardinfo *card, 748c2ecf20Sopenharmony_ci int cnt8, 758c2ecf20Sopenharmony_ci void *buf, 768c2ecf20Sopenharmony_ci unsigned int stream) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci int i = 0; 798c2ecf20Sopenharmony_ci u32 *data = buf; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (unlikely(card->eeh_state)) 828c2ecf20Sopenharmony_ci return -EIO; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci for (i = 0; cnt8 > 0; i++, cnt8 -= 4) { 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * Firmware implementation makes it necessary to byte swap on 878c2ecf20Sopenharmony_ci * little endian processors. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci if (LITTLE_ENDIAN && stream) 908c2ecf20Sopenharmony_ci data[i] = ioread32be(card->regmap + CREG_DATA(i)); 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci data[i] = ioread32(card->regmap + CREG_DATA(i)); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void creg_issue_cmd(struct rsxx_cardinfo *card, struct creg_cmd *cmd) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci int st; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (unlikely(card->eeh_state)) 1038c2ecf20Sopenharmony_ci return; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci iowrite32(cmd->addr, card->regmap + CREG_ADD); 1068c2ecf20Sopenharmony_ci iowrite32(cmd->cnt8, card->regmap + CREG_CNT); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (cmd->op == CREG_OP_WRITE) { 1098c2ecf20Sopenharmony_ci if (cmd->buf) { 1108c2ecf20Sopenharmony_ci st = copy_to_creg_data(card, cmd->cnt8, 1118c2ecf20Sopenharmony_ci cmd->buf, cmd->stream); 1128c2ecf20Sopenharmony_ci if (st) 1138c2ecf20Sopenharmony_ci return; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (unlikely(card->eeh_state)) 1188c2ecf20Sopenharmony_ci return; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Setting the valid bit will kick off the command. */ 1218c2ecf20Sopenharmony_ci iowrite32(cmd->op, card->regmap + CREG_CMD); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void creg_kick_queue(struct rsxx_cardinfo *card) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci if (card->creg_ctrl.active || list_empty(&card->creg_ctrl.queue)) 1278c2ecf20Sopenharmony_ci return; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci card->creg_ctrl.active = 1; 1308c2ecf20Sopenharmony_ci card->creg_ctrl.active_cmd = list_first_entry(&card->creg_ctrl.queue, 1318c2ecf20Sopenharmony_ci struct creg_cmd, list); 1328c2ecf20Sopenharmony_ci list_del(&card->creg_ctrl.active_cmd->list); 1338c2ecf20Sopenharmony_ci card->creg_ctrl.q_depth--; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * We have to set the timer before we push the new command. Otherwise, 1378c2ecf20Sopenharmony_ci * we could create a race condition that would occur if the timer 1388c2ecf20Sopenharmony_ci * was not canceled, and expired after the new command was pushed, 1398c2ecf20Sopenharmony_ci * but before the command was issued to hardware. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci mod_timer(&card->creg_ctrl.cmd_timer, 1428c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(CREG_TIMEOUT_MSEC)); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci creg_issue_cmd(card, card->creg_ctrl.active_cmd); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int creg_queue_cmd(struct rsxx_cardinfo *card, 1488c2ecf20Sopenharmony_ci unsigned int op, 1498c2ecf20Sopenharmony_ci unsigned int addr, 1508c2ecf20Sopenharmony_ci unsigned int cnt8, 1518c2ecf20Sopenharmony_ci void *buf, 1528c2ecf20Sopenharmony_ci int stream, 1538c2ecf20Sopenharmony_ci creg_cmd_cb callback, 1548c2ecf20Sopenharmony_ci void *cb_private) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct creg_cmd *cmd; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Don't queue stuff up if we're halted. */ 1598c2ecf20Sopenharmony_ci if (unlikely(card->halt)) 1608c2ecf20Sopenharmony_ci return -EINVAL; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (card->creg_ctrl.reset) 1638c2ecf20Sopenharmony_ci return -EAGAIN; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (cnt8 > MAX_CREG_DATA8) 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci cmd = kmem_cache_alloc(creg_cmd_pool, GFP_KERNEL); 1698c2ecf20Sopenharmony_ci if (!cmd) 1708c2ecf20Sopenharmony_ci return -ENOMEM; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cmd->list); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci cmd->op = op; 1758c2ecf20Sopenharmony_ci cmd->addr = addr; 1768c2ecf20Sopenharmony_ci cmd->cnt8 = cnt8; 1778c2ecf20Sopenharmony_ci cmd->buf = buf; 1788c2ecf20Sopenharmony_ci cmd->stream = stream; 1798c2ecf20Sopenharmony_ci cmd->cb = callback; 1808c2ecf20Sopenharmony_ci cmd->cb_private = cb_private; 1818c2ecf20Sopenharmony_ci cmd->status = 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci spin_lock_bh(&card->creg_ctrl.lock); 1848c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &card->creg_ctrl.queue); 1858c2ecf20Sopenharmony_ci card->creg_ctrl.q_depth++; 1868c2ecf20Sopenharmony_ci creg_kick_queue(card); 1878c2ecf20Sopenharmony_ci spin_unlock_bh(&card->creg_ctrl.lock); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void creg_cmd_timed_out(struct timer_list *t) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct rsxx_cardinfo *card = from_timer(card, t, creg_ctrl.cmd_timer); 1958c2ecf20Sopenharmony_ci struct creg_cmd *cmd; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci spin_lock(&card->creg_ctrl.lock); 1988c2ecf20Sopenharmony_ci cmd = card->creg_ctrl.active_cmd; 1998c2ecf20Sopenharmony_ci card->creg_ctrl.active_cmd = NULL; 2008c2ecf20Sopenharmony_ci spin_unlock(&card->creg_ctrl.lock); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (cmd == NULL) { 2038c2ecf20Sopenharmony_ci card->creg_ctrl.creg_stats.creg_timeout++; 2048c2ecf20Sopenharmony_ci dev_warn(CARD_TO_DEV(card), 2058c2ecf20Sopenharmony_ci "No active command associated with timeout!\n"); 2068c2ecf20Sopenharmony_ci return; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (cmd->cb) 2108c2ecf20Sopenharmony_ci cmd->cb(card, cmd, -ETIMEDOUT); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci kmem_cache_free(creg_cmd_pool, cmd); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci spin_lock(&card->creg_ctrl.lock); 2168c2ecf20Sopenharmony_ci card->creg_ctrl.active = 0; 2178c2ecf20Sopenharmony_ci creg_kick_queue(card); 2188c2ecf20Sopenharmony_ci spin_unlock(&card->creg_ctrl.lock); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void creg_cmd_done(struct work_struct *work) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct rsxx_cardinfo *card; 2258c2ecf20Sopenharmony_ci struct creg_cmd *cmd; 2268c2ecf20Sopenharmony_ci int st = 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci card = container_of(work, struct rsxx_cardinfo, 2298c2ecf20Sopenharmony_ci creg_ctrl.done_work); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* 2328c2ecf20Sopenharmony_ci * The timer could not be cancelled for some reason, 2338c2ecf20Sopenharmony_ci * race to pop the active command. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci if (del_timer_sync(&card->creg_ctrl.cmd_timer) == 0) 2368c2ecf20Sopenharmony_ci card->creg_ctrl.creg_stats.failed_cancel_timer++; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci spin_lock_bh(&card->creg_ctrl.lock); 2398c2ecf20Sopenharmony_ci cmd = card->creg_ctrl.active_cmd; 2408c2ecf20Sopenharmony_ci card->creg_ctrl.active_cmd = NULL; 2418c2ecf20Sopenharmony_ci spin_unlock_bh(&card->creg_ctrl.lock); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (cmd == NULL) { 2448c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 2458c2ecf20Sopenharmony_ci "Spurious creg interrupt!\n"); 2468c2ecf20Sopenharmony_ci return; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci card->creg_ctrl.creg_stats.stat = ioread32(card->regmap + CREG_STAT); 2508c2ecf20Sopenharmony_ci cmd->status = card->creg_ctrl.creg_stats.stat; 2518c2ecf20Sopenharmony_ci if ((cmd->status & CREG_STAT_STATUS_MASK) == 0) { 2528c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 2538c2ecf20Sopenharmony_ci "Invalid status on creg command\n"); 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * At this point we're probably reading garbage from HW. Don't 2568c2ecf20Sopenharmony_ci * do anything else that could mess up the system and let 2578c2ecf20Sopenharmony_ci * the sync function return an error. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci st = -EIO; 2608c2ecf20Sopenharmony_ci goto creg_done; 2618c2ecf20Sopenharmony_ci } else if (cmd->status & CREG_STAT_ERROR) { 2628c2ecf20Sopenharmony_ci st = -EIO; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (cmd->op == CREG_OP_READ) { 2668c2ecf20Sopenharmony_ci unsigned int cnt8 = ioread32(card->regmap + CREG_CNT); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Paranoid Sanity Checks */ 2698c2ecf20Sopenharmony_ci if (!cmd->buf) { 2708c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 2718c2ecf20Sopenharmony_ci "Buffer not given for read.\n"); 2728c2ecf20Sopenharmony_ci st = -EIO; 2738c2ecf20Sopenharmony_ci goto creg_done; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci if (cnt8 != cmd->cnt8) { 2768c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 2778c2ecf20Sopenharmony_ci "count mismatch\n"); 2788c2ecf20Sopenharmony_ci st = -EIO; 2798c2ecf20Sopenharmony_ci goto creg_done; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci st = copy_from_creg_data(card, cnt8, cmd->buf, cmd->stream); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cicreg_done: 2868c2ecf20Sopenharmony_ci if (cmd->cb) 2878c2ecf20Sopenharmony_ci cmd->cb(card, cmd, st); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci kmem_cache_free(creg_cmd_pool, cmd); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci spin_lock_bh(&card->creg_ctrl.lock); 2928c2ecf20Sopenharmony_ci card->creg_ctrl.active = 0; 2938c2ecf20Sopenharmony_ci creg_kick_queue(card); 2948c2ecf20Sopenharmony_ci spin_unlock_bh(&card->creg_ctrl.lock); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void creg_reset(struct rsxx_cardinfo *card) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct creg_cmd *cmd = NULL; 3008c2ecf20Sopenharmony_ci struct creg_cmd *tmp; 3018c2ecf20Sopenharmony_ci unsigned long flags; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * mutex_trylock is used here because if reset_lock is taken then a 3058c2ecf20Sopenharmony_ci * reset is already happening. So, we can just go ahead and return. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci if (!mutex_trylock(&card->creg_ctrl.reset_lock)) 3088c2ecf20Sopenharmony_ci return; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci card->creg_ctrl.reset = 1; 3118c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->irq_lock, flags); 3128c2ecf20Sopenharmony_ci rsxx_disable_ier_and_isr(card, CR_INTR_CREG | CR_INTR_EVENT); 3138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->irq_lock, flags); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci dev_warn(CARD_TO_DEV(card), 3168c2ecf20Sopenharmony_ci "Resetting creg interface for recovery\n"); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Cancel outstanding commands */ 3198c2ecf20Sopenharmony_ci spin_lock_bh(&card->creg_ctrl.lock); 3208c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, tmp, &card->creg_ctrl.queue, list) { 3218c2ecf20Sopenharmony_ci list_del(&cmd->list); 3228c2ecf20Sopenharmony_ci card->creg_ctrl.q_depth--; 3238c2ecf20Sopenharmony_ci if (cmd->cb) 3248c2ecf20Sopenharmony_ci cmd->cb(card, cmd, -ECANCELED); 3258c2ecf20Sopenharmony_ci kmem_cache_free(creg_cmd_pool, cmd); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci cmd = card->creg_ctrl.active_cmd; 3298c2ecf20Sopenharmony_ci card->creg_ctrl.active_cmd = NULL; 3308c2ecf20Sopenharmony_ci if (cmd) { 3318c2ecf20Sopenharmony_ci if (timer_pending(&card->creg_ctrl.cmd_timer)) 3328c2ecf20Sopenharmony_ci del_timer_sync(&card->creg_ctrl.cmd_timer); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (cmd->cb) 3358c2ecf20Sopenharmony_ci cmd->cb(card, cmd, -ECANCELED); 3368c2ecf20Sopenharmony_ci kmem_cache_free(creg_cmd_pool, cmd); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci card->creg_ctrl.active = 0; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci spin_unlock_bh(&card->creg_ctrl.lock); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci card->creg_ctrl.reset = 0; 3438c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->irq_lock, flags); 3448c2ecf20Sopenharmony_ci rsxx_enable_ier_and_isr(card, CR_INTR_CREG | CR_INTR_EVENT); 3458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->irq_lock, flags); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mutex_unlock(&card->creg_ctrl.reset_lock); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* Used for synchronous accesses */ 3518c2ecf20Sopenharmony_cistruct creg_completion { 3528c2ecf20Sopenharmony_ci struct completion *cmd_done; 3538c2ecf20Sopenharmony_ci int st; 3548c2ecf20Sopenharmony_ci u32 creg_status; 3558c2ecf20Sopenharmony_ci}; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void creg_cmd_done_cb(struct rsxx_cardinfo *card, 3588c2ecf20Sopenharmony_ci struct creg_cmd *cmd, 3598c2ecf20Sopenharmony_ci int st) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct creg_completion *cmd_completion; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci cmd_completion = cmd->cb_private; 3648c2ecf20Sopenharmony_ci BUG_ON(!cmd_completion); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci cmd_completion->st = st; 3678c2ecf20Sopenharmony_ci cmd_completion->creg_status = cmd->status; 3688c2ecf20Sopenharmony_ci complete(cmd_completion->cmd_done); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int __issue_creg_rw(struct rsxx_cardinfo *card, 3728c2ecf20Sopenharmony_ci unsigned int op, 3738c2ecf20Sopenharmony_ci unsigned int addr, 3748c2ecf20Sopenharmony_ci unsigned int cnt8, 3758c2ecf20Sopenharmony_ci void *buf, 3768c2ecf20Sopenharmony_ci int stream, 3778c2ecf20Sopenharmony_ci unsigned int *hw_stat) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(cmd_done); 3808c2ecf20Sopenharmony_ci struct creg_completion completion; 3818c2ecf20Sopenharmony_ci unsigned long timeout; 3828c2ecf20Sopenharmony_ci int st; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci completion.cmd_done = &cmd_done; 3858c2ecf20Sopenharmony_ci completion.st = 0; 3868c2ecf20Sopenharmony_ci completion.creg_status = 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci st = creg_queue_cmd(card, op, addr, cnt8, buf, stream, creg_cmd_done_cb, 3898c2ecf20Sopenharmony_ci &completion); 3908c2ecf20Sopenharmony_ci if (st) 3918c2ecf20Sopenharmony_ci return st; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * This timeout is necessary for unresponsive hardware. The additional 3958c2ecf20Sopenharmony_ci * 20 seconds to used to guarantee that each cregs requests has time to 3968c2ecf20Sopenharmony_ci * complete. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci timeout = msecs_to_jiffies(CREG_TIMEOUT_MSEC * 3998c2ecf20Sopenharmony_ci card->creg_ctrl.q_depth + 20000); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * The creg interface is guaranteed to complete. It has a timeout 4038c2ecf20Sopenharmony_ci * mechanism that will kick in if hardware does not respond. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci st = wait_for_completion_timeout(completion.cmd_done, timeout); 4068c2ecf20Sopenharmony_ci if (st == 0) { 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * This is really bad, because the kernel timer did not 4098c2ecf20Sopenharmony_ci * expire and notify us of a timeout! 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci dev_crit(CARD_TO_DEV(card), 4128c2ecf20Sopenharmony_ci "cregs timer failed\n"); 4138c2ecf20Sopenharmony_ci creg_reset(card); 4148c2ecf20Sopenharmony_ci return -EIO; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci *hw_stat = completion.creg_status; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (completion.st) { 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * This read is needed to verify that there has not been any 4228c2ecf20Sopenharmony_ci * extreme errors that might have occurred, i.e. EEH. The 4238c2ecf20Sopenharmony_ci * function iowrite32 will not detect EEH errors, so it is 4248c2ecf20Sopenharmony_ci * necessary that we recover if such an error is the reason 4258c2ecf20Sopenharmony_ci * for the timeout. This is a dummy read. 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ci ioread32(card->regmap + SCRATCH); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci dev_warn(CARD_TO_DEV(card), 4308c2ecf20Sopenharmony_ci "creg command failed(%d x%08x)\n", 4318c2ecf20Sopenharmony_ci completion.st, addr); 4328c2ecf20Sopenharmony_ci return completion.st; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int issue_creg_rw(struct rsxx_cardinfo *card, 4398c2ecf20Sopenharmony_ci u32 addr, 4408c2ecf20Sopenharmony_ci unsigned int size8, 4418c2ecf20Sopenharmony_ci void *data, 4428c2ecf20Sopenharmony_ci int stream, 4438c2ecf20Sopenharmony_ci int read) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci unsigned int hw_stat; 4468c2ecf20Sopenharmony_ci unsigned int xfer; 4478c2ecf20Sopenharmony_ci unsigned int op; 4488c2ecf20Sopenharmony_ci int st; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci op = read ? CREG_OP_READ : CREG_OP_WRITE; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci do { 4538c2ecf20Sopenharmony_ci xfer = min_t(unsigned int, size8, MAX_CREG_DATA8); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci st = __issue_creg_rw(card, op, addr, xfer, 4568c2ecf20Sopenharmony_ci data, stream, &hw_stat); 4578c2ecf20Sopenharmony_ci if (st) 4588c2ecf20Sopenharmony_ci return st; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci data = (char *)data + xfer; 4618c2ecf20Sopenharmony_ci addr += xfer; 4628c2ecf20Sopenharmony_ci size8 -= xfer; 4638c2ecf20Sopenharmony_ci } while (size8); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/* ---------------------------- Public API ---------------------------------- */ 4698c2ecf20Sopenharmony_ciint rsxx_creg_write(struct rsxx_cardinfo *card, 4708c2ecf20Sopenharmony_ci u32 addr, 4718c2ecf20Sopenharmony_ci unsigned int size8, 4728c2ecf20Sopenharmony_ci void *data, 4738c2ecf20Sopenharmony_ci int byte_stream) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci return issue_creg_rw(card, addr, size8, data, byte_stream, 0); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ciint rsxx_creg_read(struct rsxx_cardinfo *card, 4798c2ecf20Sopenharmony_ci u32 addr, 4808c2ecf20Sopenharmony_ci unsigned int size8, 4818c2ecf20Sopenharmony_ci void *data, 4828c2ecf20Sopenharmony_ci int byte_stream) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci return issue_creg_rw(card, addr, size8, data, byte_stream, 1); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ciint rsxx_get_card_state(struct rsxx_cardinfo *card, unsigned int *state) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci return rsxx_creg_read(card, CREG_ADD_CARD_STATE, 4908c2ecf20Sopenharmony_ci sizeof(*state), state, 0); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ciint rsxx_get_card_size8(struct rsxx_cardinfo *card, u64 *size8) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci unsigned int size; 4968c2ecf20Sopenharmony_ci int st; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci st = rsxx_creg_read(card, CREG_ADD_CARD_SIZE, 4998c2ecf20Sopenharmony_ci sizeof(size), &size, 0); 5008c2ecf20Sopenharmony_ci if (st) 5018c2ecf20Sopenharmony_ci return st; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci *size8 = (u64)size * RSXX_HW_BLK_SIZE; 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ciint rsxx_get_num_targets(struct rsxx_cardinfo *card, 5088c2ecf20Sopenharmony_ci unsigned int *n_targets) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci return rsxx_creg_read(card, CREG_ADD_NUM_TARGETS, 5118c2ecf20Sopenharmony_ci sizeof(*n_targets), n_targets, 0); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciint rsxx_get_card_capabilities(struct rsxx_cardinfo *card, 5158c2ecf20Sopenharmony_ci u32 *capabilities) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci return rsxx_creg_read(card, CREG_ADD_CAPABILITIES, 5188c2ecf20Sopenharmony_ci sizeof(*capabilities), capabilities, 0); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ciint rsxx_issue_card_cmd(struct rsxx_cardinfo *card, u32 cmd) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci return rsxx_creg_write(card, CREG_ADD_CARD_CMD, 5248c2ecf20Sopenharmony_ci sizeof(cmd), &cmd, 0); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/*----------------- HW Log Functions -------------------*/ 5298c2ecf20Sopenharmony_cistatic void hw_log_msg(struct rsxx_cardinfo *card, const char *str, int len) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci static char level; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* 5348c2ecf20Sopenharmony_ci * New messages start with "<#>", where # is the log level. Messages 5358c2ecf20Sopenharmony_ci * that extend past the log buffer will use the previous level 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci if ((len > 3) && (str[0] == '<') && (str[2] == '>')) { 5388c2ecf20Sopenharmony_ci level = str[1]; 5398c2ecf20Sopenharmony_ci str += 3; /* Skip past the log level. */ 5408c2ecf20Sopenharmony_ci len -= 3; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci switch (level) { 5448c2ecf20Sopenharmony_ci case '0': 5458c2ecf20Sopenharmony_ci dev_emerg(CARD_TO_DEV(card), "HW: %.*s", len, str); 5468c2ecf20Sopenharmony_ci break; 5478c2ecf20Sopenharmony_ci case '1': 5488c2ecf20Sopenharmony_ci dev_alert(CARD_TO_DEV(card), "HW: %.*s", len, str); 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci case '2': 5518c2ecf20Sopenharmony_ci dev_crit(CARD_TO_DEV(card), "HW: %.*s", len, str); 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci case '3': 5548c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), "HW: %.*s", len, str); 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci case '4': 5578c2ecf20Sopenharmony_ci dev_warn(CARD_TO_DEV(card), "HW: %.*s", len, str); 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci case '5': 5608c2ecf20Sopenharmony_ci dev_notice(CARD_TO_DEV(card), "HW: %.*s", len, str); 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci case '6': 5638c2ecf20Sopenharmony_ci dev_info(CARD_TO_DEV(card), "HW: %.*s", len, str); 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci case '7': 5668c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "HW: %.*s", len, str); 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci default: 5698c2ecf20Sopenharmony_ci dev_info(CARD_TO_DEV(card), "HW: %.*s", len, str); 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/* 5758c2ecf20Sopenharmony_ci * The substrncpy function copies the src string (which includes the 5768c2ecf20Sopenharmony_ci * terminating '\0' character), up to the count into the dest pointer. 5778c2ecf20Sopenharmony_ci * Returns the number of bytes copied to dest. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_cistatic int substrncpy(char *dest, const char *src, int count) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci int max_cnt = count; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci while (count) { 5848c2ecf20Sopenharmony_ci count--; 5858c2ecf20Sopenharmony_ci *dest = *src; 5868c2ecf20Sopenharmony_ci if (*dest == '\0') 5878c2ecf20Sopenharmony_ci break; 5888c2ecf20Sopenharmony_ci src++; 5898c2ecf20Sopenharmony_ci dest++; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci return max_cnt - count; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic void read_hw_log_done(struct rsxx_cardinfo *card, 5968c2ecf20Sopenharmony_ci struct creg_cmd *cmd, 5978c2ecf20Sopenharmony_ci int st) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci char *buf; 6008c2ecf20Sopenharmony_ci char *log_str; 6018c2ecf20Sopenharmony_ci int cnt; 6028c2ecf20Sopenharmony_ci int len; 6038c2ecf20Sopenharmony_ci int off; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci buf = cmd->buf; 6068c2ecf20Sopenharmony_ci off = 0; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* Failed getting the log message */ 6098c2ecf20Sopenharmony_ci if (st) 6108c2ecf20Sopenharmony_ci return; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci while (off < cmd->cnt8) { 6138c2ecf20Sopenharmony_ci log_str = &card->log.buf[card->log.buf_len]; 6148c2ecf20Sopenharmony_ci cnt = min(cmd->cnt8 - off, LOG_BUF_SIZE8 - card->log.buf_len); 6158c2ecf20Sopenharmony_ci len = substrncpy(log_str, &buf[off], cnt); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci off += len; 6188c2ecf20Sopenharmony_ci card->log.buf_len += len; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* 6218c2ecf20Sopenharmony_ci * Flush the log if we've hit the end of a message or if we've 6228c2ecf20Sopenharmony_ci * run out of buffer space. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci if ((log_str[len - 1] == '\0') || 6258c2ecf20Sopenharmony_ci (card->log.buf_len == LOG_BUF_SIZE8)) { 6268c2ecf20Sopenharmony_ci if (card->log.buf_len != 1) /* Don't log blank lines. */ 6278c2ecf20Sopenharmony_ci hw_log_msg(card, card->log.buf, 6288c2ecf20Sopenharmony_ci card->log.buf_len); 6298c2ecf20Sopenharmony_ci card->log.buf_len = 0; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (cmd->status & CREG_STAT_LOG_PENDING) 6358c2ecf20Sopenharmony_ci rsxx_read_hw_log(card); 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ciint rsxx_read_hw_log(struct rsxx_cardinfo *card) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci int st; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci st = creg_queue_cmd(card, CREG_OP_READ, CREG_ADD_LOG, 6438c2ecf20Sopenharmony_ci sizeof(card->log.tmp), card->log.tmp, 6448c2ecf20Sopenharmony_ci 1, read_hw_log_done, NULL); 6458c2ecf20Sopenharmony_ci if (st) 6468c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 6478c2ecf20Sopenharmony_ci "Failed getting log text\n"); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return st; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci/*-------------- IOCTL REG Access ------------------*/ 6538c2ecf20Sopenharmony_cistatic int issue_reg_cmd(struct rsxx_cardinfo *card, 6548c2ecf20Sopenharmony_ci struct rsxx_reg_access *cmd, 6558c2ecf20Sopenharmony_ci int read) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci unsigned int op = read ? CREG_OP_READ : CREG_OP_WRITE; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return __issue_creg_rw(card, op, cmd->addr, cmd->cnt, cmd->data, 6608c2ecf20Sopenharmony_ci cmd->stream, &cmd->stat); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ciint rsxx_reg_access(struct rsxx_cardinfo *card, 6648c2ecf20Sopenharmony_ci struct rsxx_reg_access __user *ucmd, 6658c2ecf20Sopenharmony_ci int read) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct rsxx_reg_access cmd; 6688c2ecf20Sopenharmony_ci int st; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci st = copy_from_user(&cmd, ucmd, sizeof(cmd)); 6718c2ecf20Sopenharmony_ci if (st) 6728c2ecf20Sopenharmony_ci return -EFAULT; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (cmd.cnt > RSXX_MAX_REG_CNT) 6758c2ecf20Sopenharmony_ci return -EFAULT; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci st = issue_reg_cmd(card, &cmd, read); 6788c2ecf20Sopenharmony_ci if (st) 6798c2ecf20Sopenharmony_ci return st; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci st = put_user(cmd.stat, &ucmd->stat); 6828c2ecf20Sopenharmony_ci if (st) 6838c2ecf20Sopenharmony_ci return -EFAULT; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (read) { 6868c2ecf20Sopenharmony_ci st = copy_to_user(ucmd->data, cmd.data, cmd.cnt); 6878c2ecf20Sopenharmony_ci if (st) 6888c2ecf20Sopenharmony_ci return -EFAULT; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return 0; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_civoid rsxx_eeh_save_issued_creg(struct rsxx_cardinfo *card) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct creg_cmd *cmd = NULL; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci cmd = card->creg_ctrl.active_cmd; 6998c2ecf20Sopenharmony_ci card->creg_ctrl.active_cmd = NULL; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (cmd) { 7028c2ecf20Sopenharmony_ci del_timer_sync(&card->creg_ctrl.cmd_timer); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci spin_lock_bh(&card->creg_ctrl.lock); 7058c2ecf20Sopenharmony_ci list_add(&cmd->list, &card->creg_ctrl.queue); 7068c2ecf20Sopenharmony_ci card->creg_ctrl.q_depth++; 7078c2ecf20Sopenharmony_ci card->creg_ctrl.active = 0; 7088c2ecf20Sopenharmony_ci spin_unlock_bh(&card->creg_ctrl.lock); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_civoid rsxx_kick_creg_queue(struct rsxx_cardinfo *card) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci spin_lock_bh(&card->creg_ctrl.lock); 7158c2ecf20Sopenharmony_ci if (!list_empty(&card->creg_ctrl.queue)) 7168c2ecf20Sopenharmony_ci creg_kick_queue(card); 7178c2ecf20Sopenharmony_ci spin_unlock_bh(&card->creg_ctrl.lock); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/*------------ Initialization & Setup --------------*/ 7218c2ecf20Sopenharmony_ciint rsxx_creg_setup(struct rsxx_cardinfo *card) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci card->creg_ctrl.active_cmd = NULL; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci card->creg_ctrl.creg_wq = 7268c2ecf20Sopenharmony_ci create_singlethread_workqueue(DRIVER_NAME"_creg"); 7278c2ecf20Sopenharmony_ci if (!card->creg_ctrl.creg_wq) 7288c2ecf20Sopenharmony_ci return -ENOMEM; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci INIT_WORK(&card->creg_ctrl.done_work, creg_cmd_done); 7318c2ecf20Sopenharmony_ci mutex_init(&card->creg_ctrl.reset_lock); 7328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->creg_ctrl.queue); 7338c2ecf20Sopenharmony_ci spin_lock_init(&card->creg_ctrl.lock); 7348c2ecf20Sopenharmony_ci timer_setup(&card->creg_ctrl.cmd_timer, creg_cmd_timed_out, 0); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return 0; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_civoid rsxx_creg_destroy(struct rsxx_cardinfo *card) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci struct creg_cmd *cmd; 7428c2ecf20Sopenharmony_ci struct creg_cmd *tmp; 7438c2ecf20Sopenharmony_ci int cnt = 0; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* Cancel outstanding commands */ 7468c2ecf20Sopenharmony_ci spin_lock_bh(&card->creg_ctrl.lock); 7478c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, tmp, &card->creg_ctrl.queue, list) { 7488c2ecf20Sopenharmony_ci list_del(&cmd->list); 7498c2ecf20Sopenharmony_ci if (cmd->cb) 7508c2ecf20Sopenharmony_ci cmd->cb(card, cmd, -ECANCELED); 7518c2ecf20Sopenharmony_ci kmem_cache_free(creg_cmd_pool, cmd); 7528c2ecf20Sopenharmony_ci cnt++; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (cnt) 7568c2ecf20Sopenharmony_ci dev_info(CARD_TO_DEV(card), 7578c2ecf20Sopenharmony_ci "Canceled %d queue creg commands\n", cnt); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci cmd = card->creg_ctrl.active_cmd; 7608c2ecf20Sopenharmony_ci card->creg_ctrl.active_cmd = NULL; 7618c2ecf20Sopenharmony_ci if (cmd) { 7628c2ecf20Sopenharmony_ci if (timer_pending(&card->creg_ctrl.cmd_timer)) 7638c2ecf20Sopenharmony_ci del_timer_sync(&card->creg_ctrl.cmd_timer); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (cmd->cb) 7668c2ecf20Sopenharmony_ci cmd->cb(card, cmd, -ECANCELED); 7678c2ecf20Sopenharmony_ci dev_info(CARD_TO_DEV(card), 7688c2ecf20Sopenharmony_ci "Canceled active creg command\n"); 7698c2ecf20Sopenharmony_ci kmem_cache_free(creg_cmd_pool, cmd); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci spin_unlock_bh(&card->creg_ctrl.lock); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci cancel_work_sync(&card->creg_ctrl.done_work); 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ciint rsxx_creg_init(void) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci creg_cmd_pool = KMEM_CACHE(creg_cmd, SLAB_HWCACHE_ALIGN); 7808c2ecf20Sopenharmony_ci if (!creg_cmd_pool) 7818c2ecf20Sopenharmony_ci return -ENOMEM; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return 0; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_civoid rsxx_creg_cleanup(void) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci kmem_cache_destroy(creg_cmd_pool); 7898c2ecf20Sopenharmony_ci} 790