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