162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CXL Flash Device Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation
662306a36Sopenharmony_ci *             Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2015 IBM Corporation
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/pci.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/unaligned.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
1962306a36Sopenharmony_ci#include <scsi/scsi_host.h>
2062306a36Sopenharmony_ci#include <uapi/scsi/cxlflash_ioctl.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "main.h"
2362306a36Sopenharmony_ci#include "sislite.h"
2462306a36Sopenharmony_ci#include "common.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciMODULE_DESCRIPTION(CXLFLASH_ADAPTER_NAME);
2762306a36Sopenharmony_ciMODULE_AUTHOR("Manoj N. Kumar <manoj@linux.vnet.ibm.com>");
2862306a36Sopenharmony_ciMODULE_AUTHOR("Matthew R. Ochs <mrochs@linux.vnet.ibm.com>");
2962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic struct class *cxlflash_class;
3262306a36Sopenharmony_cistatic u32 cxlflash_major;
3362306a36Sopenharmony_cistatic DECLARE_BITMAP(cxlflash_minor, CXLFLASH_MAX_ADAPTERS);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/**
3662306a36Sopenharmony_ci * process_cmd_err() - command error handler
3762306a36Sopenharmony_ci * @cmd:	AFU command that experienced the error.
3862306a36Sopenharmony_ci * @scp:	SCSI command associated with the AFU command in error.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * Translates error bits from AFU command to SCSI command results.
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_cistatic void process_cmd_err(struct afu_cmd *cmd, struct scsi_cmnd *scp)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct afu *afu = cmd->parent;
4562306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
4662306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
4762306a36Sopenharmony_ci	struct sisl_ioasa *ioasa;
4862306a36Sopenharmony_ci	u32 resid;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	ioasa = &(cmd->sa);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (ioasa->rc.flags & SISL_RC_FLAGS_UNDERRUN) {
5362306a36Sopenharmony_ci		resid = ioasa->resid;
5462306a36Sopenharmony_ci		scsi_set_resid(scp, resid);
5562306a36Sopenharmony_ci		dev_dbg(dev, "%s: cmd underrun cmd = %p scp = %p, resid = %d\n",
5662306a36Sopenharmony_ci			__func__, cmd, scp, resid);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (ioasa->rc.flags & SISL_RC_FLAGS_OVERRUN) {
6062306a36Sopenharmony_ci		dev_dbg(dev, "%s: cmd underrun cmd = %p scp = %p\n",
6162306a36Sopenharmony_ci			__func__, cmd, scp);
6262306a36Sopenharmony_ci		scp->result = (DID_ERROR << 16);
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	dev_dbg(dev, "%s: cmd failed afu_rc=%02x scsi_rc=%02x fc_rc=%02x "
6662306a36Sopenharmony_ci		"afu_extra=%02x scsi_extra=%02x fc_extra=%02x\n", __func__,
6762306a36Sopenharmony_ci		ioasa->rc.afu_rc, ioasa->rc.scsi_rc, ioasa->rc.fc_rc,
6862306a36Sopenharmony_ci		ioasa->afu_extra, ioasa->scsi_extra, ioasa->fc_extra);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (ioasa->rc.scsi_rc) {
7162306a36Sopenharmony_ci		/* We have a SCSI status */
7262306a36Sopenharmony_ci		if (ioasa->rc.flags & SISL_RC_FLAGS_SENSE_VALID) {
7362306a36Sopenharmony_ci			memcpy(scp->sense_buffer, ioasa->sense_data,
7462306a36Sopenharmony_ci			       SISL_SENSE_DATA_LEN);
7562306a36Sopenharmony_ci			scp->result = ioasa->rc.scsi_rc;
7662306a36Sopenharmony_ci		} else
7762306a36Sopenharmony_ci			scp->result = ioasa->rc.scsi_rc | (DID_ERROR << 16);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci	 * We encountered an error. Set scp->result based on nature
8262306a36Sopenharmony_ci	 * of error.
8362306a36Sopenharmony_ci	 */
8462306a36Sopenharmony_ci	if (ioasa->rc.fc_rc) {
8562306a36Sopenharmony_ci		/* We have an FC status */
8662306a36Sopenharmony_ci		switch (ioasa->rc.fc_rc) {
8762306a36Sopenharmony_ci		case SISL_FC_RC_LINKDOWN:
8862306a36Sopenharmony_ci			scp->result = (DID_REQUEUE << 16);
8962306a36Sopenharmony_ci			break;
9062306a36Sopenharmony_ci		case SISL_FC_RC_RESID:
9162306a36Sopenharmony_ci			/* This indicates an FCP resid underrun */
9262306a36Sopenharmony_ci			if (!(ioasa->rc.flags & SISL_RC_FLAGS_OVERRUN)) {
9362306a36Sopenharmony_ci				/* If the SISL_RC_FLAGS_OVERRUN flag was set,
9462306a36Sopenharmony_ci				 * then we will handle this error else where.
9562306a36Sopenharmony_ci				 * If not then we must handle it here.
9662306a36Sopenharmony_ci				 * This is probably an AFU bug.
9762306a36Sopenharmony_ci				 */
9862306a36Sopenharmony_ci				scp->result = (DID_ERROR << 16);
9962306a36Sopenharmony_ci			}
10062306a36Sopenharmony_ci			break;
10162306a36Sopenharmony_ci		case SISL_FC_RC_RESIDERR:
10262306a36Sopenharmony_ci			/* Resid mismatch between adapter and device */
10362306a36Sopenharmony_ci		case SISL_FC_RC_TGTABORT:
10462306a36Sopenharmony_ci		case SISL_FC_RC_ABORTOK:
10562306a36Sopenharmony_ci		case SISL_FC_RC_ABORTFAIL:
10662306a36Sopenharmony_ci		case SISL_FC_RC_NOLOGI:
10762306a36Sopenharmony_ci		case SISL_FC_RC_ABORTPEND:
10862306a36Sopenharmony_ci		case SISL_FC_RC_WRABORTPEND:
10962306a36Sopenharmony_ci		case SISL_FC_RC_NOEXP:
11062306a36Sopenharmony_ci		case SISL_FC_RC_INUSE:
11162306a36Sopenharmony_ci			scp->result = (DID_ERROR << 16);
11262306a36Sopenharmony_ci			break;
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (ioasa->rc.afu_rc) {
11762306a36Sopenharmony_ci		/* We have an AFU error */
11862306a36Sopenharmony_ci		switch (ioasa->rc.afu_rc) {
11962306a36Sopenharmony_ci		case SISL_AFU_RC_NO_CHANNELS:
12062306a36Sopenharmony_ci			scp->result = (DID_NO_CONNECT << 16);
12162306a36Sopenharmony_ci			break;
12262306a36Sopenharmony_ci		case SISL_AFU_RC_DATA_DMA_ERR:
12362306a36Sopenharmony_ci			switch (ioasa->afu_extra) {
12462306a36Sopenharmony_ci			case SISL_AFU_DMA_ERR_PAGE_IN:
12562306a36Sopenharmony_ci				/* Retry */
12662306a36Sopenharmony_ci				scp->result = (DID_IMM_RETRY << 16);
12762306a36Sopenharmony_ci				break;
12862306a36Sopenharmony_ci			case SISL_AFU_DMA_ERR_INVALID_EA:
12962306a36Sopenharmony_ci			default:
13062306a36Sopenharmony_ci				scp->result = (DID_ERROR << 16);
13162306a36Sopenharmony_ci			}
13262306a36Sopenharmony_ci			break;
13362306a36Sopenharmony_ci		case SISL_AFU_RC_OUT_OF_DATA_BUFS:
13462306a36Sopenharmony_ci			/* Retry */
13562306a36Sopenharmony_ci			scp->result = (DID_ERROR << 16);
13662306a36Sopenharmony_ci			break;
13762306a36Sopenharmony_ci		default:
13862306a36Sopenharmony_ci			scp->result = (DID_ERROR << 16);
13962306a36Sopenharmony_ci		}
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/**
14462306a36Sopenharmony_ci * cmd_complete() - command completion handler
14562306a36Sopenharmony_ci * @cmd:	AFU command that has completed.
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci * For SCSI commands this routine prepares and submits commands that have
14862306a36Sopenharmony_ci * either completed or timed out to the SCSI stack. For internal commands
14962306a36Sopenharmony_ci * (TMF or AFU), this routine simply notifies the originator that the
15062306a36Sopenharmony_ci * command has completed.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistatic void cmd_complete(struct afu_cmd *cmd)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct scsi_cmnd *scp;
15562306a36Sopenharmony_ci	ulong lock_flags;
15662306a36Sopenharmony_ci	struct afu *afu = cmd->parent;
15762306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
15862306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
15962306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(afu, cmd->hwq_index);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	spin_lock_irqsave(&hwq->hsq_slock, lock_flags);
16262306a36Sopenharmony_ci	list_del(&cmd->list);
16362306a36Sopenharmony_ci	spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (cmd->scp) {
16662306a36Sopenharmony_ci		scp = cmd->scp;
16762306a36Sopenharmony_ci		if (unlikely(cmd->sa.ioasc))
16862306a36Sopenharmony_ci			process_cmd_err(cmd, scp);
16962306a36Sopenharmony_ci		else
17062306a36Sopenharmony_ci			scp->result = (DID_OK << 16);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		dev_dbg_ratelimited(dev, "%s:scp=%p result=%08x ioasc=%08x\n",
17362306a36Sopenharmony_ci				    __func__, scp, scp->result, cmd->sa.ioasc);
17462306a36Sopenharmony_ci		scsi_done(scp);
17562306a36Sopenharmony_ci	} else if (cmd->cmd_tmf) {
17662306a36Sopenharmony_ci		spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
17762306a36Sopenharmony_ci		cfg->tmf_active = false;
17862306a36Sopenharmony_ci		wake_up_all_locked(&cfg->tmf_waitq);
17962306a36Sopenharmony_ci		spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
18062306a36Sopenharmony_ci	} else
18162306a36Sopenharmony_ci		complete(&cmd->cevent);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/**
18562306a36Sopenharmony_ci * flush_pending_cmds() - flush all pending commands on this hardware queue
18662306a36Sopenharmony_ci * @hwq:	Hardware queue to flush.
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * The hardware send queue lock associated with this hardware queue must be
18962306a36Sopenharmony_ci * held when calling this routine.
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_cistatic void flush_pending_cmds(struct hwq *hwq)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = hwq->afu->parent;
19462306a36Sopenharmony_ci	struct afu_cmd *cmd, *tmp;
19562306a36Sopenharmony_ci	struct scsi_cmnd *scp;
19662306a36Sopenharmony_ci	ulong lock_flags;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, tmp, &hwq->pending_cmds, list) {
19962306a36Sopenharmony_ci		/* Bypass command when on a doneq, cmd_complete() will handle */
20062306a36Sopenharmony_ci		if (!list_empty(&cmd->queue))
20162306a36Sopenharmony_ci			continue;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		list_del(&cmd->list);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		if (cmd->scp) {
20662306a36Sopenharmony_ci			scp = cmd->scp;
20762306a36Sopenharmony_ci			scp->result = (DID_IMM_RETRY << 16);
20862306a36Sopenharmony_ci			scsi_done(scp);
20962306a36Sopenharmony_ci		} else {
21062306a36Sopenharmony_ci			cmd->cmd_aborted = true;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci			if (cmd->cmd_tmf) {
21362306a36Sopenharmony_ci				spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
21462306a36Sopenharmony_ci				cfg->tmf_active = false;
21562306a36Sopenharmony_ci				wake_up_all_locked(&cfg->tmf_waitq);
21662306a36Sopenharmony_ci				spin_unlock_irqrestore(&cfg->tmf_slock,
21762306a36Sopenharmony_ci						       lock_flags);
21862306a36Sopenharmony_ci			} else
21962306a36Sopenharmony_ci				complete(&cmd->cevent);
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/**
22562306a36Sopenharmony_ci * context_reset() - reset context via specified register
22662306a36Sopenharmony_ci * @hwq:	Hardware queue owning the context to be reset.
22762306a36Sopenharmony_ci * @reset_reg:	MMIO register to perform reset.
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * When the reset is successful, the SISLite specification guarantees that
23062306a36Sopenharmony_ci * the AFU has aborted all currently pending I/O. Accordingly, these commands
23162306a36Sopenharmony_ci * must be flushed.
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic int context_reset(struct hwq *hwq, __be64 __iomem *reset_reg)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = hwq->afu->parent;
23862306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
23962306a36Sopenharmony_ci	int rc = -ETIMEDOUT;
24062306a36Sopenharmony_ci	int nretry = 0;
24162306a36Sopenharmony_ci	u64 val = 0x1;
24262306a36Sopenharmony_ci	ulong lock_flags;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	dev_dbg(dev, "%s: hwq=%p\n", __func__, hwq);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	spin_lock_irqsave(&hwq->hsq_slock, lock_flags);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	writeq_be(val, reset_reg);
24962306a36Sopenharmony_ci	do {
25062306a36Sopenharmony_ci		val = readq_be(reset_reg);
25162306a36Sopenharmony_ci		if ((val & 0x1) == 0x0) {
25262306a36Sopenharmony_ci			rc = 0;
25362306a36Sopenharmony_ci			break;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		/* Double delay each time */
25762306a36Sopenharmony_ci		udelay(1 << nretry);
25862306a36Sopenharmony_ci	} while (nretry++ < MC_ROOM_RETRY_CNT);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (!rc)
26162306a36Sopenharmony_ci		flush_pending_cmds(hwq);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d, val=%016llx nretry=%d\n",
26662306a36Sopenharmony_ci		__func__, rc, val, nretry);
26762306a36Sopenharmony_ci	return rc;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/**
27162306a36Sopenharmony_ci * context_reset_ioarrin() - reset context via IOARRIN register
27262306a36Sopenharmony_ci * @hwq:	Hardware queue owning the context to be reset.
27362306a36Sopenharmony_ci *
27462306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_cistatic int context_reset_ioarrin(struct hwq *hwq)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	return context_reset(hwq, &hwq->host_map->ioarrin);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/**
28262306a36Sopenharmony_ci * context_reset_sq() - reset context via SQ_CONTEXT_RESET register
28362306a36Sopenharmony_ci * @hwq:	Hardware queue owning the context to be reset.
28462306a36Sopenharmony_ci *
28562306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
28662306a36Sopenharmony_ci */
28762306a36Sopenharmony_cistatic int context_reset_sq(struct hwq *hwq)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	return context_reset(hwq, &hwq->host_map->sq_ctx_reset);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/**
29362306a36Sopenharmony_ci * send_cmd_ioarrin() - sends an AFU command via IOARRIN register
29462306a36Sopenharmony_ci * @afu:	AFU associated with the host.
29562306a36Sopenharmony_ci * @cmd:	AFU command to send.
29662306a36Sopenharmony_ci *
29762306a36Sopenharmony_ci * Return:
29862306a36Sopenharmony_ci *	0 on success, SCSI_MLQUEUE_HOST_BUSY on failure
29962306a36Sopenharmony_ci */
30062306a36Sopenharmony_cistatic int send_cmd_ioarrin(struct afu *afu, struct afu_cmd *cmd)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
30362306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
30462306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(afu, cmd->hwq_index);
30562306a36Sopenharmony_ci	int rc = 0;
30662306a36Sopenharmony_ci	s64 room;
30762306a36Sopenharmony_ci	ulong lock_flags;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/*
31062306a36Sopenharmony_ci	 * To avoid the performance penalty of MMIO, spread the update of
31162306a36Sopenharmony_ci	 * 'room' over multiple commands.
31262306a36Sopenharmony_ci	 */
31362306a36Sopenharmony_ci	spin_lock_irqsave(&hwq->hsq_slock, lock_flags);
31462306a36Sopenharmony_ci	if (--hwq->room < 0) {
31562306a36Sopenharmony_ci		room = readq_be(&hwq->host_map->cmd_room);
31662306a36Sopenharmony_ci		if (room <= 0) {
31762306a36Sopenharmony_ci			dev_dbg_ratelimited(dev, "%s: no cmd_room to send "
31862306a36Sopenharmony_ci					    "0x%02X, room=0x%016llX\n",
31962306a36Sopenharmony_ci					    __func__, cmd->rcb.cdb[0], room);
32062306a36Sopenharmony_ci			hwq->room = 0;
32162306a36Sopenharmony_ci			rc = SCSI_MLQUEUE_HOST_BUSY;
32262306a36Sopenharmony_ci			goto out;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci		hwq->room = room - 1;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	list_add(&cmd->list, &hwq->pending_cmds);
32862306a36Sopenharmony_ci	writeq_be((u64)&cmd->rcb, &hwq->host_map->ioarrin);
32962306a36Sopenharmony_ciout:
33062306a36Sopenharmony_ci	spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags);
33162306a36Sopenharmony_ci	dev_dbg_ratelimited(dev, "%s: cmd=%p len=%u ea=%016llx rc=%d\n",
33262306a36Sopenharmony_ci		__func__, cmd, cmd->rcb.data_len, cmd->rcb.data_ea, rc);
33362306a36Sopenharmony_ci	return rc;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/**
33762306a36Sopenharmony_ci * send_cmd_sq() - sends an AFU command via SQ ring
33862306a36Sopenharmony_ci * @afu:	AFU associated with the host.
33962306a36Sopenharmony_ci * @cmd:	AFU command to send.
34062306a36Sopenharmony_ci *
34162306a36Sopenharmony_ci * Return:
34262306a36Sopenharmony_ci *	0 on success, SCSI_MLQUEUE_HOST_BUSY on failure
34362306a36Sopenharmony_ci */
34462306a36Sopenharmony_cistatic int send_cmd_sq(struct afu *afu, struct afu_cmd *cmd)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
34762306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
34862306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(afu, cmd->hwq_index);
34962306a36Sopenharmony_ci	int rc = 0;
35062306a36Sopenharmony_ci	int newval;
35162306a36Sopenharmony_ci	ulong lock_flags;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	newval = atomic_dec_if_positive(&hwq->hsq_credits);
35462306a36Sopenharmony_ci	if (newval <= 0) {
35562306a36Sopenharmony_ci		rc = SCSI_MLQUEUE_HOST_BUSY;
35662306a36Sopenharmony_ci		goto out;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	cmd->rcb.ioasa = &cmd->sa;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	spin_lock_irqsave(&hwq->hsq_slock, lock_flags);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	*hwq->hsq_curr = cmd->rcb;
36462306a36Sopenharmony_ci	if (hwq->hsq_curr < hwq->hsq_end)
36562306a36Sopenharmony_ci		hwq->hsq_curr++;
36662306a36Sopenharmony_ci	else
36762306a36Sopenharmony_ci		hwq->hsq_curr = hwq->hsq_start;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	list_add(&cmd->list, &hwq->pending_cmds);
37062306a36Sopenharmony_ci	writeq_be((u64)hwq->hsq_curr, &hwq->host_map->sq_tail);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags);
37362306a36Sopenharmony_ciout:
37462306a36Sopenharmony_ci	dev_dbg(dev, "%s: cmd=%p len=%u ea=%016llx ioasa=%p rc=%d curr=%p "
37562306a36Sopenharmony_ci	       "head=%016llx tail=%016llx\n", __func__, cmd, cmd->rcb.data_len,
37662306a36Sopenharmony_ci	       cmd->rcb.data_ea, cmd->rcb.ioasa, rc, hwq->hsq_curr,
37762306a36Sopenharmony_ci	       readq_be(&hwq->host_map->sq_head),
37862306a36Sopenharmony_ci	       readq_be(&hwq->host_map->sq_tail));
37962306a36Sopenharmony_ci	return rc;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/**
38362306a36Sopenharmony_ci * wait_resp() - polls for a response or timeout to a sent AFU command
38462306a36Sopenharmony_ci * @afu:	AFU associated with the host.
38562306a36Sopenharmony_ci * @cmd:	AFU command that was sent.
38662306a36Sopenharmony_ci *
38762306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
38862306a36Sopenharmony_ci */
38962306a36Sopenharmony_cistatic int wait_resp(struct afu *afu, struct afu_cmd *cmd)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
39262306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
39362306a36Sopenharmony_ci	int rc = 0;
39462306a36Sopenharmony_ci	ulong timeout = msecs_to_jiffies(cmd->rcb.timeout * 2 * 1000);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	timeout = wait_for_completion_timeout(&cmd->cevent, timeout);
39762306a36Sopenharmony_ci	if (!timeout)
39862306a36Sopenharmony_ci		rc = -ETIMEDOUT;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (cmd->cmd_aborted)
40162306a36Sopenharmony_ci		rc = -EAGAIN;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (unlikely(cmd->sa.ioasc != 0)) {
40462306a36Sopenharmony_ci		dev_err(dev, "%s: cmd %02x failed, ioasc=%08x\n",
40562306a36Sopenharmony_ci			__func__, cmd->rcb.cdb[0], cmd->sa.ioasc);
40662306a36Sopenharmony_ci		rc = -EIO;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return rc;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci/**
41362306a36Sopenharmony_ci * cmd_to_target_hwq() - selects a target hardware queue for a SCSI command
41462306a36Sopenharmony_ci * @host:	SCSI host associated with device.
41562306a36Sopenharmony_ci * @scp:	SCSI command to send.
41662306a36Sopenharmony_ci * @afu:	SCSI command to send.
41762306a36Sopenharmony_ci *
41862306a36Sopenharmony_ci * Hashes a command based upon the hardware queue mode.
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * Return: Trusted index of target hardware queue
42162306a36Sopenharmony_ci */
42262306a36Sopenharmony_cistatic u32 cmd_to_target_hwq(struct Scsi_Host *host, struct scsi_cmnd *scp,
42362306a36Sopenharmony_ci			     struct afu *afu)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	u32 tag;
42662306a36Sopenharmony_ci	u32 hwq = 0;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (afu->num_hwqs == 1)
42962306a36Sopenharmony_ci		return 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	switch (afu->hwq_mode) {
43262306a36Sopenharmony_ci	case HWQ_MODE_RR:
43362306a36Sopenharmony_ci		hwq = afu->hwq_rr_count++ % afu->num_hwqs;
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci	case HWQ_MODE_TAG:
43662306a36Sopenharmony_ci		tag = blk_mq_unique_tag(scsi_cmd_to_rq(scp));
43762306a36Sopenharmony_ci		hwq = blk_mq_unique_tag_to_hwq(tag);
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	case HWQ_MODE_CPU:
44062306a36Sopenharmony_ci		hwq = smp_processor_id() % afu->num_hwqs;
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci	default:
44362306a36Sopenharmony_ci		WARN_ON_ONCE(1);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return hwq;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/**
45062306a36Sopenharmony_ci * send_tmf() - sends a Task Management Function (TMF)
45162306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
45262306a36Sopenharmony_ci * @sdev:	SCSI device destined for TMF.
45362306a36Sopenharmony_ci * @tmfcmd:	TMF command to send.
45462306a36Sopenharmony_ci *
45562306a36Sopenharmony_ci * Return:
45662306a36Sopenharmony_ci *	0 on success, SCSI_MLQUEUE_HOST_BUSY or -errno on failure
45762306a36Sopenharmony_ci */
45862306a36Sopenharmony_cistatic int send_tmf(struct cxlflash_cfg *cfg, struct scsi_device *sdev,
45962306a36Sopenharmony_ci		    u64 tmfcmd)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
46262306a36Sopenharmony_ci	struct afu_cmd *cmd = NULL;
46362306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
46462306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(afu, PRIMARY_HWQ);
46562306a36Sopenharmony_ci	bool needs_deletion = false;
46662306a36Sopenharmony_ci	char *buf = NULL;
46762306a36Sopenharmony_ci	ulong lock_flags;
46862306a36Sopenharmony_ci	int rc = 0;
46962306a36Sopenharmony_ci	ulong to;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	buf = kzalloc(sizeof(*cmd) + __alignof__(*cmd) - 1, GFP_KERNEL);
47262306a36Sopenharmony_ci	if (unlikely(!buf)) {
47362306a36Sopenharmony_ci		dev_err(dev, "%s: no memory for command\n", __func__);
47462306a36Sopenharmony_ci		rc = -ENOMEM;
47562306a36Sopenharmony_ci		goto out;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	cmd = (struct afu_cmd *)PTR_ALIGN(buf, __alignof__(*cmd));
47962306a36Sopenharmony_ci	INIT_LIST_HEAD(&cmd->queue);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* When Task Management Function is active do not send another */
48262306a36Sopenharmony_ci	spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
48362306a36Sopenharmony_ci	if (cfg->tmf_active)
48462306a36Sopenharmony_ci		wait_event_interruptible_lock_irq(cfg->tmf_waitq,
48562306a36Sopenharmony_ci						  !cfg->tmf_active,
48662306a36Sopenharmony_ci						  cfg->tmf_slock);
48762306a36Sopenharmony_ci	cfg->tmf_active = true;
48862306a36Sopenharmony_ci	spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	cmd->parent = afu;
49162306a36Sopenharmony_ci	cmd->cmd_tmf = true;
49262306a36Sopenharmony_ci	cmd->hwq_index = hwq->index;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	cmd->rcb.ctx_id = hwq->ctx_hndl;
49562306a36Sopenharmony_ci	cmd->rcb.msi = SISL_MSI_RRQ_UPDATED;
49662306a36Sopenharmony_ci	cmd->rcb.port_sel = CHAN2PORTMASK(sdev->channel);
49762306a36Sopenharmony_ci	cmd->rcb.lun_id = lun_to_lunid(sdev->lun);
49862306a36Sopenharmony_ci	cmd->rcb.req_flags = (SISL_REQ_FLAGS_PORT_LUN_ID |
49962306a36Sopenharmony_ci			      SISL_REQ_FLAGS_SUP_UNDERRUN |
50062306a36Sopenharmony_ci			      SISL_REQ_FLAGS_TMF_CMD);
50162306a36Sopenharmony_ci	memcpy(cmd->rcb.cdb, &tmfcmd, sizeof(tmfcmd));
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	rc = afu->send_cmd(afu, cmd);
50462306a36Sopenharmony_ci	if (unlikely(rc)) {
50562306a36Sopenharmony_ci		spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
50662306a36Sopenharmony_ci		cfg->tmf_active = false;
50762306a36Sopenharmony_ci		spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
50862306a36Sopenharmony_ci		goto out;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
51262306a36Sopenharmony_ci	to = msecs_to_jiffies(5000);
51362306a36Sopenharmony_ci	to = wait_event_interruptible_lock_irq_timeout(cfg->tmf_waitq,
51462306a36Sopenharmony_ci						       !cfg->tmf_active,
51562306a36Sopenharmony_ci						       cfg->tmf_slock,
51662306a36Sopenharmony_ci						       to);
51762306a36Sopenharmony_ci	if (!to) {
51862306a36Sopenharmony_ci		dev_err(dev, "%s: TMF timed out\n", __func__);
51962306a36Sopenharmony_ci		rc = -ETIMEDOUT;
52062306a36Sopenharmony_ci		needs_deletion = true;
52162306a36Sopenharmony_ci	} else if (cmd->cmd_aborted) {
52262306a36Sopenharmony_ci		dev_err(dev, "%s: TMF aborted\n", __func__);
52362306a36Sopenharmony_ci		rc = -EAGAIN;
52462306a36Sopenharmony_ci	} else if (cmd->sa.ioasc) {
52562306a36Sopenharmony_ci		dev_err(dev, "%s: TMF failed ioasc=%08x\n",
52662306a36Sopenharmony_ci			__func__, cmd->sa.ioasc);
52762306a36Sopenharmony_ci		rc = -EIO;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci	cfg->tmf_active = false;
53062306a36Sopenharmony_ci	spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (needs_deletion) {
53362306a36Sopenharmony_ci		spin_lock_irqsave(&hwq->hsq_slock, lock_flags);
53462306a36Sopenharmony_ci		list_del(&cmd->list);
53562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags);
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ciout:
53862306a36Sopenharmony_ci	kfree(buf);
53962306a36Sopenharmony_ci	return rc;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci/**
54362306a36Sopenharmony_ci * cxlflash_driver_info() - information handler for this host driver
54462306a36Sopenharmony_ci * @host:	SCSI host associated with device.
54562306a36Sopenharmony_ci *
54662306a36Sopenharmony_ci * Return: A string describing the device.
54762306a36Sopenharmony_ci */
54862306a36Sopenharmony_cistatic const char *cxlflash_driver_info(struct Scsi_Host *host)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	return CXLFLASH_ADAPTER_NAME;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/**
55462306a36Sopenharmony_ci * cxlflash_queuecommand() - sends a mid-layer request
55562306a36Sopenharmony_ci * @host:	SCSI host associated with device.
55662306a36Sopenharmony_ci * @scp:	SCSI command to send.
55762306a36Sopenharmony_ci *
55862306a36Sopenharmony_ci * Return: 0 on success, SCSI_MLQUEUE_HOST_BUSY on failure
55962306a36Sopenharmony_ci */
56062306a36Sopenharmony_cistatic int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(host);
56362306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
56462306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
56562306a36Sopenharmony_ci	struct afu_cmd *cmd = sc_to_afuci(scp);
56662306a36Sopenharmony_ci	struct scatterlist *sg = scsi_sglist(scp);
56762306a36Sopenharmony_ci	int hwq_index = cmd_to_target_hwq(host, scp, afu);
56862306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(afu, hwq_index);
56962306a36Sopenharmony_ci	u16 req_flags = SISL_REQ_FLAGS_SUP_UNDERRUN;
57062306a36Sopenharmony_ci	ulong lock_flags;
57162306a36Sopenharmony_ci	int rc = 0;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	dev_dbg_ratelimited(dev, "%s: (scp=%p) %d/%d/%d/%llu "
57462306a36Sopenharmony_ci			    "cdb=(%08x-%08x-%08x-%08x)\n",
57562306a36Sopenharmony_ci			    __func__, scp, host->host_no, scp->device->channel,
57662306a36Sopenharmony_ci			    scp->device->id, scp->device->lun,
57762306a36Sopenharmony_ci			    get_unaligned_be32(&((u32 *)scp->cmnd)[0]),
57862306a36Sopenharmony_ci			    get_unaligned_be32(&((u32 *)scp->cmnd)[1]),
57962306a36Sopenharmony_ci			    get_unaligned_be32(&((u32 *)scp->cmnd)[2]),
58062306a36Sopenharmony_ci			    get_unaligned_be32(&((u32 *)scp->cmnd)[3]));
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/*
58362306a36Sopenharmony_ci	 * If a Task Management Function is active, wait for it to complete
58462306a36Sopenharmony_ci	 * before continuing with regular commands.
58562306a36Sopenharmony_ci	 */
58662306a36Sopenharmony_ci	spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
58762306a36Sopenharmony_ci	if (cfg->tmf_active) {
58862306a36Sopenharmony_ci		spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
58962306a36Sopenharmony_ci		rc = SCSI_MLQUEUE_HOST_BUSY;
59062306a36Sopenharmony_ci		goto out;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci	spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	switch (cfg->state) {
59562306a36Sopenharmony_ci	case STATE_PROBING:
59662306a36Sopenharmony_ci	case STATE_PROBED:
59762306a36Sopenharmony_ci	case STATE_RESET:
59862306a36Sopenharmony_ci		dev_dbg_ratelimited(dev, "%s: device is in reset\n", __func__);
59962306a36Sopenharmony_ci		rc = SCSI_MLQUEUE_HOST_BUSY;
60062306a36Sopenharmony_ci		goto out;
60162306a36Sopenharmony_ci	case STATE_FAILTERM:
60262306a36Sopenharmony_ci		dev_dbg_ratelimited(dev, "%s: device has failed\n", __func__);
60362306a36Sopenharmony_ci		scp->result = (DID_NO_CONNECT << 16);
60462306a36Sopenharmony_ci		scsi_done(scp);
60562306a36Sopenharmony_ci		rc = 0;
60662306a36Sopenharmony_ci		goto out;
60762306a36Sopenharmony_ci	default:
60862306a36Sopenharmony_ci		atomic_inc(&afu->cmds_active);
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (likely(sg)) {
61362306a36Sopenharmony_ci		cmd->rcb.data_len = sg->length;
61462306a36Sopenharmony_ci		cmd->rcb.data_ea = (uintptr_t)sg_virt(sg);
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	cmd->scp = scp;
61862306a36Sopenharmony_ci	cmd->parent = afu;
61962306a36Sopenharmony_ci	cmd->hwq_index = hwq_index;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	cmd->sa.ioasc = 0;
62262306a36Sopenharmony_ci	cmd->rcb.ctx_id = hwq->ctx_hndl;
62362306a36Sopenharmony_ci	cmd->rcb.msi = SISL_MSI_RRQ_UPDATED;
62462306a36Sopenharmony_ci	cmd->rcb.port_sel = CHAN2PORTMASK(scp->device->channel);
62562306a36Sopenharmony_ci	cmd->rcb.lun_id = lun_to_lunid(scp->device->lun);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (scp->sc_data_direction == DMA_TO_DEVICE)
62862306a36Sopenharmony_ci		req_flags |= SISL_REQ_FLAGS_HOST_WRITE;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	cmd->rcb.req_flags = req_flags;
63162306a36Sopenharmony_ci	memcpy(cmd->rcb.cdb, scp->cmnd, sizeof(cmd->rcb.cdb));
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	rc = afu->send_cmd(afu, cmd);
63462306a36Sopenharmony_ci	atomic_dec(&afu->cmds_active);
63562306a36Sopenharmony_ciout:
63662306a36Sopenharmony_ci	return rc;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/**
64062306a36Sopenharmony_ci * cxlflash_wait_for_pci_err_recovery() - wait for error recovery during probe
64162306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
64262306a36Sopenharmony_ci */
64362306a36Sopenharmony_cistatic void cxlflash_wait_for_pci_err_recovery(struct cxlflash_cfg *cfg)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct pci_dev *pdev = cfg->dev;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (pci_channel_offline(pdev))
64862306a36Sopenharmony_ci		wait_event_timeout(cfg->reset_waitq,
64962306a36Sopenharmony_ci				   !pci_channel_offline(pdev),
65062306a36Sopenharmony_ci				   CXLFLASH_PCI_ERROR_RECOVERY_TIMEOUT);
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci/**
65462306a36Sopenharmony_ci * free_mem() - free memory associated with the AFU
65562306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
65662306a36Sopenharmony_ci */
65762306a36Sopenharmony_cistatic void free_mem(struct cxlflash_cfg *cfg)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if (cfg->afu) {
66262306a36Sopenharmony_ci		free_pages((ulong)afu, get_order(sizeof(struct afu)));
66362306a36Sopenharmony_ci		cfg->afu = NULL;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/**
66862306a36Sopenharmony_ci * cxlflash_reset_sync() - synchronizing point for asynchronous resets
66962306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
67062306a36Sopenharmony_ci */
67162306a36Sopenharmony_cistatic void cxlflash_reset_sync(struct cxlflash_cfg *cfg)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	if (cfg->async_reset_cookie == 0)
67462306a36Sopenharmony_ci		return;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Wait until all async calls prior to this cookie have completed */
67762306a36Sopenharmony_ci	async_synchronize_cookie(cfg->async_reset_cookie + 1);
67862306a36Sopenharmony_ci	cfg->async_reset_cookie = 0;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/**
68262306a36Sopenharmony_ci * stop_afu() - stops the AFU command timers and unmaps the MMIO space
68362306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * Safe to call with AFU in a partially allocated/initialized state.
68662306a36Sopenharmony_ci *
68762306a36Sopenharmony_ci * Cancels scheduled worker threads, waits for any active internal AFU
68862306a36Sopenharmony_ci * commands to timeout, disables IRQ polling and then unmaps the MMIO space.
68962306a36Sopenharmony_ci */
69062306a36Sopenharmony_cistatic void stop_afu(struct cxlflash_cfg *cfg)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
69362306a36Sopenharmony_ci	struct hwq *hwq;
69462306a36Sopenharmony_ci	int i;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	cancel_work_sync(&cfg->work_q);
69762306a36Sopenharmony_ci	if (!current_is_async())
69862306a36Sopenharmony_ci		cxlflash_reset_sync(cfg);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (likely(afu)) {
70162306a36Sopenharmony_ci		while (atomic_read(&afu->cmds_active))
70262306a36Sopenharmony_ci			ssleep(1);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		if (afu_is_irqpoll_enabled(afu)) {
70562306a36Sopenharmony_ci			for (i = 0; i < afu->num_hwqs; i++) {
70662306a36Sopenharmony_ci				hwq = get_hwq(afu, i);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci				irq_poll_disable(&hwq->irqpoll);
70962306a36Sopenharmony_ci			}
71062306a36Sopenharmony_ci		}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		if (likely(afu->afu_map)) {
71362306a36Sopenharmony_ci			cfg->ops->psa_unmap(afu->afu_map);
71462306a36Sopenharmony_ci			afu->afu_map = NULL;
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci/**
72062306a36Sopenharmony_ci * term_intr() - disables all AFU interrupts
72162306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
72262306a36Sopenharmony_ci * @level:	Depth of allocation, where to begin waterfall tear down.
72362306a36Sopenharmony_ci * @index:	Index of the hardware queue.
72462306a36Sopenharmony_ci *
72562306a36Sopenharmony_ci * Safe to call with AFU/MC in partially allocated/initialized state.
72662306a36Sopenharmony_ci */
72762306a36Sopenharmony_cistatic void term_intr(struct cxlflash_cfg *cfg, enum undo_level level,
72862306a36Sopenharmony_ci		      u32 index)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
73162306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
73262306a36Sopenharmony_ci	struct hwq *hwq;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (!afu) {
73562306a36Sopenharmony_ci		dev_err(dev, "%s: returning with NULL afu\n", __func__);
73662306a36Sopenharmony_ci		return;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	hwq = get_hwq(afu, index);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (!hwq->ctx_cookie) {
74262306a36Sopenharmony_ci		dev_err(dev, "%s: returning with NULL MC\n", __func__);
74362306a36Sopenharmony_ci		return;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	switch (level) {
74762306a36Sopenharmony_ci	case UNMAP_THREE:
74862306a36Sopenharmony_ci		/* SISL_MSI_ASYNC_ERROR is setup only for the primary HWQ */
74962306a36Sopenharmony_ci		if (index == PRIMARY_HWQ)
75062306a36Sopenharmony_ci			cfg->ops->unmap_afu_irq(hwq->ctx_cookie, 3, hwq);
75162306a36Sopenharmony_ci		fallthrough;
75262306a36Sopenharmony_ci	case UNMAP_TWO:
75362306a36Sopenharmony_ci		cfg->ops->unmap_afu_irq(hwq->ctx_cookie, 2, hwq);
75462306a36Sopenharmony_ci		fallthrough;
75562306a36Sopenharmony_ci	case UNMAP_ONE:
75662306a36Sopenharmony_ci		cfg->ops->unmap_afu_irq(hwq->ctx_cookie, 1, hwq);
75762306a36Sopenharmony_ci		fallthrough;
75862306a36Sopenharmony_ci	case FREE_IRQ:
75962306a36Sopenharmony_ci		cfg->ops->free_afu_irqs(hwq->ctx_cookie);
76062306a36Sopenharmony_ci		fallthrough;
76162306a36Sopenharmony_ci	case UNDO_NOOP:
76262306a36Sopenharmony_ci		/* No action required */
76362306a36Sopenharmony_ci		break;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci/**
76862306a36Sopenharmony_ci * term_mc() - terminates the master context
76962306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
77062306a36Sopenharmony_ci * @index:	Index of the hardware queue.
77162306a36Sopenharmony_ci *
77262306a36Sopenharmony_ci * Safe to call with AFU/MC in partially allocated/initialized state.
77362306a36Sopenharmony_ci */
77462306a36Sopenharmony_cistatic void term_mc(struct cxlflash_cfg *cfg, u32 index)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
77762306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
77862306a36Sopenharmony_ci	struct hwq *hwq;
77962306a36Sopenharmony_ci	ulong lock_flags;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (!afu) {
78262306a36Sopenharmony_ci		dev_err(dev, "%s: returning with NULL afu\n", __func__);
78362306a36Sopenharmony_ci		return;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	hwq = get_hwq(afu, index);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (!hwq->ctx_cookie) {
78962306a36Sopenharmony_ci		dev_err(dev, "%s: returning with NULL MC\n", __func__);
79062306a36Sopenharmony_ci		return;
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	WARN_ON(cfg->ops->stop_context(hwq->ctx_cookie));
79462306a36Sopenharmony_ci	if (index != PRIMARY_HWQ)
79562306a36Sopenharmony_ci		WARN_ON(cfg->ops->release_context(hwq->ctx_cookie));
79662306a36Sopenharmony_ci	hwq->ctx_cookie = NULL;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	spin_lock_irqsave(&hwq->hrrq_slock, lock_flags);
79962306a36Sopenharmony_ci	hwq->hrrq_online = false;
80062306a36Sopenharmony_ci	spin_unlock_irqrestore(&hwq->hrrq_slock, lock_flags);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	spin_lock_irqsave(&hwq->hsq_slock, lock_flags);
80362306a36Sopenharmony_ci	flush_pending_cmds(hwq);
80462306a36Sopenharmony_ci	spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags);
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci/**
80862306a36Sopenharmony_ci * term_afu() - terminates the AFU
80962306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
81062306a36Sopenharmony_ci *
81162306a36Sopenharmony_ci * Safe to call with AFU/MC in partially allocated/initialized state.
81262306a36Sopenharmony_ci */
81362306a36Sopenharmony_cistatic void term_afu(struct cxlflash_cfg *cfg)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
81662306a36Sopenharmony_ci	int k;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	/*
81962306a36Sopenharmony_ci	 * Tear down is carefully orchestrated to ensure
82062306a36Sopenharmony_ci	 * no interrupts can come in when the problem state
82162306a36Sopenharmony_ci	 * area is unmapped.
82262306a36Sopenharmony_ci	 *
82362306a36Sopenharmony_ci	 * 1) Disable all AFU interrupts for each master
82462306a36Sopenharmony_ci	 * 2) Unmap the problem state area
82562306a36Sopenharmony_ci	 * 3) Stop each master context
82662306a36Sopenharmony_ci	 */
82762306a36Sopenharmony_ci	for (k = cfg->afu->num_hwqs - 1; k >= 0; k--)
82862306a36Sopenharmony_ci		term_intr(cfg, UNMAP_THREE, k);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	stop_afu(cfg);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	for (k = cfg->afu->num_hwqs - 1; k >= 0; k--)
83362306a36Sopenharmony_ci		term_mc(cfg, k);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning\n", __func__);
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci/**
83962306a36Sopenharmony_ci * notify_shutdown() - notifies device of pending shutdown
84062306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
84162306a36Sopenharmony_ci * @wait:	Whether to wait for shutdown processing to complete.
84262306a36Sopenharmony_ci *
84362306a36Sopenharmony_ci * This function will notify the AFU that the adapter is being shutdown
84462306a36Sopenharmony_ci * and will wait for shutdown processing to complete if wait is true.
84562306a36Sopenharmony_ci * This notification should flush pending I/Os to the device and halt
84662306a36Sopenharmony_ci * further I/Os until the next AFU reset is issued and device restarted.
84762306a36Sopenharmony_ci */
84862306a36Sopenharmony_cistatic void notify_shutdown(struct cxlflash_cfg *cfg, bool wait)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
85162306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
85262306a36Sopenharmony_ci	struct dev_dependent_vals *ddv;
85362306a36Sopenharmony_ci	__be64 __iomem *fc_port_regs;
85462306a36Sopenharmony_ci	u64 reg, status;
85562306a36Sopenharmony_ci	int i, retry_cnt = 0;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	ddv = (struct dev_dependent_vals *)cfg->dev_id->driver_data;
85862306a36Sopenharmony_ci	if (!(ddv->flags & CXLFLASH_NOTIFY_SHUTDOWN))
85962306a36Sopenharmony_ci		return;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (!afu || !afu->afu_map) {
86262306a36Sopenharmony_ci		dev_dbg(dev, "%s: Problem state area not mapped\n", __func__);
86362306a36Sopenharmony_ci		return;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	/* Notify AFU */
86762306a36Sopenharmony_ci	for (i = 0; i < cfg->num_fc_ports; i++) {
86862306a36Sopenharmony_ci		fc_port_regs = get_fc_port_regs(cfg, i);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		reg = readq_be(&fc_port_regs[FC_CONFIG2 / 8]);
87162306a36Sopenharmony_ci		reg |= SISL_FC_SHUTDOWN_NORMAL;
87262306a36Sopenharmony_ci		writeq_be(reg, &fc_port_regs[FC_CONFIG2 / 8]);
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!wait)
87662306a36Sopenharmony_ci		return;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/* Wait up to 1.5 seconds for shutdown processing to complete */
87962306a36Sopenharmony_ci	for (i = 0; i < cfg->num_fc_ports; i++) {
88062306a36Sopenharmony_ci		fc_port_regs = get_fc_port_regs(cfg, i);
88162306a36Sopenharmony_ci		retry_cnt = 0;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci		while (true) {
88462306a36Sopenharmony_ci			status = readq_be(&fc_port_regs[FC_STATUS / 8]);
88562306a36Sopenharmony_ci			if (status & SISL_STATUS_SHUTDOWN_COMPLETE)
88662306a36Sopenharmony_ci				break;
88762306a36Sopenharmony_ci			if (++retry_cnt >= MC_RETRY_CNT) {
88862306a36Sopenharmony_ci				dev_dbg(dev, "%s: port %d shutdown processing "
88962306a36Sopenharmony_ci					"not yet completed\n", __func__, i);
89062306a36Sopenharmony_ci				break;
89162306a36Sopenharmony_ci			}
89262306a36Sopenharmony_ci			msleep(100 * retry_cnt);
89362306a36Sopenharmony_ci		}
89462306a36Sopenharmony_ci	}
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci/**
89862306a36Sopenharmony_ci * cxlflash_get_minor() - gets the first available minor number
89962306a36Sopenharmony_ci *
90062306a36Sopenharmony_ci * Return: Unique minor number that can be used to create the character device.
90162306a36Sopenharmony_ci */
90262306a36Sopenharmony_cistatic int cxlflash_get_minor(void)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	int minor;
90562306a36Sopenharmony_ci	long bit;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	bit = find_first_zero_bit(cxlflash_minor, CXLFLASH_MAX_ADAPTERS);
90862306a36Sopenharmony_ci	if (bit >= CXLFLASH_MAX_ADAPTERS)
90962306a36Sopenharmony_ci		return -1;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	minor = bit & MINORMASK;
91262306a36Sopenharmony_ci	set_bit(minor, cxlflash_minor);
91362306a36Sopenharmony_ci	return minor;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci/**
91762306a36Sopenharmony_ci * cxlflash_put_minor() - releases the minor number
91862306a36Sopenharmony_ci * @minor:	Minor number that is no longer needed.
91962306a36Sopenharmony_ci */
92062306a36Sopenharmony_cistatic void cxlflash_put_minor(int minor)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	clear_bit(minor, cxlflash_minor);
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci/**
92662306a36Sopenharmony_ci * cxlflash_release_chrdev() - release the character device for the host
92762306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
92862306a36Sopenharmony_ci */
92962306a36Sopenharmony_cistatic void cxlflash_release_chrdev(struct cxlflash_cfg *cfg)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	device_unregister(cfg->chardev);
93262306a36Sopenharmony_ci	cfg->chardev = NULL;
93362306a36Sopenharmony_ci	cdev_del(&cfg->cdev);
93462306a36Sopenharmony_ci	cxlflash_put_minor(MINOR(cfg->cdev.dev));
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/**
93862306a36Sopenharmony_ci * cxlflash_remove() - PCI entry point to tear down host
93962306a36Sopenharmony_ci * @pdev:	PCI device associated with the host.
94062306a36Sopenharmony_ci *
94162306a36Sopenharmony_ci * Safe to use as a cleanup in partially allocated/initialized state. Note that
94262306a36Sopenharmony_ci * the reset_waitq is flushed as part of the stop/termination of user contexts.
94362306a36Sopenharmony_ci */
94462306a36Sopenharmony_cistatic void cxlflash_remove(struct pci_dev *pdev)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = pci_get_drvdata(pdev);
94762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
94862306a36Sopenharmony_ci	ulong lock_flags;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (!pci_is_enabled(pdev)) {
95162306a36Sopenharmony_ci		dev_dbg(dev, "%s: Device is disabled\n", __func__);
95262306a36Sopenharmony_ci		return;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* Yield to running recovery threads before continuing with remove */
95662306a36Sopenharmony_ci	wait_event(cfg->reset_waitq, cfg->state != STATE_RESET &&
95762306a36Sopenharmony_ci				     cfg->state != STATE_PROBING);
95862306a36Sopenharmony_ci	spin_lock_irqsave(&cfg->tmf_slock, lock_flags);
95962306a36Sopenharmony_ci	if (cfg->tmf_active)
96062306a36Sopenharmony_ci		wait_event_interruptible_lock_irq(cfg->tmf_waitq,
96162306a36Sopenharmony_ci						  !cfg->tmf_active,
96262306a36Sopenharmony_ci						  cfg->tmf_slock);
96362306a36Sopenharmony_ci	spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	/* Notify AFU and wait for shutdown processing to complete */
96662306a36Sopenharmony_ci	notify_shutdown(cfg, true);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	cfg->state = STATE_FAILTERM;
96962306a36Sopenharmony_ci	cxlflash_stop_term_user_contexts(cfg);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	switch (cfg->init_state) {
97262306a36Sopenharmony_ci	case INIT_STATE_CDEV:
97362306a36Sopenharmony_ci		cxlflash_release_chrdev(cfg);
97462306a36Sopenharmony_ci		fallthrough;
97562306a36Sopenharmony_ci	case INIT_STATE_SCSI:
97662306a36Sopenharmony_ci		cxlflash_term_local_luns(cfg);
97762306a36Sopenharmony_ci		scsi_remove_host(cfg->host);
97862306a36Sopenharmony_ci		fallthrough;
97962306a36Sopenharmony_ci	case INIT_STATE_AFU:
98062306a36Sopenharmony_ci		term_afu(cfg);
98162306a36Sopenharmony_ci		fallthrough;
98262306a36Sopenharmony_ci	case INIT_STATE_PCI:
98362306a36Sopenharmony_ci		cfg->ops->destroy_afu(cfg->afu_cookie);
98462306a36Sopenharmony_ci		pci_disable_device(pdev);
98562306a36Sopenharmony_ci		fallthrough;
98662306a36Sopenharmony_ci	case INIT_STATE_NONE:
98762306a36Sopenharmony_ci		free_mem(cfg);
98862306a36Sopenharmony_ci		scsi_host_put(cfg->host);
98962306a36Sopenharmony_ci		break;
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning\n", __func__);
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci/**
99662306a36Sopenharmony_ci * alloc_mem() - allocates the AFU and its command pool
99762306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
99862306a36Sopenharmony_ci *
99962306a36Sopenharmony_ci * A partially allocated state remains on failure.
100062306a36Sopenharmony_ci *
100162306a36Sopenharmony_ci * Return:
100262306a36Sopenharmony_ci *	0 on success
100362306a36Sopenharmony_ci *	-ENOMEM on failure to allocate memory
100462306a36Sopenharmony_ci */
100562306a36Sopenharmony_cistatic int alloc_mem(struct cxlflash_cfg *cfg)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	int rc = 0;
100862306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/* AFU is ~28k, i.e. only one 64k page or up to seven 4k pages */
101162306a36Sopenharmony_ci	cfg->afu = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
101262306a36Sopenharmony_ci					    get_order(sizeof(struct afu)));
101362306a36Sopenharmony_ci	if (unlikely(!cfg->afu)) {
101462306a36Sopenharmony_ci		dev_err(dev, "%s: cannot get %d free pages\n",
101562306a36Sopenharmony_ci			__func__, get_order(sizeof(struct afu)));
101662306a36Sopenharmony_ci		rc = -ENOMEM;
101762306a36Sopenharmony_ci		goto out;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci	cfg->afu->parent = cfg;
102062306a36Sopenharmony_ci	cfg->afu->desired_hwqs = CXLFLASH_DEF_HWQS;
102162306a36Sopenharmony_ci	cfg->afu->afu_map = NULL;
102262306a36Sopenharmony_ciout:
102362306a36Sopenharmony_ci	return rc;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci/**
102762306a36Sopenharmony_ci * init_pci() - initializes the host as a PCI device
102862306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
102962306a36Sopenharmony_ci *
103062306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
103162306a36Sopenharmony_ci */
103262306a36Sopenharmony_cistatic int init_pci(struct cxlflash_cfg *cfg)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	struct pci_dev *pdev = cfg->dev;
103562306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
103662306a36Sopenharmony_ci	int rc = 0;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	rc = pci_enable_device(pdev);
103962306a36Sopenharmony_ci	if (rc || pci_channel_offline(pdev)) {
104062306a36Sopenharmony_ci		if (pci_channel_offline(pdev)) {
104162306a36Sopenharmony_ci			cxlflash_wait_for_pci_err_recovery(cfg);
104262306a36Sopenharmony_ci			rc = pci_enable_device(pdev);
104362306a36Sopenharmony_ci		}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		if (rc) {
104662306a36Sopenharmony_ci			dev_err(dev, "%s: Cannot enable adapter\n", __func__);
104762306a36Sopenharmony_ci			cxlflash_wait_for_pci_err_recovery(cfg);
104862306a36Sopenharmony_ci			goto out;
104962306a36Sopenharmony_ci		}
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ciout:
105362306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
105462306a36Sopenharmony_ci	return rc;
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci/**
105862306a36Sopenharmony_ci * init_scsi() - adds the host to the SCSI stack and kicks off host scan
105962306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
106062306a36Sopenharmony_ci *
106162306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
106262306a36Sopenharmony_ci */
106362306a36Sopenharmony_cistatic int init_scsi(struct cxlflash_cfg *cfg)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct pci_dev *pdev = cfg->dev;
106662306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
106762306a36Sopenharmony_ci	int rc = 0;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	rc = scsi_add_host(cfg->host, &pdev->dev);
107062306a36Sopenharmony_ci	if (rc) {
107162306a36Sopenharmony_ci		dev_err(dev, "%s: scsi_add_host failed rc=%d\n", __func__, rc);
107262306a36Sopenharmony_ci		goto out;
107362306a36Sopenharmony_ci	}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	scsi_scan_host(cfg->host);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ciout:
107862306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
107962306a36Sopenharmony_ci	return rc;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci/**
108362306a36Sopenharmony_ci * set_port_online() - transitions the specified host FC port to online state
108462306a36Sopenharmony_ci * @fc_regs:	Top of MMIO region defined for specified port.
108562306a36Sopenharmony_ci *
108662306a36Sopenharmony_ci * The provided MMIO region must be mapped prior to call. Online state means
108762306a36Sopenharmony_ci * that the FC link layer has synced, completed the handshaking process, and
108862306a36Sopenharmony_ci * is ready for login to start.
108962306a36Sopenharmony_ci */
109062306a36Sopenharmony_cistatic void set_port_online(__be64 __iomem *fc_regs)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	u64 cmdcfg;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	cmdcfg = readq_be(&fc_regs[FC_MTIP_CMDCONFIG / 8]);
109562306a36Sopenharmony_ci	cmdcfg &= (~FC_MTIP_CMDCONFIG_OFFLINE);	/* clear OFF_LINE */
109662306a36Sopenharmony_ci	cmdcfg |= (FC_MTIP_CMDCONFIG_ONLINE);	/* set ON_LINE */
109762306a36Sopenharmony_ci	writeq_be(cmdcfg, &fc_regs[FC_MTIP_CMDCONFIG / 8]);
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci/**
110162306a36Sopenharmony_ci * set_port_offline() - transitions the specified host FC port to offline state
110262306a36Sopenharmony_ci * @fc_regs:	Top of MMIO region defined for specified port.
110362306a36Sopenharmony_ci *
110462306a36Sopenharmony_ci * The provided MMIO region must be mapped prior to call.
110562306a36Sopenharmony_ci */
110662306a36Sopenharmony_cistatic void set_port_offline(__be64 __iomem *fc_regs)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	u64 cmdcfg;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	cmdcfg = readq_be(&fc_regs[FC_MTIP_CMDCONFIG / 8]);
111162306a36Sopenharmony_ci	cmdcfg &= (~FC_MTIP_CMDCONFIG_ONLINE);	/* clear ON_LINE */
111262306a36Sopenharmony_ci	cmdcfg |= (FC_MTIP_CMDCONFIG_OFFLINE);	/* set OFF_LINE */
111362306a36Sopenharmony_ci	writeq_be(cmdcfg, &fc_regs[FC_MTIP_CMDCONFIG / 8]);
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci/**
111762306a36Sopenharmony_ci * wait_port_online() - waits for the specified host FC port come online
111862306a36Sopenharmony_ci * @fc_regs:	Top of MMIO region defined for specified port.
111962306a36Sopenharmony_ci * @delay_us:	Number of microseconds to delay between reading port status.
112062306a36Sopenharmony_ci * @nretry:	Number of cycles to retry reading port status.
112162306a36Sopenharmony_ci *
112262306a36Sopenharmony_ci * The provided MMIO region must be mapped prior to call. This will timeout
112362306a36Sopenharmony_ci * when the cable is not plugged in.
112462306a36Sopenharmony_ci *
112562306a36Sopenharmony_ci * Return:
112662306a36Sopenharmony_ci *	TRUE (1) when the specified port is online
112762306a36Sopenharmony_ci *	FALSE (0) when the specified port fails to come online after timeout
112862306a36Sopenharmony_ci */
112962306a36Sopenharmony_cistatic bool wait_port_online(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	u64 status;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	WARN_ON(delay_us < 1000);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	do {
113662306a36Sopenharmony_ci		msleep(delay_us / 1000);
113762306a36Sopenharmony_ci		status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]);
113862306a36Sopenharmony_ci		if (status == U64_MAX)
113962306a36Sopenharmony_ci			nretry /= 2;
114062306a36Sopenharmony_ci	} while ((status & FC_MTIP_STATUS_MASK) != FC_MTIP_STATUS_ONLINE &&
114162306a36Sopenharmony_ci		 nretry--);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return ((status & FC_MTIP_STATUS_MASK) == FC_MTIP_STATUS_ONLINE);
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci/**
114762306a36Sopenharmony_ci * wait_port_offline() - waits for the specified host FC port go offline
114862306a36Sopenharmony_ci * @fc_regs:	Top of MMIO region defined for specified port.
114962306a36Sopenharmony_ci * @delay_us:	Number of microseconds to delay between reading port status.
115062306a36Sopenharmony_ci * @nretry:	Number of cycles to retry reading port status.
115162306a36Sopenharmony_ci *
115262306a36Sopenharmony_ci * The provided MMIO region must be mapped prior to call.
115362306a36Sopenharmony_ci *
115462306a36Sopenharmony_ci * Return:
115562306a36Sopenharmony_ci *	TRUE (1) when the specified port is offline
115662306a36Sopenharmony_ci *	FALSE (0) when the specified port fails to go offline after timeout
115762306a36Sopenharmony_ci */
115862306a36Sopenharmony_cistatic bool wait_port_offline(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	u64 status;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	WARN_ON(delay_us < 1000);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	do {
116562306a36Sopenharmony_ci		msleep(delay_us / 1000);
116662306a36Sopenharmony_ci		status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]);
116762306a36Sopenharmony_ci		if (status == U64_MAX)
116862306a36Sopenharmony_ci			nretry /= 2;
116962306a36Sopenharmony_ci	} while ((status & FC_MTIP_STATUS_MASK) != FC_MTIP_STATUS_OFFLINE &&
117062306a36Sopenharmony_ci		 nretry--);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	return ((status & FC_MTIP_STATUS_MASK) == FC_MTIP_STATUS_OFFLINE);
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci/**
117662306a36Sopenharmony_ci * afu_set_wwpn() - configures the WWPN for the specified host FC port
117762306a36Sopenharmony_ci * @afu:	AFU associated with the host that owns the specified FC port.
117862306a36Sopenharmony_ci * @port:	Port number being configured.
117962306a36Sopenharmony_ci * @fc_regs:	Top of MMIO region defined for specified port.
118062306a36Sopenharmony_ci * @wwpn:	The world-wide-port-number previously discovered for port.
118162306a36Sopenharmony_ci *
118262306a36Sopenharmony_ci * The provided MMIO region must be mapped prior to call. As part of the
118362306a36Sopenharmony_ci * sequence to configure the WWPN, the port is toggled offline and then back
118462306a36Sopenharmony_ci * online. This toggling action can cause this routine to delay up to a few
118562306a36Sopenharmony_ci * seconds. When configured to use the internal LUN feature of the AFU, a
118662306a36Sopenharmony_ci * failure to come online is overridden.
118762306a36Sopenharmony_ci */
118862306a36Sopenharmony_cistatic void afu_set_wwpn(struct afu *afu, int port, __be64 __iomem *fc_regs,
118962306a36Sopenharmony_ci			 u64 wwpn)
119062306a36Sopenharmony_ci{
119162306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
119262306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	set_port_offline(fc_regs);
119562306a36Sopenharmony_ci	if (!wait_port_offline(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US,
119662306a36Sopenharmony_ci			       FC_PORT_STATUS_RETRY_CNT)) {
119762306a36Sopenharmony_ci		dev_dbg(dev, "%s: wait on port %d to go offline timed out\n",
119862306a36Sopenharmony_ci			__func__, port);
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	writeq_be(wwpn, &fc_regs[FC_PNAME / 8]);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	set_port_online(fc_regs);
120462306a36Sopenharmony_ci	if (!wait_port_online(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US,
120562306a36Sopenharmony_ci			      FC_PORT_STATUS_RETRY_CNT)) {
120662306a36Sopenharmony_ci		dev_dbg(dev, "%s: wait on port %d to go online timed out\n",
120762306a36Sopenharmony_ci			__func__, port);
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci/**
121262306a36Sopenharmony_ci * afu_link_reset() - resets the specified host FC port
121362306a36Sopenharmony_ci * @afu:	AFU associated with the host that owns the specified FC port.
121462306a36Sopenharmony_ci * @port:	Port number being configured.
121562306a36Sopenharmony_ci * @fc_regs:	Top of MMIO region defined for specified port.
121662306a36Sopenharmony_ci *
121762306a36Sopenharmony_ci * The provided MMIO region must be mapped prior to call. The sequence to
121862306a36Sopenharmony_ci * reset the port involves toggling it offline and then back online. This
121962306a36Sopenharmony_ci * action can cause this routine to delay up to a few seconds. An effort
122062306a36Sopenharmony_ci * is made to maintain link with the device by switching to host to use
122162306a36Sopenharmony_ci * the alternate port exclusively while the reset takes place.
122262306a36Sopenharmony_ci * failure to come online is overridden.
122362306a36Sopenharmony_ci */
122462306a36Sopenharmony_cistatic void afu_link_reset(struct afu *afu, int port, __be64 __iomem *fc_regs)
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
122762306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
122862306a36Sopenharmony_ci	u64 port_sel;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* first switch the AFU to the other links, if any */
123162306a36Sopenharmony_ci	port_sel = readq_be(&afu->afu_map->global.regs.afu_port_sel);
123262306a36Sopenharmony_ci	port_sel &= ~(1ULL << port);
123362306a36Sopenharmony_ci	writeq_be(port_sel, &afu->afu_map->global.regs.afu_port_sel);
123462306a36Sopenharmony_ci	cxlflash_afu_sync(afu, 0, 0, AFU_GSYNC);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	set_port_offline(fc_regs);
123762306a36Sopenharmony_ci	if (!wait_port_offline(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US,
123862306a36Sopenharmony_ci			       FC_PORT_STATUS_RETRY_CNT))
123962306a36Sopenharmony_ci		dev_err(dev, "%s: wait on port %d to go offline timed out\n",
124062306a36Sopenharmony_ci			__func__, port);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	set_port_online(fc_regs);
124362306a36Sopenharmony_ci	if (!wait_port_online(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US,
124462306a36Sopenharmony_ci			      FC_PORT_STATUS_RETRY_CNT))
124562306a36Sopenharmony_ci		dev_err(dev, "%s: wait on port %d to go online timed out\n",
124662306a36Sopenharmony_ci			__func__, port);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	/* switch back to include this port */
124962306a36Sopenharmony_ci	port_sel |= (1ULL << port);
125062306a36Sopenharmony_ci	writeq_be(port_sel, &afu->afu_map->global.regs.afu_port_sel);
125162306a36Sopenharmony_ci	cxlflash_afu_sync(afu, 0, 0, AFU_GSYNC);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning port_sel=%016llx\n", __func__, port_sel);
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci/**
125762306a36Sopenharmony_ci * afu_err_intr_init() - clears and initializes the AFU for error interrupts
125862306a36Sopenharmony_ci * @afu:	AFU associated with the host.
125962306a36Sopenharmony_ci */
126062306a36Sopenharmony_cistatic void afu_err_intr_init(struct afu *afu)
126162306a36Sopenharmony_ci{
126262306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
126362306a36Sopenharmony_ci	__be64 __iomem *fc_port_regs;
126462306a36Sopenharmony_ci	int i;
126562306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(afu, PRIMARY_HWQ);
126662306a36Sopenharmony_ci	u64 reg;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	/* global async interrupts: AFU clears afu_ctrl on context exit
126962306a36Sopenharmony_ci	 * if async interrupts were sent to that context. This prevents
127062306a36Sopenharmony_ci	 * the AFU form sending further async interrupts when
127162306a36Sopenharmony_ci	 * there is
127262306a36Sopenharmony_ci	 * nobody to receive them.
127362306a36Sopenharmony_ci	 */
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	/* mask all */
127662306a36Sopenharmony_ci	writeq_be(-1ULL, &afu->afu_map->global.regs.aintr_mask);
127762306a36Sopenharmony_ci	/* set LISN# to send and point to primary master context */
127862306a36Sopenharmony_ci	reg = ((u64) (((hwq->ctx_hndl << 8) | SISL_MSI_ASYNC_ERROR)) << 40);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	if (afu->internal_lun)
128162306a36Sopenharmony_ci		reg |= 1;	/* Bit 63 indicates local lun */
128262306a36Sopenharmony_ci	writeq_be(reg, &afu->afu_map->global.regs.afu_ctrl);
128362306a36Sopenharmony_ci	/* clear all */
128462306a36Sopenharmony_ci	writeq_be(-1ULL, &afu->afu_map->global.regs.aintr_clear);
128562306a36Sopenharmony_ci	/* unmask bits that are of interest */
128662306a36Sopenharmony_ci	/* note: afu can send an interrupt after this step */
128762306a36Sopenharmony_ci	writeq_be(SISL_ASTATUS_MASK, &afu->afu_map->global.regs.aintr_mask);
128862306a36Sopenharmony_ci	/* clear again in case a bit came on after previous clear but before */
128962306a36Sopenharmony_ci	/* unmask */
129062306a36Sopenharmony_ci	writeq_be(-1ULL, &afu->afu_map->global.regs.aintr_clear);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	/* Clear/Set internal lun bits */
129362306a36Sopenharmony_ci	fc_port_regs = get_fc_port_regs(cfg, 0);
129462306a36Sopenharmony_ci	reg = readq_be(&fc_port_regs[FC_CONFIG2 / 8]);
129562306a36Sopenharmony_ci	reg &= SISL_FC_INTERNAL_MASK;
129662306a36Sopenharmony_ci	if (afu->internal_lun)
129762306a36Sopenharmony_ci		reg |= ((u64)(afu->internal_lun - 1) << SISL_FC_INTERNAL_SHIFT);
129862306a36Sopenharmony_ci	writeq_be(reg, &fc_port_regs[FC_CONFIG2 / 8]);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	/* now clear FC errors */
130162306a36Sopenharmony_ci	for (i = 0; i < cfg->num_fc_ports; i++) {
130262306a36Sopenharmony_ci		fc_port_regs = get_fc_port_regs(cfg, i);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci		writeq_be(0xFFFFFFFFU, &fc_port_regs[FC_ERROR / 8]);
130562306a36Sopenharmony_ci		writeq_be(0, &fc_port_regs[FC_ERRCAP / 8]);
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	/* sync interrupts for master's IOARRIN write */
130962306a36Sopenharmony_ci	/* note that unlike asyncs, there can be no pending sync interrupts */
131062306a36Sopenharmony_ci	/* at this time (this is a fresh context and master has not written */
131162306a36Sopenharmony_ci	/* IOARRIN yet), so there is nothing to clear. */
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	/* set LISN#, it is always sent to the context that wrote IOARRIN */
131462306a36Sopenharmony_ci	for (i = 0; i < afu->num_hwqs; i++) {
131562306a36Sopenharmony_ci		hwq = get_hwq(afu, i);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci		reg = readq_be(&hwq->host_map->ctx_ctrl);
131862306a36Sopenharmony_ci		WARN_ON((reg & SISL_CTX_CTRL_LISN_MASK) != 0);
131962306a36Sopenharmony_ci		reg |= SISL_MSI_SYNC_ERROR;
132062306a36Sopenharmony_ci		writeq_be(reg, &hwq->host_map->ctx_ctrl);
132162306a36Sopenharmony_ci		writeq_be(SISL_ISTATUS_MASK, &hwq->host_map->intr_mask);
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci/**
132662306a36Sopenharmony_ci * cxlflash_sync_err_irq() - interrupt handler for synchronous errors
132762306a36Sopenharmony_ci * @irq:	Interrupt number.
132862306a36Sopenharmony_ci * @data:	Private data provided at interrupt registration, the AFU.
132962306a36Sopenharmony_ci *
133062306a36Sopenharmony_ci * Return: Always return IRQ_HANDLED.
133162306a36Sopenharmony_ci */
133262306a36Sopenharmony_cistatic irqreturn_t cxlflash_sync_err_irq(int irq, void *data)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	struct hwq *hwq = (struct hwq *)data;
133562306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = hwq->afu->parent;
133662306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
133762306a36Sopenharmony_ci	u64 reg;
133862306a36Sopenharmony_ci	u64 reg_unmasked;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	reg = readq_be(&hwq->host_map->intr_status);
134162306a36Sopenharmony_ci	reg_unmasked = (reg & SISL_ISTATUS_UNMASK);
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (reg_unmasked == 0UL) {
134462306a36Sopenharmony_ci		dev_err(dev, "%s: spurious interrupt, intr_status=%016llx\n",
134562306a36Sopenharmony_ci			__func__, reg);
134662306a36Sopenharmony_ci		goto cxlflash_sync_err_irq_exit;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	dev_err(dev, "%s: unexpected interrupt, intr_status=%016llx\n",
135062306a36Sopenharmony_ci		__func__, reg);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	writeq_be(reg_unmasked, &hwq->host_map->intr_clear);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cicxlflash_sync_err_irq_exit:
135562306a36Sopenharmony_ci	return IRQ_HANDLED;
135662306a36Sopenharmony_ci}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci/**
135962306a36Sopenharmony_ci * process_hrrq() - process the read-response queue
136062306a36Sopenharmony_ci * @hwq:	HWQ associated with the host.
136162306a36Sopenharmony_ci * @doneq:	Queue of commands harvested from the RRQ.
136262306a36Sopenharmony_ci * @budget:	Threshold of RRQ entries to process.
136362306a36Sopenharmony_ci *
136462306a36Sopenharmony_ci * This routine must be called holding the disabled RRQ spin lock.
136562306a36Sopenharmony_ci *
136662306a36Sopenharmony_ci * Return: The number of entries processed.
136762306a36Sopenharmony_ci */
136862306a36Sopenharmony_cistatic int process_hrrq(struct hwq *hwq, struct list_head *doneq, int budget)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	struct afu *afu = hwq->afu;
137162306a36Sopenharmony_ci	struct afu_cmd *cmd;
137262306a36Sopenharmony_ci	struct sisl_ioasa *ioasa;
137362306a36Sopenharmony_ci	struct sisl_ioarcb *ioarcb;
137462306a36Sopenharmony_ci	bool toggle = hwq->toggle;
137562306a36Sopenharmony_ci	int num_hrrq = 0;
137662306a36Sopenharmony_ci	u64 entry,
137762306a36Sopenharmony_ci	    *hrrq_start = hwq->hrrq_start,
137862306a36Sopenharmony_ci	    *hrrq_end = hwq->hrrq_end,
137962306a36Sopenharmony_ci	    *hrrq_curr = hwq->hrrq_curr;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/* Process ready RRQ entries up to the specified budget (if any) */
138262306a36Sopenharmony_ci	while (true) {
138362306a36Sopenharmony_ci		entry = *hrrq_curr;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci		if ((entry & SISL_RESP_HANDLE_T_BIT) != toggle)
138662306a36Sopenharmony_ci			break;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci		entry &= ~SISL_RESP_HANDLE_T_BIT;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci		if (afu_is_sq_cmd_mode(afu)) {
139162306a36Sopenharmony_ci			ioasa = (struct sisl_ioasa *)entry;
139262306a36Sopenharmony_ci			cmd = container_of(ioasa, struct afu_cmd, sa);
139362306a36Sopenharmony_ci		} else {
139462306a36Sopenharmony_ci			ioarcb = (struct sisl_ioarcb *)entry;
139562306a36Sopenharmony_ci			cmd = container_of(ioarcb, struct afu_cmd, rcb);
139662306a36Sopenharmony_ci		}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci		list_add_tail(&cmd->queue, doneq);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci		/* Advance to next entry or wrap and flip the toggle bit */
140162306a36Sopenharmony_ci		if (hrrq_curr < hrrq_end)
140262306a36Sopenharmony_ci			hrrq_curr++;
140362306a36Sopenharmony_ci		else {
140462306a36Sopenharmony_ci			hrrq_curr = hrrq_start;
140562306a36Sopenharmony_ci			toggle ^= SISL_RESP_HANDLE_T_BIT;
140662306a36Sopenharmony_ci		}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		atomic_inc(&hwq->hsq_credits);
140962306a36Sopenharmony_ci		num_hrrq++;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci		if (budget > 0 && num_hrrq >= budget)
141262306a36Sopenharmony_ci			break;
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	hwq->hrrq_curr = hrrq_curr;
141662306a36Sopenharmony_ci	hwq->toggle = toggle;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	return num_hrrq;
141962306a36Sopenharmony_ci}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci/**
142262306a36Sopenharmony_ci * process_cmd_doneq() - process a queue of harvested RRQ commands
142362306a36Sopenharmony_ci * @doneq:	Queue of completed commands.
142462306a36Sopenharmony_ci *
142562306a36Sopenharmony_ci * Note that upon return the queue can no longer be trusted.
142662306a36Sopenharmony_ci */
142762306a36Sopenharmony_cistatic void process_cmd_doneq(struct list_head *doneq)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	struct afu_cmd *cmd, *tmp;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	WARN_ON(list_empty(doneq));
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, tmp, doneq, queue)
143462306a36Sopenharmony_ci		cmd_complete(cmd);
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci/**
143862306a36Sopenharmony_ci * cxlflash_irqpoll() - process a queue of harvested RRQ commands
143962306a36Sopenharmony_ci * @irqpoll:	IRQ poll structure associated with queue to poll.
144062306a36Sopenharmony_ci * @budget:	Threshold of RRQ entries to process per poll.
144162306a36Sopenharmony_ci *
144262306a36Sopenharmony_ci * Return: The number of entries processed.
144362306a36Sopenharmony_ci */
144462306a36Sopenharmony_cistatic int cxlflash_irqpoll(struct irq_poll *irqpoll, int budget)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct hwq *hwq = container_of(irqpoll, struct hwq, irqpoll);
144762306a36Sopenharmony_ci	unsigned long hrrq_flags;
144862306a36Sopenharmony_ci	LIST_HEAD(doneq);
144962306a36Sopenharmony_ci	int num_entries = 0;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	spin_lock_irqsave(&hwq->hrrq_slock, hrrq_flags);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	num_entries = process_hrrq(hwq, &doneq, budget);
145462306a36Sopenharmony_ci	if (num_entries < budget)
145562306a36Sopenharmony_ci		irq_poll_complete(irqpoll);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	spin_unlock_irqrestore(&hwq->hrrq_slock, hrrq_flags);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	process_cmd_doneq(&doneq);
146062306a36Sopenharmony_ci	return num_entries;
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci/**
146462306a36Sopenharmony_ci * cxlflash_rrq_irq() - interrupt handler for read-response queue (normal path)
146562306a36Sopenharmony_ci * @irq:	Interrupt number.
146662306a36Sopenharmony_ci * @data:	Private data provided at interrupt registration, the AFU.
146762306a36Sopenharmony_ci *
146862306a36Sopenharmony_ci * Return: IRQ_HANDLED or IRQ_NONE when no ready entries found.
146962306a36Sopenharmony_ci */
147062306a36Sopenharmony_cistatic irqreturn_t cxlflash_rrq_irq(int irq, void *data)
147162306a36Sopenharmony_ci{
147262306a36Sopenharmony_ci	struct hwq *hwq = (struct hwq *)data;
147362306a36Sopenharmony_ci	struct afu *afu = hwq->afu;
147462306a36Sopenharmony_ci	unsigned long hrrq_flags;
147562306a36Sopenharmony_ci	LIST_HEAD(doneq);
147662306a36Sopenharmony_ci	int num_entries = 0;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	spin_lock_irqsave(&hwq->hrrq_slock, hrrq_flags);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/* Silently drop spurious interrupts when queue is not online */
148162306a36Sopenharmony_ci	if (!hwq->hrrq_online) {
148262306a36Sopenharmony_ci		spin_unlock_irqrestore(&hwq->hrrq_slock, hrrq_flags);
148362306a36Sopenharmony_ci		return IRQ_HANDLED;
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	if (afu_is_irqpoll_enabled(afu)) {
148762306a36Sopenharmony_ci		irq_poll_sched(&hwq->irqpoll);
148862306a36Sopenharmony_ci		spin_unlock_irqrestore(&hwq->hrrq_slock, hrrq_flags);
148962306a36Sopenharmony_ci		return IRQ_HANDLED;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	num_entries = process_hrrq(hwq, &doneq, -1);
149362306a36Sopenharmony_ci	spin_unlock_irqrestore(&hwq->hrrq_slock, hrrq_flags);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	if (num_entries == 0)
149662306a36Sopenharmony_ci		return IRQ_NONE;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	process_cmd_doneq(&doneq);
149962306a36Sopenharmony_ci	return IRQ_HANDLED;
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci/*
150362306a36Sopenharmony_ci * Asynchronous interrupt information table
150462306a36Sopenharmony_ci *
150562306a36Sopenharmony_ci * NOTE:
150662306a36Sopenharmony_ci *	- Order matters here as this array is indexed by bit position.
150762306a36Sopenharmony_ci *
150862306a36Sopenharmony_ci *	- The checkpatch script considers the BUILD_SISL_ASTATUS_FC_PORT macro
150962306a36Sopenharmony_ci *	  as complex and complains due to a lack of parentheses/braces.
151062306a36Sopenharmony_ci */
151162306a36Sopenharmony_ci#define ASTATUS_FC(_a, _b, _c, _d)					 \
151262306a36Sopenharmony_ci	{ SISL_ASTATUS_FC##_a##_##_b, _c, _a, (_d) }
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci#define BUILD_SISL_ASTATUS_FC_PORT(_a)					 \
151562306a36Sopenharmony_ci	ASTATUS_FC(_a, LINK_UP, "link up", 0),				 \
151662306a36Sopenharmony_ci	ASTATUS_FC(_a, LINK_DN, "link down", 0),			 \
151762306a36Sopenharmony_ci	ASTATUS_FC(_a, LOGI_S, "login succeeded", SCAN_HOST),		 \
151862306a36Sopenharmony_ci	ASTATUS_FC(_a, LOGI_F, "login failed", CLR_FC_ERROR),		 \
151962306a36Sopenharmony_ci	ASTATUS_FC(_a, LOGI_R, "login timed out, retrying", LINK_RESET), \
152062306a36Sopenharmony_ci	ASTATUS_FC(_a, CRC_T, "CRC threshold exceeded", LINK_RESET),	 \
152162306a36Sopenharmony_ci	ASTATUS_FC(_a, LOGO, "target initiated LOGO", 0),		 \
152262306a36Sopenharmony_ci	ASTATUS_FC(_a, OTHER, "other error", CLR_FC_ERROR | LINK_RESET)
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic const struct asyc_intr_info ainfo[] = {
152562306a36Sopenharmony_ci	BUILD_SISL_ASTATUS_FC_PORT(1),
152662306a36Sopenharmony_ci	BUILD_SISL_ASTATUS_FC_PORT(0),
152762306a36Sopenharmony_ci	BUILD_SISL_ASTATUS_FC_PORT(3),
152862306a36Sopenharmony_ci	BUILD_SISL_ASTATUS_FC_PORT(2)
152962306a36Sopenharmony_ci};
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci/**
153262306a36Sopenharmony_ci * cxlflash_async_err_irq() - interrupt handler for asynchronous errors
153362306a36Sopenharmony_ci * @irq:	Interrupt number.
153462306a36Sopenharmony_ci * @data:	Private data provided at interrupt registration, the AFU.
153562306a36Sopenharmony_ci *
153662306a36Sopenharmony_ci * Return: Always return IRQ_HANDLED.
153762306a36Sopenharmony_ci */
153862306a36Sopenharmony_cistatic irqreturn_t cxlflash_async_err_irq(int irq, void *data)
153962306a36Sopenharmony_ci{
154062306a36Sopenharmony_ci	struct hwq *hwq = (struct hwq *)data;
154162306a36Sopenharmony_ci	struct afu *afu = hwq->afu;
154262306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
154362306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
154462306a36Sopenharmony_ci	const struct asyc_intr_info *info;
154562306a36Sopenharmony_ci	struct sisl_global_map __iomem *global = &afu->afu_map->global;
154662306a36Sopenharmony_ci	__be64 __iomem *fc_port_regs;
154762306a36Sopenharmony_ci	u64 reg_unmasked;
154862306a36Sopenharmony_ci	u64 reg;
154962306a36Sopenharmony_ci	u64 bit;
155062306a36Sopenharmony_ci	u8 port;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	reg = readq_be(&global->regs.aintr_status);
155362306a36Sopenharmony_ci	reg_unmasked = (reg & SISL_ASTATUS_UNMASK);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	if (unlikely(reg_unmasked == 0)) {
155662306a36Sopenharmony_ci		dev_err(dev, "%s: spurious interrupt, aintr_status=%016llx\n",
155762306a36Sopenharmony_ci			__func__, reg);
155862306a36Sopenharmony_ci		goto out;
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	/* FYI, it is 'okay' to clear AFU status before FC_ERROR */
156262306a36Sopenharmony_ci	writeq_be(reg_unmasked, &global->regs.aintr_clear);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	/* Check each bit that is on */
156562306a36Sopenharmony_ci	for_each_set_bit(bit, (ulong *)&reg_unmasked, BITS_PER_LONG) {
156662306a36Sopenharmony_ci		if (unlikely(bit >= ARRAY_SIZE(ainfo))) {
156762306a36Sopenharmony_ci			WARN_ON_ONCE(1);
156862306a36Sopenharmony_ci			continue;
156962306a36Sopenharmony_ci		}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci		info = &ainfo[bit];
157262306a36Sopenharmony_ci		if (unlikely(info->status != 1ULL << bit)) {
157362306a36Sopenharmony_ci			WARN_ON_ONCE(1);
157462306a36Sopenharmony_ci			continue;
157562306a36Sopenharmony_ci		}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci		port = info->port;
157862306a36Sopenharmony_ci		fc_port_regs = get_fc_port_regs(cfg, port);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci		dev_err(dev, "%s: FC Port %d -> %s, fc_status=%016llx\n",
158162306a36Sopenharmony_ci			__func__, port, info->desc,
158262306a36Sopenharmony_ci		       readq_be(&fc_port_regs[FC_STATUS / 8]));
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci		/*
158562306a36Sopenharmony_ci		 * Do link reset first, some OTHER errors will set FC_ERROR
158662306a36Sopenharmony_ci		 * again if cleared before or w/o a reset
158762306a36Sopenharmony_ci		 */
158862306a36Sopenharmony_ci		if (info->action & LINK_RESET) {
158962306a36Sopenharmony_ci			dev_err(dev, "%s: FC Port %d: resetting link\n",
159062306a36Sopenharmony_ci				__func__, port);
159162306a36Sopenharmony_ci			cfg->lr_state = LINK_RESET_REQUIRED;
159262306a36Sopenharmony_ci			cfg->lr_port = port;
159362306a36Sopenharmony_ci			schedule_work(&cfg->work_q);
159462306a36Sopenharmony_ci		}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci		if (info->action & CLR_FC_ERROR) {
159762306a36Sopenharmony_ci			reg = readq_be(&fc_port_regs[FC_ERROR / 8]);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci			/*
160062306a36Sopenharmony_ci			 * Since all errors are unmasked, FC_ERROR and FC_ERRCAP
160162306a36Sopenharmony_ci			 * should be the same and tracing one is sufficient.
160262306a36Sopenharmony_ci			 */
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci			dev_err(dev, "%s: fc %d: clearing fc_error=%016llx\n",
160562306a36Sopenharmony_ci				__func__, port, reg);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci			writeq_be(reg, &fc_port_regs[FC_ERROR / 8]);
160862306a36Sopenharmony_ci			writeq_be(0, &fc_port_regs[FC_ERRCAP / 8]);
160962306a36Sopenharmony_ci		}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci		if (info->action & SCAN_HOST) {
161262306a36Sopenharmony_ci			atomic_inc(&cfg->scan_host_needed);
161362306a36Sopenharmony_ci			schedule_work(&cfg->work_q);
161462306a36Sopenharmony_ci		}
161562306a36Sopenharmony_ci	}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ciout:
161862306a36Sopenharmony_ci	return IRQ_HANDLED;
161962306a36Sopenharmony_ci}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci/**
162262306a36Sopenharmony_ci * read_vpd() - obtains the WWPNs from VPD
162362306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
162462306a36Sopenharmony_ci * @wwpn:	Array of size MAX_FC_PORTS to pass back WWPNs
162562306a36Sopenharmony_ci *
162662306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
162762306a36Sopenharmony_ci */
162862306a36Sopenharmony_cistatic int read_vpd(struct cxlflash_cfg *cfg, u64 wwpn[])
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
163162306a36Sopenharmony_ci	struct pci_dev *pdev = cfg->dev;
163262306a36Sopenharmony_ci	int i, k, rc = 0;
163362306a36Sopenharmony_ci	unsigned int kw_size;
163462306a36Sopenharmony_ci	ssize_t vpd_size;
163562306a36Sopenharmony_ci	char vpd_data[CXLFLASH_VPD_LEN];
163662306a36Sopenharmony_ci	char tmp_buf[WWPN_BUF_LEN] = { 0 };
163762306a36Sopenharmony_ci	const struct dev_dependent_vals *ddv = (struct dev_dependent_vals *)
163862306a36Sopenharmony_ci						cfg->dev_id->driver_data;
163962306a36Sopenharmony_ci	const bool wwpn_vpd_required = ddv->flags & CXLFLASH_WWPN_VPD_REQUIRED;
164062306a36Sopenharmony_ci	const char *wwpn_vpd_tags[MAX_FC_PORTS] = { "V5", "V6", "V7", "V8" };
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	/* Get the VPD data from the device */
164362306a36Sopenharmony_ci	vpd_size = cfg->ops->read_adapter_vpd(pdev, vpd_data, sizeof(vpd_data));
164462306a36Sopenharmony_ci	if (unlikely(vpd_size <= 0)) {
164562306a36Sopenharmony_ci		dev_err(dev, "%s: Unable to read VPD (size = %ld)\n",
164662306a36Sopenharmony_ci			__func__, vpd_size);
164762306a36Sopenharmony_ci		rc = -ENODEV;
164862306a36Sopenharmony_ci		goto out;
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	/*
165262306a36Sopenharmony_ci	 * Find the offset of the WWPN tag within the read only
165362306a36Sopenharmony_ci	 * VPD data and validate the found field (partials are
165462306a36Sopenharmony_ci	 * no good to us). Convert the ASCII data to an integer
165562306a36Sopenharmony_ci	 * value. Note that we must copy to a temporary buffer
165662306a36Sopenharmony_ci	 * because the conversion service requires that the ASCII
165762306a36Sopenharmony_ci	 * string be terminated.
165862306a36Sopenharmony_ci	 *
165962306a36Sopenharmony_ci	 * Allow for WWPN not being found for all devices, setting
166062306a36Sopenharmony_ci	 * the returned WWPN to zero when not found. Notify with a
166162306a36Sopenharmony_ci	 * log error for cards that should have had WWPN keywords
166262306a36Sopenharmony_ci	 * in the VPD - cards requiring WWPN will not have their
166362306a36Sopenharmony_ci	 * ports programmed and operate in an undefined state.
166462306a36Sopenharmony_ci	 */
166562306a36Sopenharmony_ci	for (k = 0; k < cfg->num_fc_ports; k++) {
166662306a36Sopenharmony_ci		i = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
166762306a36Sopenharmony_ci						 wwpn_vpd_tags[k], &kw_size);
166862306a36Sopenharmony_ci		if (i == -ENOENT) {
166962306a36Sopenharmony_ci			if (wwpn_vpd_required)
167062306a36Sopenharmony_ci				dev_err(dev, "%s: Port %d WWPN not found\n",
167162306a36Sopenharmony_ci					__func__, k);
167262306a36Sopenharmony_ci			wwpn[k] = 0ULL;
167362306a36Sopenharmony_ci			continue;
167462306a36Sopenharmony_ci		}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		if (i < 0 || kw_size != WWPN_LEN) {
167762306a36Sopenharmony_ci			dev_err(dev, "%s: Port %d WWPN incomplete or bad VPD\n",
167862306a36Sopenharmony_ci				__func__, k);
167962306a36Sopenharmony_ci			rc = -ENODEV;
168062306a36Sopenharmony_ci			goto out;
168162306a36Sopenharmony_ci		}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci		memcpy(tmp_buf, &vpd_data[i], WWPN_LEN);
168462306a36Sopenharmony_ci		rc = kstrtoul(tmp_buf, WWPN_LEN, (ulong *)&wwpn[k]);
168562306a36Sopenharmony_ci		if (unlikely(rc)) {
168662306a36Sopenharmony_ci			dev_err(dev, "%s: WWPN conversion failed for port %d\n",
168762306a36Sopenharmony_ci				__func__, k);
168862306a36Sopenharmony_ci			rc = -ENODEV;
168962306a36Sopenharmony_ci			goto out;
169062306a36Sopenharmony_ci		}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci		dev_dbg(dev, "%s: wwpn%d=%016llx\n", __func__, k, wwpn[k]);
169362306a36Sopenharmony_ci	}
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ciout:
169662306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
169762306a36Sopenharmony_ci	return rc;
169862306a36Sopenharmony_ci}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci/**
170162306a36Sopenharmony_ci * init_pcr() - initialize the provisioning and control registers
170262306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
170362306a36Sopenharmony_ci *
170462306a36Sopenharmony_ci * Also sets up fast access to the mapped registers and initializes AFU
170562306a36Sopenharmony_ci * command fields that never change.
170662306a36Sopenharmony_ci */
170762306a36Sopenharmony_cistatic void init_pcr(struct cxlflash_cfg *cfg)
170862306a36Sopenharmony_ci{
170962306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
171062306a36Sopenharmony_ci	struct sisl_ctrl_map __iomem *ctrl_map;
171162306a36Sopenharmony_ci	struct hwq *hwq;
171262306a36Sopenharmony_ci	void *cookie;
171362306a36Sopenharmony_ci	int i;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	for (i = 0; i < MAX_CONTEXT; i++) {
171662306a36Sopenharmony_ci		ctrl_map = &afu->afu_map->ctrls[i].ctrl;
171762306a36Sopenharmony_ci		/* Disrupt any clients that could be running */
171862306a36Sopenharmony_ci		/* e.g. clients that survived a master restart */
171962306a36Sopenharmony_ci		writeq_be(0, &ctrl_map->rht_start);
172062306a36Sopenharmony_ci		writeq_be(0, &ctrl_map->rht_cnt_id);
172162306a36Sopenharmony_ci		writeq_be(0, &ctrl_map->ctx_cap);
172262306a36Sopenharmony_ci	}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	/* Copy frequently used fields into hwq */
172562306a36Sopenharmony_ci	for (i = 0; i < afu->num_hwqs; i++) {
172662306a36Sopenharmony_ci		hwq = get_hwq(afu, i);
172762306a36Sopenharmony_ci		cookie = hwq->ctx_cookie;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci		hwq->ctx_hndl = (u16) cfg->ops->process_element(cookie);
173062306a36Sopenharmony_ci		hwq->host_map = &afu->afu_map->hosts[hwq->ctx_hndl].host;
173162306a36Sopenharmony_ci		hwq->ctrl_map = &afu->afu_map->ctrls[hwq->ctx_hndl].ctrl;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci		/* Program the Endian Control for the master context */
173462306a36Sopenharmony_ci		writeq_be(SISL_ENDIAN_CTRL, &hwq->host_map->endian_ctrl);
173562306a36Sopenharmony_ci	}
173662306a36Sopenharmony_ci}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci/**
173962306a36Sopenharmony_ci * init_global() - initialize AFU global registers
174062306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
174162306a36Sopenharmony_ci */
174262306a36Sopenharmony_cistatic int init_global(struct cxlflash_cfg *cfg)
174362306a36Sopenharmony_ci{
174462306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
174562306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
174662306a36Sopenharmony_ci	struct hwq *hwq;
174762306a36Sopenharmony_ci	struct sisl_host_map __iomem *hmap;
174862306a36Sopenharmony_ci	__be64 __iomem *fc_port_regs;
174962306a36Sopenharmony_ci	u64 wwpn[MAX_FC_PORTS];	/* wwpn of AFU ports */
175062306a36Sopenharmony_ci	int i = 0, num_ports = 0;
175162306a36Sopenharmony_ci	int rc = 0;
175262306a36Sopenharmony_ci	int j;
175362306a36Sopenharmony_ci	void *ctx;
175462306a36Sopenharmony_ci	u64 reg;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	rc = read_vpd(cfg, &wwpn[0]);
175762306a36Sopenharmony_ci	if (rc) {
175862306a36Sopenharmony_ci		dev_err(dev, "%s: could not read vpd rc=%d\n", __func__, rc);
175962306a36Sopenharmony_ci		goto out;
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	/* Set up RRQ and SQ in HWQ for master issued cmds */
176362306a36Sopenharmony_ci	for (i = 0; i < afu->num_hwqs; i++) {
176462306a36Sopenharmony_ci		hwq = get_hwq(afu, i);
176562306a36Sopenharmony_ci		hmap = hwq->host_map;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci		writeq_be((u64) hwq->hrrq_start, &hmap->rrq_start);
176862306a36Sopenharmony_ci		writeq_be((u64) hwq->hrrq_end, &hmap->rrq_end);
176962306a36Sopenharmony_ci		hwq->hrrq_online = true;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci		if (afu_is_sq_cmd_mode(afu)) {
177262306a36Sopenharmony_ci			writeq_be((u64)hwq->hsq_start, &hmap->sq_start);
177362306a36Sopenharmony_ci			writeq_be((u64)hwq->hsq_end, &hmap->sq_end);
177462306a36Sopenharmony_ci		}
177562306a36Sopenharmony_ci	}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	/* AFU configuration */
177862306a36Sopenharmony_ci	reg = readq_be(&afu->afu_map->global.regs.afu_config);
177962306a36Sopenharmony_ci	reg |= SISL_AFUCONF_AR_ALL|SISL_AFUCONF_ENDIAN;
178062306a36Sopenharmony_ci	/* enable all auto retry options and control endianness */
178162306a36Sopenharmony_ci	/* leave others at default: */
178262306a36Sopenharmony_ci	/* CTX_CAP write protected, mbox_r does not clear on read and */
178362306a36Sopenharmony_ci	/* checker on if dual afu */
178462306a36Sopenharmony_ci	writeq_be(reg, &afu->afu_map->global.regs.afu_config);
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	/* Global port select: select either port */
178762306a36Sopenharmony_ci	if (afu->internal_lun) {
178862306a36Sopenharmony_ci		/* Only use port 0 */
178962306a36Sopenharmony_ci		writeq_be(PORT0, &afu->afu_map->global.regs.afu_port_sel);
179062306a36Sopenharmony_ci		num_ports = 0;
179162306a36Sopenharmony_ci	} else {
179262306a36Sopenharmony_ci		writeq_be(PORT_MASK(cfg->num_fc_ports),
179362306a36Sopenharmony_ci			  &afu->afu_map->global.regs.afu_port_sel);
179462306a36Sopenharmony_ci		num_ports = cfg->num_fc_ports;
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	for (i = 0; i < num_ports; i++) {
179862306a36Sopenharmony_ci		fc_port_regs = get_fc_port_regs(cfg, i);
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci		/* Unmask all errors (but they are still masked at AFU) */
180162306a36Sopenharmony_ci		writeq_be(0, &fc_port_regs[FC_ERRMSK / 8]);
180262306a36Sopenharmony_ci		/* Clear CRC error cnt & set a threshold */
180362306a36Sopenharmony_ci		(void)readq_be(&fc_port_regs[FC_CNT_CRCERR / 8]);
180462306a36Sopenharmony_ci		writeq_be(MC_CRC_THRESH, &fc_port_regs[FC_CRC_THRESH / 8]);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci		/* Set WWPNs. If already programmed, wwpn[i] is 0 */
180762306a36Sopenharmony_ci		if (wwpn[i] != 0)
180862306a36Sopenharmony_ci			afu_set_wwpn(afu, i, &fc_port_regs[0], wwpn[i]);
180962306a36Sopenharmony_ci		/* Programming WWPN back to back causes additional
181062306a36Sopenharmony_ci		 * offline/online transitions and a PLOGI
181162306a36Sopenharmony_ci		 */
181262306a36Sopenharmony_ci		msleep(100);
181362306a36Sopenharmony_ci	}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	if (afu_is_ocxl_lisn(afu)) {
181662306a36Sopenharmony_ci		/* Set up the LISN effective address for each master */
181762306a36Sopenharmony_ci		for (i = 0; i < afu->num_hwqs; i++) {
181862306a36Sopenharmony_ci			hwq = get_hwq(afu, i);
181962306a36Sopenharmony_ci			ctx = hwq->ctx_cookie;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci			for (j = 0; j < hwq->num_irqs; j++) {
182262306a36Sopenharmony_ci				reg = cfg->ops->get_irq_objhndl(ctx, j);
182362306a36Sopenharmony_ci				writeq_be(reg, &hwq->ctrl_map->lisn_ea[j]);
182462306a36Sopenharmony_ci			}
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci			reg = hwq->ctx_hndl;
182762306a36Sopenharmony_ci			writeq_be(SISL_LISN_PASID(reg, reg),
182862306a36Sopenharmony_ci				  &hwq->ctrl_map->lisn_pasid[0]);
182962306a36Sopenharmony_ci			writeq_be(SISL_LISN_PASID(0UL, reg),
183062306a36Sopenharmony_ci				  &hwq->ctrl_map->lisn_pasid[1]);
183162306a36Sopenharmony_ci		}
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	/* Set up master's own CTX_CAP to allow real mode, host translation */
183562306a36Sopenharmony_ci	/* tables, afu cmds and read/write GSCSI cmds. */
183662306a36Sopenharmony_ci	/* First, unlock ctx_cap write by reading mbox */
183762306a36Sopenharmony_ci	for (i = 0; i < afu->num_hwqs; i++) {
183862306a36Sopenharmony_ci		hwq = get_hwq(afu, i);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci		(void)readq_be(&hwq->ctrl_map->mbox_r);	/* unlock ctx_cap */
184162306a36Sopenharmony_ci		writeq_be((SISL_CTX_CAP_REAL_MODE | SISL_CTX_CAP_HOST_XLATE |
184262306a36Sopenharmony_ci			SISL_CTX_CAP_READ_CMD | SISL_CTX_CAP_WRITE_CMD |
184362306a36Sopenharmony_ci			SISL_CTX_CAP_AFU_CMD | SISL_CTX_CAP_GSCSI_CMD),
184462306a36Sopenharmony_ci			&hwq->ctrl_map->ctx_cap);
184562306a36Sopenharmony_ci	}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	/*
184862306a36Sopenharmony_ci	 * Determine write-same unmap support for host by evaluating the unmap
184962306a36Sopenharmony_ci	 * sector support bit of the context control register associated with
185062306a36Sopenharmony_ci	 * the primary hardware queue. Note that while this status is reflected
185162306a36Sopenharmony_ci	 * in a context register, the outcome can be assumed to be host-wide.
185262306a36Sopenharmony_ci	 */
185362306a36Sopenharmony_ci	hwq = get_hwq(afu, PRIMARY_HWQ);
185462306a36Sopenharmony_ci	reg = readq_be(&hwq->host_map->ctx_ctrl);
185562306a36Sopenharmony_ci	if (reg & SISL_CTX_CTRL_UNMAP_SECTOR)
185662306a36Sopenharmony_ci		cfg->ws_unmap = true;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	/* Initialize heartbeat */
185962306a36Sopenharmony_ci	afu->hb = readq_be(&afu->afu_map->global.regs.afu_hb);
186062306a36Sopenharmony_ciout:
186162306a36Sopenharmony_ci	return rc;
186262306a36Sopenharmony_ci}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci/**
186562306a36Sopenharmony_ci * start_afu() - initializes and starts the AFU
186662306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
186762306a36Sopenharmony_ci */
186862306a36Sopenharmony_cistatic int start_afu(struct cxlflash_cfg *cfg)
186962306a36Sopenharmony_ci{
187062306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
187162306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
187262306a36Sopenharmony_ci	struct hwq *hwq;
187362306a36Sopenharmony_ci	int rc = 0;
187462306a36Sopenharmony_ci	int i;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	init_pcr(cfg);
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	/* Initialize each HWQ */
187962306a36Sopenharmony_ci	for (i = 0; i < afu->num_hwqs; i++) {
188062306a36Sopenharmony_ci		hwq = get_hwq(afu, i);
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci		/* After an AFU reset, RRQ entries are stale, clear them */
188362306a36Sopenharmony_ci		memset(&hwq->rrq_entry, 0, sizeof(hwq->rrq_entry));
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci		/* Initialize RRQ pointers */
188662306a36Sopenharmony_ci		hwq->hrrq_start = &hwq->rrq_entry[0];
188762306a36Sopenharmony_ci		hwq->hrrq_end = &hwq->rrq_entry[NUM_RRQ_ENTRY - 1];
188862306a36Sopenharmony_ci		hwq->hrrq_curr = hwq->hrrq_start;
188962306a36Sopenharmony_ci		hwq->toggle = 1;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci		/* Initialize spin locks */
189262306a36Sopenharmony_ci		spin_lock_init(&hwq->hrrq_slock);
189362306a36Sopenharmony_ci		spin_lock_init(&hwq->hsq_slock);
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci		/* Initialize SQ */
189662306a36Sopenharmony_ci		if (afu_is_sq_cmd_mode(afu)) {
189762306a36Sopenharmony_ci			memset(&hwq->sq, 0, sizeof(hwq->sq));
189862306a36Sopenharmony_ci			hwq->hsq_start = &hwq->sq[0];
189962306a36Sopenharmony_ci			hwq->hsq_end = &hwq->sq[NUM_SQ_ENTRY - 1];
190062306a36Sopenharmony_ci			hwq->hsq_curr = hwq->hsq_start;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci			atomic_set(&hwq->hsq_credits, NUM_SQ_ENTRY - 1);
190362306a36Sopenharmony_ci		}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci		/* Initialize IRQ poll */
190662306a36Sopenharmony_ci		if (afu_is_irqpoll_enabled(afu))
190762306a36Sopenharmony_ci			irq_poll_init(&hwq->irqpoll, afu->irqpoll_weight,
190862306a36Sopenharmony_ci				      cxlflash_irqpoll);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	}
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	rc = init_global(cfg);
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
191562306a36Sopenharmony_ci	return rc;
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci/**
191962306a36Sopenharmony_ci * init_intr() - setup interrupt handlers for the master context
192062306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
192162306a36Sopenharmony_ci * @hwq:	Hardware queue to initialize.
192262306a36Sopenharmony_ci *
192362306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
192462306a36Sopenharmony_ci */
192562306a36Sopenharmony_cistatic enum undo_level init_intr(struct cxlflash_cfg *cfg,
192662306a36Sopenharmony_ci				 struct hwq *hwq)
192762306a36Sopenharmony_ci{
192862306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
192962306a36Sopenharmony_ci	void *ctx = hwq->ctx_cookie;
193062306a36Sopenharmony_ci	int rc = 0;
193162306a36Sopenharmony_ci	enum undo_level level = UNDO_NOOP;
193262306a36Sopenharmony_ci	bool is_primary_hwq = (hwq->index == PRIMARY_HWQ);
193362306a36Sopenharmony_ci	int num_irqs = hwq->num_irqs;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	rc = cfg->ops->allocate_afu_irqs(ctx, num_irqs);
193662306a36Sopenharmony_ci	if (unlikely(rc)) {
193762306a36Sopenharmony_ci		dev_err(dev, "%s: allocate_afu_irqs failed rc=%d\n",
193862306a36Sopenharmony_ci			__func__, rc);
193962306a36Sopenharmony_ci		level = UNDO_NOOP;
194062306a36Sopenharmony_ci		goto out;
194162306a36Sopenharmony_ci	}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	rc = cfg->ops->map_afu_irq(ctx, 1, cxlflash_sync_err_irq, hwq,
194462306a36Sopenharmony_ci				   "SISL_MSI_SYNC_ERROR");
194562306a36Sopenharmony_ci	if (unlikely(rc <= 0)) {
194662306a36Sopenharmony_ci		dev_err(dev, "%s: SISL_MSI_SYNC_ERROR map failed\n", __func__);
194762306a36Sopenharmony_ci		level = FREE_IRQ;
194862306a36Sopenharmony_ci		goto out;
194962306a36Sopenharmony_ci	}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	rc = cfg->ops->map_afu_irq(ctx, 2, cxlflash_rrq_irq, hwq,
195262306a36Sopenharmony_ci				   "SISL_MSI_RRQ_UPDATED");
195362306a36Sopenharmony_ci	if (unlikely(rc <= 0)) {
195462306a36Sopenharmony_ci		dev_err(dev, "%s: SISL_MSI_RRQ_UPDATED map failed\n", __func__);
195562306a36Sopenharmony_ci		level = UNMAP_ONE;
195662306a36Sopenharmony_ci		goto out;
195762306a36Sopenharmony_ci	}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	/* SISL_MSI_ASYNC_ERROR is setup only for the primary HWQ */
196062306a36Sopenharmony_ci	if (!is_primary_hwq)
196162306a36Sopenharmony_ci		goto out;
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	rc = cfg->ops->map_afu_irq(ctx, 3, cxlflash_async_err_irq, hwq,
196462306a36Sopenharmony_ci				   "SISL_MSI_ASYNC_ERROR");
196562306a36Sopenharmony_ci	if (unlikely(rc <= 0)) {
196662306a36Sopenharmony_ci		dev_err(dev, "%s: SISL_MSI_ASYNC_ERROR map failed\n", __func__);
196762306a36Sopenharmony_ci		level = UNMAP_TWO;
196862306a36Sopenharmony_ci		goto out;
196962306a36Sopenharmony_ci	}
197062306a36Sopenharmony_ciout:
197162306a36Sopenharmony_ci	return level;
197262306a36Sopenharmony_ci}
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci/**
197562306a36Sopenharmony_ci * init_mc() - create and register as the master context
197662306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
197762306a36Sopenharmony_ci * @index:	HWQ Index of the master context.
197862306a36Sopenharmony_ci *
197962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
198062306a36Sopenharmony_ci */
198162306a36Sopenharmony_cistatic int init_mc(struct cxlflash_cfg *cfg, u32 index)
198262306a36Sopenharmony_ci{
198362306a36Sopenharmony_ci	void *ctx;
198462306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
198562306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(cfg->afu, index);
198662306a36Sopenharmony_ci	int rc = 0;
198762306a36Sopenharmony_ci	int num_irqs;
198862306a36Sopenharmony_ci	enum undo_level level;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	hwq->afu = cfg->afu;
199162306a36Sopenharmony_ci	hwq->index = index;
199262306a36Sopenharmony_ci	INIT_LIST_HEAD(&hwq->pending_cmds);
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	if (index == PRIMARY_HWQ) {
199562306a36Sopenharmony_ci		ctx = cfg->ops->get_context(cfg->dev, cfg->afu_cookie);
199662306a36Sopenharmony_ci		num_irqs = 3;
199762306a36Sopenharmony_ci	} else {
199862306a36Sopenharmony_ci		ctx = cfg->ops->dev_context_init(cfg->dev, cfg->afu_cookie);
199962306a36Sopenharmony_ci		num_irqs = 2;
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(ctx)) {
200262306a36Sopenharmony_ci		rc = -ENOMEM;
200362306a36Sopenharmony_ci		goto err1;
200462306a36Sopenharmony_ci	}
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	WARN_ON(hwq->ctx_cookie);
200762306a36Sopenharmony_ci	hwq->ctx_cookie = ctx;
200862306a36Sopenharmony_ci	hwq->num_irqs = num_irqs;
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	/* Set it up as a master with the CXL */
201162306a36Sopenharmony_ci	cfg->ops->set_master(ctx);
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	/* Reset AFU when initializing primary context */
201462306a36Sopenharmony_ci	if (index == PRIMARY_HWQ) {
201562306a36Sopenharmony_ci		rc = cfg->ops->afu_reset(ctx);
201662306a36Sopenharmony_ci		if (unlikely(rc)) {
201762306a36Sopenharmony_ci			dev_err(dev, "%s: AFU reset failed rc=%d\n",
201862306a36Sopenharmony_ci				      __func__, rc);
201962306a36Sopenharmony_ci			goto err1;
202062306a36Sopenharmony_ci		}
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	level = init_intr(cfg, hwq);
202462306a36Sopenharmony_ci	if (unlikely(level)) {
202562306a36Sopenharmony_ci		dev_err(dev, "%s: interrupt init failed rc=%d\n", __func__, rc);
202662306a36Sopenharmony_ci		goto err2;
202762306a36Sopenharmony_ci	}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	/* Finally, activate the context by starting it */
203062306a36Sopenharmony_ci	rc = cfg->ops->start_context(hwq->ctx_cookie);
203162306a36Sopenharmony_ci	if (unlikely(rc)) {
203262306a36Sopenharmony_ci		dev_err(dev, "%s: start context failed rc=%d\n", __func__, rc);
203362306a36Sopenharmony_ci		level = UNMAP_THREE;
203462306a36Sopenharmony_ci		goto err2;
203562306a36Sopenharmony_ci	}
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ciout:
203862306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
203962306a36Sopenharmony_ci	return rc;
204062306a36Sopenharmony_cierr2:
204162306a36Sopenharmony_ci	term_intr(cfg, level, index);
204262306a36Sopenharmony_ci	if (index != PRIMARY_HWQ)
204362306a36Sopenharmony_ci		cfg->ops->release_context(ctx);
204462306a36Sopenharmony_cierr1:
204562306a36Sopenharmony_ci	hwq->ctx_cookie = NULL;
204662306a36Sopenharmony_ci	goto out;
204762306a36Sopenharmony_ci}
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci/**
205062306a36Sopenharmony_ci * get_num_afu_ports() - determines and configures the number of AFU ports
205162306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
205262306a36Sopenharmony_ci *
205362306a36Sopenharmony_ci * This routine determines the number of AFU ports by converting the global
205462306a36Sopenharmony_ci * port selection mask. The converted value is only valid following an AFU
205562306a36Sopenharmony_ci * reset (explicit or power-on). This routine must be invoked shortly after
205662306a36Sopenharmony_ci * mapping as other routines are dependent on the number of ports during the
205762306a36Sopenharmony_ci * initialization sequence.
205862306a36Sopenharmony_ci *
205962306a36Sopenharmony_ci * To support legacy AFUs that might not have reflected an initial global
206062306a36Sopenharmony_ci * port mask (value read is 0), default to the number of ports originally
206162306a36Sopenharmony_ci * supported by the cxlflash driver (2) before hardware with other port
206262306a36Sopenharmony_ci * offerings was introduced.
206362306a36Sopenharmony_ci */
206462306a36Sopenharmony_cistatic void get_num_afu_ports(struct cxlflash_cfg *cfg)
206562306a36Sopenharmony_ci{
206662306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
206762306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
206862306a36Sopenharmony_ci	u64 port_mask;
206962306a36Sopenharmony_ci	int num_fc_ports = LEGACY_FC_PORTS;
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	port_mask = readq_be(&afu->afu_map->global.regs.afu_port_sel);
207262306a36Sopenharmony_ci	if (port_mask != 0ULL)
207362306a36Sopenharmony_ci		num_fc_ports = min(ilog2(port_mask) + 1, MAX_FC_PORTS);
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	dev_dbg(dev, "%s: port_mask=%016llx num_fc_ports=%d\n",
207662306a36Sopenharmony_ci		__func__, port_mask, num_fc_ports);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	cfg->num_fc_ports = num_fc_ports;
207962306a36Sopenharmony_ci	cfg->host->max_channel = PORTNUM2CHAN(num_fc_ports);
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci/**
208362306a36Sopenharmony_ci * init_afu() - setup as master context and start AFU
208462306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
208562306a36Sopenharmony_ci *
208662306a36Sopenharmony_ci * This routine is a higher level of control for configuring the
208762306a36Sopenharmony_ci * AFU on probe and reset paths.
208862306a36Sopenharmony_ci *
208962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
209062306a36Sopenharmony_ci */
209162306a36Sopenharmony_cistatic int init_afu(struct cxlflash_cfg *cfg)
209262306a36Sopenharmony_ci{
209362306a36Sopenharmony_ci	u64 reg;
209462306a36Sopenharmony_ci	int rc = 0;
209562306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
209662306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
209762306a36Sopenharmony_ci	struct hwq *hwq;
209862306a36Sopenharmony_ci	int i;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	cfg->ops->perst_reloads_same_image(cfg->afu_cookie, true);
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	mutex_init(&afu->sync_active);
210362306a36Sopenharmony_ci	afu->num_hwqs = afu->desired_hwqs;
210462306a36Sopenharmony_ci	for (i = 0; i < afu->num_hwqs; i++) {
210562306a36Sopenharmony_ci		rc = init_mc(cfg, i);
210662306a36Sopenharmony_ci		if (rc) {
210762306a36Sopenharmony_ci			dev_err(dev, "%s: init_mc failed rc=%d index=%d\n",
210862306a36Sopenharmony_ci				__func__, rc, i);
210962306a36Sopenharmony_ci			goto err1;
211062306a36Sopenharmony_ci		}
211162306a36Sopenharmony_ci	}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	/* Map the entire MMIO space of the AFU using the first context */
211462306a36Sopenharmony_ci	hwq = get_hwq(afu, PRIMARY_HWQ);
211562306a36Sopenharmony_ci	afu->afu_map = cfg->ops->psa_map(hwq->ctx_cookie);
211662306a36Sopenharmony_ci	if (!afu->afu_map) {
211762306a36Sopenharmony_ci		dev_err(dev, "%s: psa_map failed\n", __func__);
211862306a36Sopenharmony_ci		rc = -ENOMEM;
211962306a36Sopenharmony_ci		goto err1;
212062306a36Sopenharmony_ci	}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	/* No byte reverse on reading afu_version or string will be backwards */
212362306a36Sopenharmony_ci	reg = readq(&afu->afu_map->global.regs.afu_version);
212462306a36Sopenharmony_ci	memcpy(afu->version, &reg, sizeof(reg));
212562306a36Sopenharmony_ci	afu->interface_version =
212662306a36Sopenharmony_ci	    readq_be(&afu->afu_map->global.regs.interface_version);
212762306a36Sopenharmony_ci	if ((afu->interface_version + 1) == 0) {
212862306a36Sopenharmony_ci		dev_err(dev, "Back level AFU, please upgrade. AFU version %s "
212962306a36Sopenharmony_ci			"interface version %016llx\n", afu->version,
213062306a36Sopenharmony_ci		       afu->interface_version);
213162306a36Sopenharmony_ci		rc = -EINVAL;
213262306a36Sopenharmony_ci		goto err1;
213362306a36Sopenharmony_ci	}
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	if (afu_is_sq_cmd_mode(afu)) {
213662306a36Sopenharmony_ci		afu->send_cmd = send_cmd_sq;
213762306a36Sopenharmony_ci		afu->context_reset = context_reset_sq;
213862306a36Sopenharmony_ci	} else {
213962306a36Sopenharmony_ci		afu->send_cmd = send_cmd_ioarrin;
214062306a36Sopenharmony_ci		afu->context_reset = context_reset_ioarrin;
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	dev_dbg(dev, "%s: afu_ver=%s interface_ver=%016llx\n", __func__,
214462306a36Sopenharmony_ci		afu->version, afu->interface_version);
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	get_num_afu_ports(cfg);
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	rc = start_afu(cfg);
214962306a36Sopenharmony_ci	if (rc) {
215062306a36Sopenharmony_ci		dev_err(dev, "%s: start_afu failed, rc=%d\n", __func__, rc);
215162306a36Sopenharmony_ci		goto err1;
215262306a36Sopenharmony_ci	}
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	afu_err_intr_init(cfg->afu);
215562306a36Sopenharmony_ci	for (i = 0; i < afu->num_hwqs; i++) {
215662306a36Sopenharmony_ci		hwq = get_hwq(afu, i);
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci		hwq->room = readq_be(&hwq->host_map->cmd_room);
215962306a36Sopenharmony_ci	}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	/* Restore the LUN mappings */
216262306a36Sopenharmony_ci	cxlflash_restore_luntable(cfg);
216362306a36Sopenharmony_ciout:
216462306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
216562306a36Sopenharmony_ci	return rc;
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_cierr1:
216862306a36Sopenharmony_ci	for (i = afu->num_hwqs - 1; i >= 0; i--) {
216962306a36Sopenharmony_ci		term_intr(cfg, UNMAP_THREE, i);
217062306a36Sopenharmony_ci		term_mc(cfg, i);
217162306a36Sopenharmony_ci	}
217262306a36Sopenharmony_ci	goto out;
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci/**
217662306a36Sopenharmony_ci * afu_reset() - resets the AFU
217762306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
217862306a36Sopenharmony_ci *
217962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
218062306a36Sopenharmony_ci */
218162306a36Sopenharmony_cistatic int afu_reset(struct cxlflash_cfg *cfg)
218262306a36Sopenharmony_ci{
218362306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
218462306a36Sopenharmony_ci	int rc = 0;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	/* Stop the context before the reset. Since the context is
218762306a36Sopenharmony_ci	 * no longer available restart it after the reset is complete
218862306a36Sopenharmony_ci	 */
218962306a36Sopenharmony_ci	term_afu(cfg);
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	rc = init_afu(cfg);
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
219462306a36Sopenharmony_ci	return rc;
219562306a36Sopenharmony_ci}
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci/**
219862306a36Sopenharmony_ci * drain_ioctls() - wait until all currently executing ioctls have completed
219962306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
220062306a36Sopenharmony_ci *
220162306a36Sopenharmony_ci * Obtain write access to read/write semaphore that wraps ioctl
220262306a36Sopenharmony_ci * handling to 'drain' ioctls currently executing.
220362306a36Sopenharmony_ci */
220462306a36Sopenharmony_cistatic void drain_ioctls(struct cxlflash_cfg *cfg)
220562306a36Sopenharmony_ci{
220662306a36Sopenharmony_ci	down_write(&cfg->ioctl_rwsem);
220762306a36Sopenharmony_ci	up_write(&cfg->ioctl_rwsem);
220862306a36Sopenharmony_ci}
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci/**
221162306a36Sopenharmony_ci * cxlflash_async_reset_host() - asynchronous host reset handler
221262306a36Sopenharmony_ci * @data:	Private data provided while scheduling reset.
221362306a36Sopenharmony_ci * @cookie:	Cookie that can be used for checkpointing.
221462306a36Sopenharmony_ci */
221562306a36Sopenharmony_cistatic void cxlflash_async_reset_host(void *data, async_cookie_t cookie)
221662306a36Sopenharmony_ci{
221762306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = data;
221862306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
221962306a36Sopenharmony_ci	int rc = 0;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	if (cfg->state != STATE_RESET) {
222262306a36Sopenharmony_ci		dev_dbg(dev, "%s: Not performing a reset, state=%d\n",
222362306a36Sopenharmony_ci			__func__, cfg->state);
222462306a36Sopenharmony_ci		goto out;
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	drain_ioctls(cfg);
222862306a36Sopenharmony_ci	cxlflash_mark_contexts_error(cfg);
222962306a36Sopenharmony_ci	rc = afu_reset(cfg);
223062306a36Sopenharmony_ci	if (rc)
223162306a36Sopenharmony_ci		cfg->state = STATE_FAILTERM;
223262306a36Sopenharmony_ci	else
223362306a36Sopenharmony_ci		cfg->state = STATE_NORMAL;
223462306a36Sopenharmony_ci	wake_up_all(&cfg->reset_waitq);
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ciout:
223762306a36Sopenharmony_ci	scsi_unblock_requests(cfg->host);
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci/**
224162306a36Sopenharmony_ci * cxlflash_schedule_async_reset() - schedule an asynchronous host reset
224262306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
224362306a36Sopenharmony_ci */
224462306a36Sopenharmony_cistatic void cxlflash_schedule_async_reset(struct cxlflash_cfg *cfg)
224562306a36Sopenharmony_ci{
224662306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	if (cfg->state != STATE_NORMAL) {
224962306a36Sopenharmony_ci		dev_dbg(dev, "%s: Not performing reset state=%d\n",
225062306a36Sopenharmony_ci			__func__, cfg->state);
225162306a36Sopenharmony_ci		return;
225262306a36Sopenharmony_ci	}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	cfg->state = STATE_RESET;
225562306a36Sopenharmony_ci	scsi_block_requests(cfg->host);
225662306a36Sopenharmony_ci	cfg->async_reset_cookie = async_schedule(cxlflash_async_reset_host,
225762306a36Sopenharmony_ci						 cfg);
225862306a36Sopenharmony_ci}
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci/**
226162306a36Sopenharmony_ci * send_afu_cmd() - builds and sends an internal AFU command
226262306a36Sopenharmony_ci * @afu:	AFU associated with the host.
226362306a36Sopenharmony_ci * @rcb:	Pre-populated IOARCB describing command to send.
226462306a36Sopenharmony_ci *
226562306a36Sopenharmony_ci * The AFU can only take one internal AFU command at a time. This limitation is
226662306a36Sopenharmony_ci * enforced by using a mutex to provide exclusive access to the AFU during the
226762306a36Sopenharmony_ci * operation. This design point requires calling threads to not be on interrupt
226862306a36Sopenharmony_ci * context due to the possibility of sleeping during concurrent AFU operations.
226962306a36Sopenharmony_ci *
227062306a36Sopenharmony_ci * The command status is optionally passed back to the caller when the caller
227162306a36Sopenharmony_ci * populates the IOASA field of the IOARCB with a pointer to an IOASA structure.
227262306a36Sopenharmony_ci *
227362306a36Sopenharmony_ci * Return:
227462306a36Sopenharmony_ci *	0 on success, -errno on failure
227562306a36Sopenharmony_ci */
227662306a36Sopenharmony_cistatic int send_afu_cmd(struct afu *afu, struct sisl_ioarcb *rcb)
227762306a36Sopenharmony_ci{
227862306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
227962306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
228062306a36Sopenharmony_ci	struct afu_cmd *cmd = NULL;
228162306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(afu, PRIMARY_HWQ);
228262306a36Sopenharmony_ci	ulong lock_flags;
228362306a36Sopenharmony_ci	char *buf = NULL;
228462306a36Sopenharmony_ci	int rc = 0;
228562306a36Sopenharmony_ci	int nretry = 0;
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	if (cfg->state != STATE_NORMAL) {
228862306a36Sopenharmony_ci		dev_dbg(dev, "%s: Sync not required state=%u\n",
228962306a36Sopenharmony_ci			__func__, cfg->state);
229062306a36Sopenharmony_ci		return 0;
229162306a36Sopenharmony_ci	}
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	mutex_lock(&afu->sync_active);
229462306a36Sopenharmony_ci	atomic_inc(&afu->cmds_active);
229562306a36Sopenharmony_ci	buf = kmalloc(sizeof(*cmd) + __alignof__(*cmd) - 1, GFP_KERNEL);
229662306a36Sopenharmony_ci	if (unlikely(!buf)) {
229762306a36Sopenharmony_ci		dev_err(dev, "%s: no memory for command\n", __func__);
229862306a36Sopenharmony_ci		rc = -ENOMEM;
229962306a36Sopenharmony_ci		goto out;
230062306a36Sopenharmony_ci	}
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci	cmd = (struct afu_cmd *)PTR_ALIGN(buf, __alignof__(*cmd));
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ciretry:
230562306a36Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
230662306a36Sopenharmony_ci	memcpy(&cmd->rcb, rcb, sizeof(*rcb));
230762306a36Sopenharmony_ci	INIT_LIST_HEAD(&cmd->queue);
230862306a36Sopenharmony_ci	init_completion(&cmd->cevent);
230962306a36Sopenharmony_ci	cmd->parent = afu;
231062306a36Sopenharmony_ci	cmd->hwq_index = hwq->index;
231162306a36Sopenharmony_ci	cmd->rcb.ctx_id = hwq->ctx_hndl;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	dev_dbg(dev, "%s: afu=%p cmd=%p type=%02x nretry=%d\n",
231462306a36Sopenharmony_ci		__func__, afu, cmd, cmd->rcb.cdb[0], nretry);
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	rc = afu->send_cmd(afu, cmd);
231762306a36Sopenharmony_ci	if (unlikely(rc)) {
231862306a36Sopenharmony_ci		rc = -ENOBUFS;
231962306a36Sopenharmony_ci		goto out;
232062306a36Sopenharmony_ci	}
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci	rc = wait_resp(afu, cmd);
232362306a36Sopenharmony_ci	switch (rc) {
232462306a36Sopenharmony_ci	case -ETIMEDOUT:
232562306a36Sopenharmony_ci		rc = afu->context_reset(hwq);
232662306a36Sopenharmony_ci		if (rc) {
232762306a36Sopenharmony_ci			/* Delete the command from pending_cmds list */
232862306a36Sopenharmony_ci			spin_lock_irqsave(&hwq->hsq_slock, lock_flags);
232962306a36Sopenharmony_ci			list_del(&cmd->list);
233062306a36Sopenharmony_ci			spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags);
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci			cxlflash_schedule_async_reset(cfg);
233362306a36Sopenharmony_ci			break;
233462306a36Sopenharmony_ci		}
233562306a36Sopenharmony_ci		fallthrough;	/* to retry */
233662306a36Sopenharmony_ci	case -EAGAIN:
233762306a36Sopenharmony_ci		if (++nretry < 2)
233862306a36Sopenharmony_ci			goto retry;
233962306a36Sopenharmony_ci		fallthrough;	/* to exit */
234062306a36Sopenharmony_ci	default:
234162306a36Sopenharmony_ci		break;
234262306a36Sopenharmony_ci	}
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	if (rcb->ioasa)
234562306a36Sopenharmony_ci		*rcb->ioasa = cmd->sa;
234662306a36Sopenharmony_ciout:
234762306a36Sopenharmony_ci	atomic_dec(&afu->cmds_active);
234862306a36Sopenharmony_ci	mutex_unlock(&afu->sync_active);
234962306a36Sopenharmony_ci	kfree(buf);
235062306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
235162306a36Sopenharmony_ci	return rc;
235262306a36Sopenharmony_ci}
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci/**
235562306a36Sopenharmony_ci * cxlflash_afu_sync() - builds and sends an AFU sync command
235662306a36Sopenharmony_ci * @afu:	AFU associated with the host.
235762306a36Sopenharmony_ci * @ctx:	Identifies context requesting sync.
235862306a36Sopenharmony_ci * @res:	Identifies resource requesting sync.
235962306a36Sopenharmony_ci * @mode:	Type of sync to issue (lightweight, heavyweight, global).
236062306a36Sopenharmony_ci *
236162306a36Sopenharmony_ci * AFU sync operations are only necessary and allowed when the device is
236262306a36Sopenharmony_ci * operating normally. When not operating normally, sync requests can occur as
236362306a36Sopenharmony_ci * part of cleaning up resources associated with an adapter prior to removal.
236462306a36Sopenharmony_ci * In this scenario, these requests are simply ignored (safe due to the AFU
236562306a36Sopenharmony_ci * going away).
236662306a36Sopenharmony_ci *
236762306a36Sopenharmony_ci * Return:
236862306a36Sopenharmony_ci *	0 on success, -errno on failure
236962306a36Sopenharmony_ci */
237062306a36Sopenharmony_ciint cxlflash_afu_sync(struct afu *afu, ctx_hndl_t ctx, res_hndl_t res, u8 mode)
237162306a36Sopenharmony_ci{
237262306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = afu->parent;
237362306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
237462306a36Sopenharmony_ci	struct sisl_ioarcb rcb = { 0 };
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	dev_dbg(dev, "%s: afu=%p ctx=%u res=%u mode=%u\n",
237762306a36Sopenharmony_ci		__func__, afu, ctx, res, mode);
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	rcb.req_flags = SISL_REQ_FLAGS_AFU_CMD;
238062306a36Sopenharmony_ci	rcb.msi = SISL_MSI_RRQ_UPDATED;
238162306a36Sopenharmony_ci	rcb.timeout = MC_AFU_SYNC_TIMEOUT;
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	rcb.cdb[0] = SISL_AFU_CMD_SYNC;
238462306a36Sopenharmony_ci	rcb.cdb[1] = mode;
238562306a36Sopenharmony_ci	put_unaligned_be16(ctx, &rcb.cdb[2]);
238662306a36Sopenharmony_ci	put_unaligned_be32(res, &rcb.cdb[4]);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	return send_afu_cmd(afu, &rcb);
238962306a36Sopenharmony_ci}
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci/**
239262306a36Sopenharmony_ci * cxlflash_eh_abort_handler() - abort a SCSI command
239362306a36Sopenharmony_ci * @scp:	SCSI command to abort.
239462306a36Sopenharmony_ci *
239562306a36Sopenharmony_ci * CXL Flash devices do not support a single command abort. Reset the context
239662306a36Sopenharmony_ci * as per SISLite specification. Flush any pending commands in the hardware
239762306a36Sopenharmony_ci * queue before the reset.
239862306a36Sopenharmony_ci *
239962306a36Sopenharmony_ci * Return: SUCCESS/FAILED as defined in scsi/scsi.h
240062306a36Sopenharmony_ci */
240162306a36Sopenharmony_cistatic int cxlflash_eh_abort_handler(struct scsi_cmnd *scp)
240262306a36Sopenharmony_ci{
240362306a36Sopenharmony_ci	int rc = FAILED;
240462306a36Sopenharmony_ci	struct Scsi_Host *host = scp->device->host;
240562306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(host);
240662306a36Sopenharmony_ci	struct afu_cmd *cmd = sc_to_afuc(scp);
240762306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
240862306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
240962306a36Sopenharmony_ci	struct hwq *hwq = get_hwq(afu, cmd->hwq_index);
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	dev_dbg(dev, "%s: (scp=%p) %d/%d/%d/%llu "
241262306a36Sopenharmony_ci		"cdb=(%08x-%08x-%08x-%08x)\n", __func__, scp, host->host_no,
241362306a36Sopenharmony_ci		scp->device->channel, scp->device->id, scp->device->lun,
241462306a36Sopenharmony_ci		get_unaligned_be32(&((u32 *)scp->cmnd)[0]),
241562306a36Sopenharmony_ci		get_unaligned_be32(&((u32 *)scp->cmnd)[1]),
241662306a36Sopenharmony_ci		get_unaligned_be32(&((u32 *)scp->cmnd)[2]),
241762306a36Sopenharmony_ci		get_unaligned_be32(&((u32 *)scp->cmnd)[3]));
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	/* When the state is not normal, another reset/reload is in progress.
242062306a36Sopenharmony_ci	 * Return failed and the mid-layer will invoke host reset handler.
242162306a36Sopenharmony_ci	 */
242262306a36Sopenharmony_ci	if (cfg->state != STATE_NORMAL) {
242362306a36Sopenharmony_ci		dev_dbg(dev, "%s: Invalid state for abort, state=%d\n",
242462306a36Sopenharmony_ci			__func__, cfg->state);
242562306a36Sopenharmony_ci		goto out;
242662306a36Sopenharmony_ci	}
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	rc = afu->context_reset(hwq);
242962306a36Sopenharmony_ci	if (unlikely(rc))
243062306a36Sopenharmony_ci		goto out;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	rc = SUCCESS;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ciout:
243562306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
243662306a36Sopenharmony_ci	return rc;
243762306a36Sopenharmony_ci}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci/**
244062306a36Sopenharmony_ci * cxlflash_eh_device_reset_handler() - reset a single LUN
244162306a36Sopenharmony_ci * @scp:	SCSI command to send.
244262306a36Sopenharmony_ci *
244362306a36Sopenharmony_ci * Return:
244462306a36Sopenharmony_ci *	SUCCESS as defined in scsi/scsi.h
244562306a36Sopenharmony_ci *	FAILED as defined in scsi/scsi.h
244662306a36Sopenharmony_ci */
244762306a36Sopenharmony_cistatic int cxlflash_eh_device_reset_handler(struct scsi_cmnd *scp)
244862306a36Sopenharmony_ci{
244962306a36Sopenharmony_ci	int rc = SUCCESS;
245062306a36Sopenharmony_ci	struct scsi_device *sdev = scp->device;
245162306a36Sopenharmony_ci	struct Scsi_Host *host = sdev->host;
245262306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(host);
245362306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
245462306a36Sopenharmony_ci	int rcr = 0;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	dev_dbg(dev, "%s: %d/%d/%d/%llu\n", __func__,
245762306a36Sopenharmony_ci		host->host_no, sdev->channel, sdev->id, sdev->lun);
245862306a36Sopenharmony_ciretry:
245962306a36Sopenharmony_ci	switch (cfg->state) {
246062306a36Sopenharmony_ci	case STATE_NORMAL:
246162306a36Sopenharmony_ci		rcr = send_tmf(cfg, sdev, TMF_LUN_RESET);
246262306a36Sopenharmony_ci		if (unlikely(rcr))
246362306a36Sopenharmony_ci			rc = FAILED;
246462306a36Sopenharmony_ci		break;
246562306a36Sopenharmony_ci	case STATE_RESET:
246662306a36Sopenharmony_ci		wait_event(cfg->reset_waitq, cfg->state != STATE_RESET);
246762306a36Sopenharmony_ci		goto retry;
246862306a36Sopenharmony_ci	default:
246962306a36Sopenharmony_ci		rc = FAILED;
247062306a36Sopenharmony_ci		break;
247162306a36Sopenharmony_ci	}
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
247462306a36Sopenharmony_ci	return rc;
247562306a36Sopenharmony_ci}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci/**
247862306a36Sopenharmony_ci * cxlflash_eh_host_reset_handler() - reset the host adapter
247962306a36Sopenharmony_ci * @scp:	SCSI command from stack identifying host.
248062306a36Sopenharmony_ci *
248162306a36Sopenharmony_ci * Following a reset, the state is evaluated again in case an EEH occurred
248262306a36Sopenharmony_ci * during the reset. In such a scenario, the host reset will either yield
248362306a36Sopenharmony_ci * until the EEH recovery is complete or return success or failure based
248462306a36Sopenharmony_ci * upon the current device state.
248562306a36Sopenharmony_ci *
248662306a36Sopenharmony_ci * Return:
248762306a36Sopenharmony_ci *	SUCCESS as defined in scsi/scsi.h
248862306a36Sopenharmony_ci *	FAILED as defined in scsi/scsi.h
248962306a36Sopenharmony_ci */
249062306a36Sopenharmony_cistatic int cxlflash_eh_host_reset_handler(struct scsi_cmnd *scp)
249162306a36Sopenharmony_ci{
249262306a36Sopenharmony_ci	int rc = SUCCESS;
249362306a36Sopenharmony_ci	int rcr = 0;
249462306a36Sopenharmony_ci	struct Scsi_Host *host = scp->device->host;
249562306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(host);
249662306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	dev_dbg(dev, "%s: %d\n", __func__, host->host_no);
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	switch (cfg->state) {
250162306a36Sopenharmony_ci	case STATE_NORMAL:
250262306a36Sopenharmony_ci		cfg->state = STATE_RESET;
250362306a36Sopenharmony_ci		drain_ioctls(cfg);
250462306a36Sopenharmony_ci		cxlflash_mark_contexts_error(cfg);
250562306a36Sopenharmony_ci		rcr = afu_reset(cfg);
250662306a36Sopenharmony_ci		if (rcr) {
250762306a36Sopenharmony_ci			rc = FAILED;
250862306a36Sopenharmony_ci			cfg->state = STATE_FAILTERM;
250962306a36Sopenharmony_ci		} else
251062306a36Sopenharmony_ci			cfg->state = STATE_NORMAL;
251162306a36Sopenharmony_ci		wake_up_all(&cfg->reset_waitq);
251262306a36Sopenharmony_ci		ssleep(1);
251362306a36Sopenharmony_ci		fallthrough;
251462306a36Sopenharmony_ci	case STATE_RESET:
251562306a36Sopenharmony_ci		wait_event(cfg->reset_waitq, cfg->state != STATE_RESET);
251662306a36Sopenharmony_ci		if (cfg->state == STATE_NORMAL)
251762306a36Sopenharmony_ci			break;
251862306a36Sopenharmony_ci		fallthrough;
251962306a36Sopenharmony_ci	default:
252062306a36Sopenharmony_ci		rc = FAILED;
252162306a36Sopenharmony_ci		break;
252262306a36Sopenharmony_ci	}
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
252562306a36Sopenharmony_ci	return rc;
252662306a36Sopenharmony_ci}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci/**
252962306a36Sopenharmony_ci * cxlflash_change_queue_depth() - change the queue depth for the device
253062306a36Sopenharmony_ci * @sdev:	SCSI device destined for queue depth change.
253162306a36Sopenharmony_ci * @qdepth:	Requested queue depth value to set.
253262306a36Sopenharmony_ci *
253362306a36Sopenharmony_ci * The requested queue depth is capped to the maximum supported value.
253462306a36Sopenharmony_ci *
253562306a36Sopenharmony_ci * Return: The actual queue depth set.
253662306a36Sopenharmony_ci */
253762306a36Sopenharmony_cistatic int cxlflash_change_queue_depth(struct scsi_device *sdev, int qdepth)
253862306a36Sopenharmony_ci{
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	if (qdepth > CXLFLASH_MAX_CMDS_PER_LUN)
254162306a36Sopenharmony_ci		qdepth = CXLFLASH_MAX_CMDS_PER_LUN;
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	scsi_change_queue_depth(sdev, qdepth);
254462306a36Sopenharmony_ci	return sdev->queue_depth;
254562306a36Sopenharmony_ci}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci/**
254862306a36Sopenharmony_ci * cxlflash_show_port_status() - queries and presents the current port status
254962306a36Sopenharmony_ci * @port:	Desired port for status reporting.
255062306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
255162306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
255262306a36Sopenharmony_ci *
255362306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf or -EINVAL.
255462306a36Sopenharmony_ci */
255562306a36Sopenharmony_cistatic ssize_t cxlflash_show_port_status(u32 port,
255662306a36Sopenharmony_ci					 struct cxlflash_cfg *cfg,
255762306a36Sopenharmony_ci					 char *buf)
255862306a36Sopenharmony_ci{
255962306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
256062306a36Sopenharmony_ci	char *disp_status;
256162306a36Sopenharmony_ci	u64 status;
256262306a36Sopenharmony_ci	__be64 __iomem *fc_port_regs;
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	WARN_ON(port >= MAX_FC_PORTS);
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	if (port >= cfg->num_fc_ports) {
256762306a36Sopenharmony_ci		dev_info(dev, "%s: Port %d not supported on this card.\n",
256862306a36Sopenharmony_ci			__func__, port);
256962306a36Sopenharmony_ci		return -EINVAL;
257062306a36Sopenharmony_ci	}
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	fc_port_regs = get_fc_port_regs(cfg, port);
257362306a36Sopenharmony_ci	status = readq_be(&fc_port_regs[FC_MTIP_STATUS / 8]);
257462306a36Sopenharmony_ci	status &= FC_MTIP_STATUS_MASK;
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	if (status == FC_MTIP_STATUS_ONLINE)
257762306a36Sopenharmony_ci		disp_status = "online";
257862306a36Sopenharmony_ci	else if (status == FC_MTIP_STATUS_OFFLINE)
257962306a36Sopenharmony_ci		disp_status = "offline";
258062306a36Sopenharmony_ci	else
258162306a36Sopenharmony_ci		disp_status = "unknown";
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%s\n", disp_status);
258462306a36Sopenharmony_ci}
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci/**
258762306a36Sopenharmony_ci * port0_show() - queries and presents the current status of port 0
258862306a36Sopenharmony_ci * @dev:	Generic device associated with the host owning the port.
258962306a36Sopenharmony_ci * @attr:	Device attribute representing the port.
259062306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
259162306a36Sopenharmony_ci *
259262306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
259362306a36Sopenharmony_ci */
259462306a36Sopenharmony_cistatic ssize_t port0_show(struct device *dev,
259562306a36Sopenharmony_ci			  struct device_attribute *attr,
259662306a36Sopenharmony_ci			  char *buf)
259762306a36Sopenharmony_ci{
259862306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	return cxlflash_show_port_status(0, cfg, buf);
260162306a36Sopenharmony_ci}
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_ci/**
260462306a36Sopenharmony_ci * port1_show() - queries and presents the current status of port 1
260562306a36Sopenharmony_ci * @dev:	Generic device associated with the host owning the port.
260662306a36Sopenharmony_ci * @attr:	Device attribute representing the port.
260762306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
260862306a36Sopenharmony_ci *
260962306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
261062306a36Sopenharmony_ci */
261162306a36Sopenharmony_cistatic ssize_t port1_show(struct device *dev,
261262306a36Sopenharmony_ci			  struct device_attribute *attr,
261362306a36Sopenharmony_ci			  char *buf)
261462306a36Sopenharmony_ci{
261562306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	return cxlflash_show_port_status(1, cfg, buf);
261862306a36Sopenharmony_ci}
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci/**
262162306a36Sopenharmony_ci * port2_show() - queries and presents the current status of port 2
262262306a36Sopenharmony_ci * @dev:	Generic device associated with the host owning the port.
262362306a36Sopenharmony_ci * @attr:	Device attribute representing the port.
262462306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
262562306a36Sopenharmony_ci *
262662306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
262762306a36Sopenharmony_ci */
262862306a36Sopenharmony_cistatic ssize_t port2_show(struct device *dev,
262962306a36Sopenharmony_ci			  struct device_attribute *attr,
263062306a36Sopenharmony_ci			  char *buf)
263162306a36Sopenharmony_ci{
263262306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	return cxlflash_show_port_status(2, cfg, buf);
263562306a36Sopenharmony_ci}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci/**
263862306a36Sopenharmony_ci * port3_show() - queries and presents the current status of port 3
263962306a36Sopenharmony_ci * @dev:	Generic device associated with the host owning the port.
264062306a36Sopenharmony_ci * @attr:	Device attribute representing the port.
264162306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
264262306a36Sopenharmony_ci *
264362306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
264462306a36Sopenharmony_ci */
264562306a36Sopenharmony_cistatic ssize_t port3_show(struct device *dev,
264662306a36Sopenharmony_ci			  struct device_attribute *attr,
264762306a36Sopenharmony_ci			  char *buf)
264862306a36Sopenharmony_ci{
264962306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci	return cxlflash_show_port_status(3, cfg, buf);
265262306a36Sopenharmony_ci}
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci/**
265562306a36Sopenharmony_ci * lun_mode_show() - presents the current LUN mode of the host
265662306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
265762306a36Sopenharmony_ci * @attr:	Device attribute representing the LUN mode.
265862306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back the LUN mode in ASCII.
265962306a36Sopenharmony_ci *
266062306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
266162306a36Sopenharmony_ci */
266262306a36Sopenharmony_cistatic ssize_t lun_mode_show(struct device *dev,
266362306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
266462306a36Sopenharmony_ci{
266562306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
266662306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%u\n", afu->internal_lun);
266962306a36Sopenharmony_ci}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci/**
267262306a36Sopenharmony_ci * lun_mode_store() - sets the LUN mode of the host
267362306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
267462306a36Sopenharmony_ci * @attr:	Device attribute representing the LUN mode.
267562306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE containing the LUN mode in ASCII.
267662306a36Sopenharmony_ci * @count:	Length of data resizing in @buf.
267762306a36Sopenharmony_ci *
267862306a36Sopenharmony_ci * The CXL Flash AFU supports a dummy LUN mode where the external
267962306a36Sopenharmony_ci * links and storage are not required. Space on the FPGA is used
268062306a36Sopenharmony_ci * to create 1 or 2 small LUNs which are presented to the system
268162306a36Sopenharmony_ci * as if they were a normal storage device. This feature is useful
268262306a36Sopenharmony_ci * during development and also provides manufacturing with a way
268362306a36Sopenharmony_ci * to test the AFU without an actual device.
268462306a36Sopenharmony_ci *
268562306a36Sopenharmony_ci * 0 = external LUN[s] (default)
268662306a36Sopenharmony_ci * 1 = internal LUN (1 x 64K, 512B blocks, id 0)
268762306a36Sopenharmony_ci * 2 = internal LUN (1 x 64K, 4K blocks, id 0)
268862306a36Sopenharmony_ci * 3 = internal LUN (2 x 32K, 512B blocks, ids 0,1)
268962306a36Sopenharmony_ci * 4 = internal LUN (2 x 32K, 4K blocks, ids 0,1)
269062306a36Sopenharmony_ci *
269162306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
269262306a36Sopenharmony_ci */
269362306a36Sopenharmony_cistatic ssize_t lun_mode_store(struct device *dev,
269462306a36Sopenharmony_ci			      struct device_attribute *attr,
269562306a36Sopenharmony_ci			      const char *buf, size_t count)
269662306a36Sopenharmony_ci{
269762306a36Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
269862306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(shost);
269962306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
270062306a36Sopenharmony_ci	int rc;
270162306a36Sopenharmony_ci	u32 lun_mode;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	rc = kstrtouint(buf, 10, &lun_mode);
270462306a36Sopenharmony_ci	if (!rc && (lun_mode < 5) && (lun_mode != afu->internal_lun)) {
270562306a36Sopenharmony_ci		afu->internal_lun = lun_mode;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci		/*
270862306a36Sopenharmony_ci		 * When configured for internal LUN, there is only one channel,
270962306a36Sopenharmony_ci		 * channel number 0, else there will be one less than the number
271062306a36Sopenharmony_ci		 * of fc ports for this card.
271162306a36Sopenharmony_ci		 */
271262306a36Sopenharmony_ci		if (afu->internal_lun)
271362306a36Sopenharmony_ci			shost->max_channel = 0;
271462306a36Sopenharmony_ci		else
271562306a36Sopenharmony_ci			shost->max_channel = PORTNUM2CHAN(cfg->num_fc_ports);
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci		afu_reset(cfg);
271862306a36Sopenharmony_ci		scsi_scan_host(cfg->host);
271962306a36Sopenharmony_ci	}
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	return count;
272262306a36Sopenharmony_ci}
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci/**
272562306a36Sopenharmony_ci * ioctl_version_show() - presents the current ioctl version of the host
272662306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
272762306a36Sopenharmony_ci * @attr:	Device attribute representing the ioctl version.
272862306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back the ioctl version.
272962306a36Sopenharmony_ci *
273062306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
273162306a36Sopenharmony_ci */
273262306a36Sopenharmony_cistatic ssize_t ioctl_version_show(struct device *dev,
273362306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
273462306a36Sopenharmony_ci{
273562306a36Sopenharmony_ci	ssize_t bytes = 0;
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	bytes = scnprintf(buf, PAGE_SIZE,
273862306a36Sopenharmony_ci			  "disk: %u\n", DK_CXLFLASH_VERSION_0);
273962306a36Sopenharmony_ci	bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
274062306a36Sopenharmony_ci			   "host: %u\n", HT_CXLFLASH_VERSION_0);
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	return bytes;
274362306a36Sopenharmony_ci}
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci/**
274662306a36Sopenharmony_ci * cxlflash_show_port_lun_table() - queries and presents the port LUN table
274762306a36Sopenharmony_ci * @port:	Desired port for status reporting.
274862306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
274962306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
275062306a36Sopenharmony_ci *
275162306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf or -EINVAL.
275262306a36Sopenharmony_ci */
275362306a36Sopenharmony_cistatic ssize_t cxlflash_show_port_lun_table(u32 port,
275462306a36Sopenharmony_ci					    struct cxlflash_cfg *cfg,
275562306a36Sopenharmony_ci					    char *buf)
275662306a36Sopenharmony_ci{
275762306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
275862306a36Sopenharmony_ci	__be64 __iomem *fc_port_luns;
275962306a36Sopenharmony_ci	int i;
276062306a36Sopenharmony_ci	ssize_t bytes = 0;
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	WARN_ON(port >= MAX_FC_PORTS);
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	if (port >= cfg->num_fc_ports) {
276562306a36Sopenharmony_ci		dev_info(dev, "%s: Port %d not supported on this card.\n",
276662306a36Sopenharmony_ci			__func__, port);
276762306a36Sopenharmony_ci		return -EINVAL;
276862306a36Sopenharmony_ci	}
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	fc_port_luns = get_fc_port_luns(cfg, port);
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	for (i = 0; i < CXLFLASH_NUM_VLUNS; i++)
277362306a36Sopenharmony_ci		bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
277462306a36Sopenharmony_ci				   "%03d: %016llx\n",
277562306a36Sopenharmony_ci				   i, readq_be(&fc_port_luns[i]));
277662306a36Sopenharmony_ci	return bytes;
277762306a36Sopenharmony_ci}
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci/**
278062306a36Sopenharmony_ci * port0_lun_table_show() - presents the current LUN table of port 0
278162306a36Sopenharmony_ci * @dev:	Generic device associated with the host owning the port.
278262306a36Sopenharmony_ci * @attr:	Device attribute representing the port.
278362306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
278462306a36Sopenharmony_ci *
278562306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
278662306a36Sopenharmony_ci */
278762306a36Sopenharmony_cistatic ssize_t port0_lun_table_show(struct device *dev,
278862306a36Sopenharmony_ci				    struct device_attribute *attr,
278962306a36Sopenharmony_ci				    char *buf)
279062306a36Sopenharmony_ci{
279162306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci	return cxlflash_show_port_lun_table(0, cfg, buf);
279462306a36Sopenharmony_ci}
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci/**
279762306a36Sopenharmony_ci * port1_lun_table_show() - presents the current LUN table of port 1
279862306a36Sopenharmony_ci * @dev:	Generic device associated with the host owning the port.
279962306a36Sopenharmony_ci * @attr:	Device attribute representing the port.
280062306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
280162306a36Sopenharmony_ci *
280262306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
280362306a36Sopenharmony_ci */
280462306a36Sopenharmony_cistatic ssize_t port1_lun_table_show(struct device *dev,
280562306a36Sopenharmony_ci				    struct device_attribute *attr,
280662306a36Sopenharmony_ci				    char *buf)
280762306a36Sopenharmony_ci{
280862306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	return cxlflash_show_port_lun_table(1, cfg, buf);
281162306a36Sopenharmony_ci}
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci/**
281462306a36Sopenharmony_ci * port2_lun_table_show() - presents the current LUN table of port 2
281562306a36Sopenharmony_ci * @dev:	Generic device associated with the host owning the port.
281662306a36Sopenharmony_ci * @attr:	Device attribute representing the port.
281762306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
281862306a36Sopenharmony_ci *
281962306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
282062306a36Sopenharmony_ci */
282162306a36Sopenharmony_cistatic ssize_t port2_lun_table_show(struct device *dev,
282262306a36Sopenharmony_ci				    struct device_attribute *attr,
282362306a36Sopenharmony_ci				    char *buf)
282462306a36Sopenharmony_ci{
282562306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	return cxlflash_show_port_lun_table(2, cfg, buf);
282862306a36Sopenharmony_ci}
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci/**
283162306a36Sopenharmony_ci * port3_lun_table_show() - presents the current LUN table of port 3
283262306a36Sopenharmony_ci * @dev:	Generic device associated with the host owning the port.
283362306a36Sopenharmony_ci * @attr:	Device attribute representing the port.
283462306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back port status in ASCII.
283562306a36Sopenharmony_ci *
283662306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
283762306a36Sopenharmony_ci */
283862306a36Sopenharmony_cistatic ssize_t port3_lun_table_show(struct device *dev,
283962306a36Sopenharmony_ci				    struct device_attribute *attr,
284062306a36Sopenharmony_ci				    char *buf)
284162306a36Sopenharmony_ci{
284262306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	return cxlflash_show_port_lun_table(3, cfg, buf);
284562306a36Sopenharmony_ci}
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci/**
284862306a36Sopenharmony_ci * irqpoll_weight_show() - presents the current IRQ poll weight for the host
284962306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
285062306a36Sopenharmony_ci * @attr:	Device attribute representing the IRQ poll weight.
285162306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back the current IRQ poll
285262306a36Sopenharmony_ci *		weight in ASCII.
285362306a36Sopenharmony_ci *
285462306a36Sopenharmony_ci * An IRQ poll weight of 0 indicates polling is disabled.
285562306a36Sopenharmony_ci *
285662306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
285762306a36Sopenharmony_ci */
285862306a36Sopenharmony_cistatic ssize_t irqpoll_weight_show(struct device *dev,
285962306a36Sopenharmony_ci				   struct device_attribute *attr, char *buf)
286062306a36Sopenharmony_ci{
286162306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
286262306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%u\n", afu->irqpoll_weight);
286562306a36Sopenharmony_ci}
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_ci/**
286862306a36Sopenharmony_ci * irqpoll_weight_store() - sets the current IRQ poll weight for the host
286962306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
287062306a36Sopenharmony_ci * @attr:	Device attribute representing the IRQ poll weight.
287162306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE containing the desired IRQ poll
287262306a36Sopenharmony_ci *		weight in ASCII.
287362306a36Sopenharmony_ci * @count:	Length of data resizing in @buf.
287462306a36Sopenharmony_ci *
287562306a36Sopenharmony_ci * An IRQ poll weight of 0 indicates polling is disabled.
287662306a36Sopenharmony_ci *
287762306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
287862306a36Sopenharmony_ci */
287962306a36Sopenharmony_cistatic ssize_t irqpoll_weight_store(struct device *dev,
288062306a36Sopenharmony_ci				    struct device_attribute *attr,
288162306a36Sopenharmony_ci				    const char *buf, size_t count)
288262306a36Sopenharmony_ci{
288362306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
288462306a36Sopenharmony_ci	struct device *cfgdev = &cfg->dev->dev;
288562306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
288662306a36Sopenharmony_ci	struct hwq *hwq;
288762306a36Sopenharmony_ci	u32 weight;
288862306a36Sopenharmony_ci	int rc, i;
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	rc = kstrtouint(buf, 10, &weight);
289162306a36Sopenharmony_ci	if (rc)
289262306a36Sopenharmony_ci		return -EINVAL;
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	if (weight > 256) {
289562306a36Sopenharmony_ci		dev_info(cfgdev,
289662306a36Sopenharmony_ci			 "Invalid IRQ poll weight. It must be 256 or less.\n");
289762306a36Sopenharmony_ci		return -EINVAL;
289862306a36Sopenharmony_ci	}
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci	if (weight == afu->irqpoll_weight) {
290162306a36Sopenharmony_ci		dev_info(cfgdev,
290262306a36Sopenharmony_ci			 "Current IRQ poll weight has the same weight.\n");
290362306a36Sopenharmony_ci		return -EINVAL;
290462306a36Sopenharmony_ci	}
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	if (afu_is_irqpoll_enabled(afu)) {
290762306a36Sopenharmony_ci		for (i = 0; i < afu->num_hwqs; i++) {
290862306a36Sopenharmony_ci			hwq = get_hwq(afu, i);
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci			irq_poll_disable(&hwq->irqpoll);
291162306a36Sopenharmony_ci		}
291262306a36Sopenharmony_ci	}
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	afu->irqpoll_weight = weight;
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	if (weight > 0) {
291762306a36Sopenharmony_ci		for (i = 0; i < afu->num_hwqs; i++) {
291862306a36Sopenharmony_ci			hwq = get_hwq(afu, i);
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci			irq_poll_init(&hwq->irqpoll, weight, cxlflash_irqpoll);
292162306a36Sopenharmony_ci		}
292262306a36Sopenharmony_ci	}
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	return count;
292562306a36Sopenharmony_ci}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci/**
292862306a36Sopenharmony_ci * num_hwqs_show() - presents the number of hardware queues for the host
292962306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
293062306a36Sopenharmony_ci * @attr:	Device attribute representing the number of hardware queues.
293162306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back the number of hardware
293262306a36Sopenharmony_ci *		queues in ASCII.
293362306a36Sopenharmony_ci *
293462306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
293562306a36Sopenharmony_ci */
293662306a36Sopenharmony_cistatic ssize_t num_hwqs_show(struct device *dev,
293762306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
293862306a36Sopenharmony_ci{
293962306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
294062306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%u\n", afu->num_hwqs);
294362306a36Sopenharmony_ci}
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_ci/**
294662306a36Sopenharmony_ci * num_hwqs_store() - sets the number of hardware queues for the host
294762306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
294862306a36Sopenharmony_ci * @attr:	Device attribute representing the number of hardware queues.
294962306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE containing the number of hardware
295062306a36Sopenharmony_ci *		queues in ASCII.
295162306a36Sopenharmony_ci * @count:	Length of data resizing in @buf.
295262306a36Sopenharmony_ci *
295362306a36Sopenharmony_ci * n > 0: num_hwqs = n
295462306a36Sopenharmony_ci * n = 0: num_hwqs = num_online_cpus()
295562306a36Sopenharmony_ci * n < 0: num_online_cpus() / abs(n)
295662306a36Sopenharmony_ci *
295762306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
295862306a36Sopenharmony_ci */
295962306a36Sopenharmony_cistatic ssize_t num_hwqs_store(struct device *dev,
296062306a36Sopenharmony_ci			      struct device_attribute *attr,
296162306a36Sopenharmony_ci			      const char *buf, size_t count)
296262306a36Sopenharmony_ci{
296362306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
296462306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
296562306a36Sopenharmony_ci	int rc;
296662306a36Sopenharmony_ci	int nhwqs, num_hwqs;
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	rc = kstrtoint(buf, 10, &nhwqs);
296962306a36Sopenharmony_ci	if (rc)
297062306a36Sopenharmony_ci		return -EINVAL;
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	if (nhwqs >= 1)
297362306a36Sopenharmony_ci		num_hwqs = nhwqs;
297462306a36Sopenharmony_ci	else if (nhwqs == 0)
297562306a36Sopenharmony_ci		num_hwqs = num_online_cpus();
297662306a36Sopenharmony_ci	else
297762306a36Sopenharmony_ci		num_hwqs = num_online_cpus() / abs(nhwqs);
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	afu->desired_hwqs = min(num_hwqs, CXLFLASH_MAX_HWQS);
298062306a36Sopenharmony_ci	WARN_ON_ONCE(afu->desired_hwqs == 0);
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ciretry:
298362306a36Sopenharmony_ci	switch (cfg->state) {
298462306a36Sopenharmony_ci	case STATE_NORMAL:
298562306a36Sopenharmony_ci		cfg->state = STATE_RESET;
298662306a36Sopenharmony_ci		drain_ioctls(cfg);
298762306a36Sopenharmony_ci		cxlflash_mark_contexts_error(cfg);
298862306a36Sopenharmony_ci		rc = afu_reset(cfg);
298962306a36Sopenharmony_ci		if (rc)
299062306a36Sopenharmony_ci			cfg->state = STATE_FAILTERM;
299162306a36Sopenharmony_ci		else
299262306a36Sopenharmony_ci			cfg->state = STATE_NORMAL;
299362306a36Sopenharmony_ci		wake_up_all(&cfg->reset_waitq);
299462306a36Sopenharmony_ci		break;
299562306a36Sopenharmony_ci	case STATE_RESET:
299662306a36Sopenharmony_ci		wait_event(cfg->reset_waitq, cfg->state != STATE_RESET);
299762306a36Sopenharmony_ci		if (cfg->state == STATE_NORMAL)
299862306a36Sopenharmony_ci			goto retry;
299962306a36Sopenharmony_ci		fallthrough;
300062306a36Sopenharmony_ci	default:
300162306a36Sopenharmony_ci		/* Ideally should not happen */
300262306a36Sopenharmony_ci		dev_err(dev, "%s: Device is not ready, state=%d\n",
300362306a36Sopenharmony_ci			__func__, cfg->state);
300462306a36Sopenharmony_ci		break;
300562306a36Sopenharmony_ci	}
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	return count;
300862306a36Sopenharmony_ci}
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_cistatic const char *hwq_mode_name[MAX_HWQ_MODE] = { "rr", "tag", "cpu" };
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci/**
301362306a36Sopenharmony_ci * hwq_mode_show() - presents the HWQ steering mode for the host
301462306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
301562306a36Sopenharmony_ci * @attr:	Device attribute representing the HWQ steering mode.
301662306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back the HWQ steering mode
301762306a36Sopenharmony_ci *		as a character string.
301862306a36Sopenharmony_ci *
301962306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
302062306a36Sopenharmony_ci */
302162306a36Sopenharmony_cistatic ssize_t hwq_mode_show(struct device *dev,
302262306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
302362306a36Sopenharmony_ci{
302462306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev));
302562306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%s\n", hwq_mode_name[afu->hwq_mode]);
302862306a36Sopenharmony_ci}
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci/**
303162306a36Sopenharmony_ci * hwq_mode_store() - sets the HWQ steering mode for the host
303262306a36Sopenharmony_ci * @dev:	Generic device associated with the host.
303362306a36Sopenharmony_ci * @attr:	Device attribute representing the HWQ steering mode.
303462306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE containing the HWQ steering mode
303562306a36Sopenharmony_ci *		as a character string.
303662306a36Sopenharmony_ci * @count:	Length of data resizing in @buf.
303762306a36Sopenharmony_ci *
303862306a36Sopenharmony_ci * rr = Round-Robin
303962306a36Sopenharmony_ci * tag = Block MQ Tagging
304062306a36Sopenharmony_ci * cpu = CPU Affinity
304162306a36Sopenharmony_ci *
304262306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
304362306a36Sopenharmony_ci */
304462306a36Sopenharmony_cistatic ssize_t hwq_mode_store(struct device *dev,
304562306a36Sopenharmony_ci			      struct device_attribute *attr,
304662306a36Sopenharmony_ci			      const char *buf, size_t count)
304762306a36Sopenharmony_ci{
304862306a36Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
304962306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = shost_priv(shost);
305062306a36Sopenharmony_ci	struct device *cfgdev = &cfg->dev->dev;
305162306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
305262306a36Sopenharmony_ci	int i;
305362306a36Sopenharmony_ci	u32 mode = MAX_HWQ_MODE;
305462306a36Sopenharmony_ci
305562306a36Sopenharmony_ci	for (i = 0; i < MAX_HWQ_MODE; i++) {
305662306a36Sopenharmony_ci		if (!strncmp(hwq_mode_name[i], buf, strlen(hwq_mode_name[i]))) {
305762306a36Sopenharmony_ci			mode = i;
305862306a36Sopenharmony_ci			break;
305962306a36Sopenharmony_ci		}
306062306a36Sopenharmony_ci	}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	if (mode >= MAX_HWQ_MODE) {
306362306a36Sopenharmony_ci		dev_info(cfgdev, "Invalid HWQ steering mode.\n");
306462306a36Sopenharmony_ci		return -EINVAL;
306562306a36Sopenharmony_ci	}
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci	afu->hwq_mode = mode;
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci	return count;
307062306a36Sopenharmony_ci}
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci/**
307362306a36Sopenharmony_ci * mode_show() - presents the current mode of the device
307462306a36Sopenharmony_ci * @dev:	Generic device associated with the device.
307562306a36Sopenharmony_ci * @attr:	Device attribute representing the device mode.
307662306a36Sopenharmony_ci * @buf:	Buffer of length PAGE_SIZE to report back the dev mode in ASCII.
307762306a36Sopenharmony_ci *
307862306a36Sopenharmony_ci * Return: The size of the ASCII string returned in @buf.
307962306a36Sopenharmony_ci */
308062306a36Sopenharmony_cistatic ssize_t mode_show(struct device *dev,
308162306a36Sopenharmony_ci			 struct device_attribute *attr, char *buf)
308262306a36Sopenharmony_ci{
308362306a36Sopenharmony_ci	struct scsi_device *sdev = to_scsi_device(dev);
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%s\n",
308662306a36Sopenharmony_ci			 sdev->hostdata ? "superpipe" : "legacy");
308762306a36Sopenharmony_ci}
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci/*
309062306a36Sopenharmony_ci * Host attributes
309162306a36Sopenharmony_ci */
309262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(port0);
309362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(port1);
309462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(port2);
309562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(port3);
309662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(lun_mode);
309762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ioctl_version);
309862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(port0_lun_table);
309962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(port1_lun_table);
310062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(port2_lun_table);
310162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(port3_lun_table);
310262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(irqpoll_weight);
310362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(num_hwqs);
310462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(hwq_mode);
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_cistatic struct attribute *cxlflash_host_attrs[] = {
310762306a36Sopenharmony_ci	&dev_attr_port0.attr,
310862306a36Sopenharmony_ci	&dev_attr_port1.attr,
310962306a36Sopenharmony_ci	&dev_attr_port2.attr,
311062306a36Sopenharmony_ci	&dev_attr_port3.attr,
311162306a36Sopenharmony_ci	&dev_attr_lun_mode.attr,
311262306a36Sopenharmony_ci	&dev_attr_ioctl_version.attr,
311362306a36Sopenharmony_ci	&dev_attr_port0_lun_table.attr,
311462306a36Sopenharmony_ci	&dev_attr_port1_lun_table.attr,
311562306a36Sopenharmony_ci	&dev_attr_port2_lun_table.attr,
311662306a36Sopenharmony_ci	&dev_attr_port3_lun_table.attr,
311762306a36Sopenharmony_ci	&dev_attr_irqpoll_weight.attr,
311862306a36Sopenharmony_ci	&dev_attr_num_hwqs.attr,
311962306a36Sopenharmony_ci	&dev_attr_hwq_mode.attr,
312062306a36Sopenharmony_ci	NULL
312162306a36Sopenharmony_ci};
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ciATTRIBUTE_GROUPS(cxlflash_host);
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci/*
312662306a36Sopenharmony_ci * Device attributes
312762306a36Sopenharmony_ci */
312862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(mode);
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_cistatic struct attribute *cxlflash_dev_attrs[] = {
313162306a36Sopenharmony_ci	&dev_attr_mode.attr,
313262306a36Sopenharmony_ci	NULL
313362306a36Sopenharmony_ci};
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ciATTRIBUTE_GROUPS(cxlflash_dev);
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_ci/*
313862306a36Sopenharmony_ci * Host template
313962306a36Sopenharmony_ci */
314062306a36Sopenharmony_cistatic struct scsi_host_template driver_template = {
314162306a36Sopenharmony_ci	.module = THIS_MODULE,
314262306a36Sopenharmony_ci	.name = CXLFLASH_ADAPTER_NAME,
314362306a36Sopenharmony_ci	.info = cxlflash_driver_info,
314462306a36Sopenharmony_ci	.ioctl = cxlflash_ioctl,
314562306a36Sopenharmony_ci	.proc_name = CXLFLASH_NAME,
314662306a36Sopenharmony_ci	.queuecommand = cxlflash_queuecommand,
314762306a36Sopenharmony_ci	.eh_abort_handler = cxlflash_eh_abort_handler,
314862306a36Sopenharmony_ci	.eh_device_reset_handler = cxlflash_eh_device_reset_handler,
314962306a36Sopenharmony_ci	.eh_host_reset_handler = cxlflash_eh_host_reset_handler,
315062306a36Sopenharmony_ci	.change_queue_depth = cxlflash_change_queue_depth,
315162306a36Sopenharmony_ci	.cmd_per_lun = CXLFLASH_MAX_CMDS_PER_LUN,
315262306a36Sopenharmony_ci	.can_queue = CXLFLASH_MAX_CMDS,
315362306a36Sopenharmony_ci	.cmd_size = sizeof(struct afu_cmd) + __alignof__(struct afu_cmd) - 1,
315462306a36Sopenharmony_ci	.this_id = -1,
315562306a36Sopenharmony_ci	.sg_tablesize = 1,	/* No scatter gather support */
315662306a36Sopenharmony_ci	.max_sectors = CXLFLASH_MAX_SECTORS,
315762306a36Sopenharmony_ci	.shost_groups = cxlflash_host_groups,
315862306a36Sopenharmony_ci	.sdev_groups = cxlflash_dev_groups,
315962306a36Sopenharmony_ci};
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci/*
316262306a36Sopenharmony_ci * Device dependent values
316362306a36Sopenharmony_ci */
316462306a36Sopenharmony_cistatic struct dev_dependent_vals dev_corsa_vals = { CXLFLASH_MAX_SECTORS,
316562306a36Sopenharmony_ci					CXLFLASH_WWPN_VPD_REQUIRED };
316662306a36Sopenharmony_cistatic struct dev_dependent_vals dev_flash_gt_vals = { CXLFLASH_MAX_SECTORS,
316762306a36Sopenharmony_ci					CXLFLASH_NOTIFY_SHUTDOWN };
316862306a36Sopenharmony_cistatic struct dev_dependent_vals dev_briard_vals = { CXLFLASH_MAX_SECTORS,
316962306a36Sopenharmony_ci					(CXLFLASH_NOTIFY_SHUTDOWN |
317062306a36Sopenharmony_ci					CXLFLASH_OCXL_DEV) };
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci/*
317362306a36Sopenharmony_ci * PCI device binding table
317462306a36Sopenharmony_ci */
317562306a36Sopenharmony_cistatic struct pci_device_id cxlflash_pci_table[] = {
317662306a36Sopenharmony_ci	{PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CORSA,
317762306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_corsa_vals},
317862306a36Sopenharmony_ci	{PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_FLASH_GT,
317962306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_flash_gt_vals},
318062306a36Sopenharmony_ci	{PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_BRIARD,
318162306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_briard_vals},
318262306a36Sopenharmony_ci	{}
318362306a36Sopenharmony_ci};
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cxlflash_pci_table);
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci/**
318862306a36Sopenharmony_ci * cxlflash_worker_thread() - work thread handler for the AFU
318962306a36Sopenharmony_ci * @work:	Work structure contained within cxlflash associated with host.
319062306a36Sopenharmony_ci *
319162306a36Sopenharmony_ci * Handles the following events:
319262306a36Sopenharmony_ci * - Link reset which cannot be performed on interrupt context due to
319362306a36Sopenharmony_ci * blocking up to a few seconds
319462306a36Sopenharmony_ci * - Rescan the host
319562306a36Sopenharmony_ci */
319662306a36Sopenharmony_cistatic void cxlflash_worker_thread(struct work_struct *work)
319762306a36Sopenharmony_ci{
319862306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = container_of(work, struct cxlflash_cfg,
319962306a36Sopenharmony_ci						work_q);
320062306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
320162306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
320262306a36Sopenharmony_ci	__be64 __iomem *fc_port_regs;
320362306a36Sopenharmony_ci	int port;
320462306a36Sopenharmony_ci	ulong lock_flags;
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	/* Avoid MMIO if the device has failed */
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	if (cfg->state != STATE_NORMAL)
320962306a36Sopenharmony_ci		return;
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	spin_lock_irqsave(cfg->host->host_lock, lock_flags);
321262306a36Sopenharmony_ci
321362306a36Sopenharmony_ci	if (cfg->lr_state == LINK_RESET_REQUIRED) {
321462306a36Sopenharmony_ci		port = cfg->lr_port;
321562306a36Sopenharmony_ci		if (port < 0)
321662306a36Sopenharmony_ci			dev_err(dev, "%s: invalid port index %d\n",
321762306a36Sopenharmony_ci				__func__, port);
321862306a36Sopenharmony_ci		else {
321962306a36Sopenharmony_ci			spin_unlock_irqrestore(cfg->host->host_lock,
322062306a36Sopenharmony_ci					       lock_flags);
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci			/* The reset can block... */
322362306a36Sopenharmony_ci			fc_port_regs = get_fc_port_regs(cfg, port);
322462306a36Sopenharmony_ci			afu_link_reset(afu, port, fc_port_regs);
322562306a36Sopenharmony_ci			spin_lock_irqsave(cfg->host->host_lock, lock_flags);
322662306a36Sopenharmony_ci		}
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci		cfg->lr_state = LINK_RESET_COMPLETE;
322962306a36Sopenharmony_ci	}
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_ci	spin_unlock_irqrestore(cfg->host->host_lock, lock_flags);
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	if (atomic_dec_if_positive(&cfg->scan_host_needed) >= 0)
323462306a36Sopenharmony_ci		scsi_scan_host(cfg->host);
323562306a36Sopenharmony_ci}
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci/**
323862306a36Sopenharmony_ci * cxlflash_chr_open() - character device open handler
323962306a36Sopenharmony_ci * @inode:	Device inode associated with this character device.
324062306a36Sopenharmony_ci * @file:	File pointer for this device.
324162306a36Sopenharmony_ci *
324262306a36Sopenharmony_ci * Only users with admin privileges are allowed to open the character device.
324362306a36Sopenharmony_ci *
324462306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
324562306a36Sopenharmony_ci */
324662306a36Sopenharmony_cistatic int cxlflash_chr_open(struct inode *inode, struct file *file)
324762306a36Sopenharmony_ci{
324862306a36Sopenharmony_ci	struct cxlflash_cfg *cfg;
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
325162306a36Sopenharmony_ci		return -EACCES;
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	cfg = container_of(inode->i_cdev, struct cxlflash_cfg, cdev);
325462306a36Sopenharmony_ci	file->private_data = cfg;
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci	return 0;
325762306a36Sopenharmony_ci}
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_ci/**
326062306a36Sopenharmony_ci * decode_hioctl() - translates encoded host ioctl to easily identifiable string
326162306a36Sopenharmony_ci * @cmd:        The host ioctl command to decode.
326262306a36Sopenharmony_ci *
326362306a36Sopenharmony_ci * Return: A string identifying the decoded host ioctl.
326462306a36Sopenharmony_ci */
326562306a36Sopenharmony_cistatic char *decode_hioctl(unsigned int cmd)
326662306a36Sopenharmony_ci{
326762306a36Sopenharmony_ci	switch (cmd) {
326862306a36Sopenharmony_ci	case HT_CXLFLASH_LUN_PROVISION:
326962306a36Sopenharmony_ci		return __stringify_1(HT_CXLFLASH_LUN_PROVISION);
327062306a36Sopenharmony_ci	}
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci	return "UNKNOWN";
327362306a36Sopenharmony_ci}
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci/**
327662306a36Sopenharmony_ci * cxlflash_lun_provision() - host LUN provisioning handler
327762306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
327862306a36Sopenharmony_ci * @lunprov:	Kernel copy of userspace ioctl data structure.
327962306a36Sopenharmony_ci *
328062306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
328162306a36Sopenharmony_ci */
328262306a36Sopenharmony_cistatic int cxlflash_lun_provision(struct cxlflash_cfg *cfg,
328362306a36Sopenharmony_ci				  struct ht_cxlflash_lun_provision *lunprov)
328462306a36Sopenharmony_ci{
328562306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
328662306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
328762306a36Sopenharmony_ci	struct sisl_ioarcb rcb;
328862306a36Sopenharmony_ci	struct sisl_ioasa asa;
328962306a36Sopenharmony_ci	__be64 __iomem *fc_port_regs;
329062306a36Sopenharmony_ci	u16 port = lunprov->port;
329162306a36Sopenharmony_ci	u16 scmd = lunprov->hdr.subcmd;
329262306a36Sopenharmony_ci	u16 type;
329362306a36Sopenharmony_ci	u64 reg;
329462306a36Sopenharmony_ci	u64 size;
329562306a36Sopenharmony_ci	u64 lun_id;
329662306a36Sopenharmony_ci	int rc = 0;
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	if (!afu_is_lun_provision(afu)) {
329962306a36Sopenharmony_ci		rc = -ENOTSUPP;
330062306a36Sopenharmony_ci		goto out;
330162306a36Sopenharmony_ci	}
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci	if (port >= cfg->num_fc_ports) {
330462306a36Sopenharmony_ci		rc = -EINVAL;
330562306a36Sopenharmony_ci		goto out;
330662306a36Sopenharmony_ci	}
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_ci	switch (scmd) {
330962306a36Sopenharmony_ci	case HT_CXLFLASH_LUN_PROVISION_SUBCMD_CREATE_LUN:
331062306a36Sopenharmony_ci		type = SISL_AFU_LUN_PROVISION_CREATE;
331162306a36Sopenharmony_ci		size = lunprov->size;
331262306a36Sopenharmony_ci		lun_id = 0;
331362306a36Sopenharmony_ci		break;
331462306a36Sopenharmony_ci	case HT_CXLFLASH_LUN_PROVISION_SUBCMD_DELETE_LUN:
331562306a36Sopenharmony_ci		type = SISL_AFU_LUN_PROVISION_DELETE;
331662306a36Sopenharmony_ci		size = 0;
331762306a36Sopenharmony_ci		lun_id = lunprov->lun_id;
331862306a36Sopenharmony_ci		break;
331962306a36Sopenharmony_ci	case HT_CXLFLASH_LUN_PROVISION_SUBCMD_QUERY_PORT:
332062306a36Sopenharmony_ci		fc_port_regs = get_fc_port_regs(cfg, port);
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ci		reg = readq_be(&fc_port_regs[FC_MAX_NUM_LUNS / 8]);
332362306a36Sopenharmony_ci		lunprov->max_num_luns = reg;
332462306a36Sopenharmony_ci		reg = readq_be(&fc_port_regs[FC_CUR_NUM_LUNS / 8]);
332562306a36Sopenharmony_ci		lunprov->cur_num_luns = reg;
332662306a36Sopenharmony_ci		reg = readq_be(&fc_port_regs[FC_MAX_CAP_PORT / 8]);
332762306a36Sopenharmony_ci		lunprov->max_cap_port = reg;
332862306a36Sopenharmony_ci		reg = readq_be(&fc_port_regs[FC_CUR_CAP_PORT / 8]);
332962306a36Sopenharmony_ci		lunprov->cur_cap_port = reg;
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci		goto out;
333262306a36Sopenharmony_ci	default:
333362306a36Sopenharmony_ci		rc = -EINVAL;
333462306a36Sopenharmony_ci		goto out;
333562306a36Sopenharmony_ci	}
333662306a36Sopenharmony_ci
333762306a36Sopenharmony_ci	memset(&rcb, 0, sizeof(rcb));
333862306a36Sopenharmony_ci	memset(&asa, 0, sizeof(asa));
333962306a36Sopenharmony_ci	rcb.req_flags = SISL_REQ_FLAGS_AFU_CMD;
334062306a36Sopenharmony_ci	rcb.lun_id = lun_id;
334162306a36Sopenharmony_ci	rcb.msi = SISL_MSI_RRQ_UPDATED;
334262306a36Sopenharmony_ci	rcb.timeout = MC_LUN_PROV_TIMEOUT;
334362306a36Sopenharmony_ci	rcb.ioasa = &asa;
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci	rcb.cdb[0] = SISL_AFU_CMD_LUN_PROVISION;
334662306a36Sopenharmony_ci	rcb.cdb[1] = type;
334762306a36Sopenharmony_ci	rcb.cdb[2] = port;
334862306a36Sopenharmony_ci	put_unaligned_be64(size, &rcb.cdb[8]);
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	rc = send_afu_cmd(afu, &rcb);
335162306a36Sopenharmony_ci	if (rc) {
335262306a36Sopenharmony_ci		dev_err(dev, "%s: send_afu_cmd failed rc=%d asc=%08x afux=%x\n",
335362306a36Sopenharmony_ci			__func__, rc, asa.ioasc, asa.afu_extra);
335462306a36Sopenharmony_ci		goto out;
335562306a36Sopenharmony_ci	}
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci	if (scmd == HT_CXLFLASH_LUN_PROVISION_SUBCMD_CREATE_LUN) {
335862306a36Sopenharmony_ci		lunprov->lun_id = (u64)asa.lunid_hi << 32 | asa.lunid_lo;
335962306a36Sopenharmony_ci		memcpy(lunprov->wwid, asa.wwid, sizeof(lunprov->wwid));
336062306a36Sopenharmony_ci	}
336162306a36Sopenharmony_ciout:
336262306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
336362306a36Sopenharmony_ci	return rc;
336462306a36Sopenharmony_ci}
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_ci/**
336762306a36Sopenharmony_ci * cxlflash_afu_debug() - host AFU debug handler
336862306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
336962306a36Sopenharmony_ci * @afu_dbg:	Kernel copy of userspace ioctl data structure.
337062306a36Sopenharmony_ci *
337162306a36Sopenharmony_ci * For debug requests requiring a data buffer, always provide an aligned
337262306a36Sopenharmony_ci * (cache line) buffer to the AFU to appease any alignment requirements.
337362306a36Sopenharmony_ci *
337462306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
337562306a36Sopenharmony_ci */
337662306a36Sopenharmony_cistatic int cxlflash_afu_debug(struct cxlflash_cfg *cfg,
337762306a36Sopenharmony_ci			      struct ht_cxlflash_afu_debug *afu_dbg)
337862306a36Sopenharmony_ci{
337962306a36Sopenharmony_ci	struct afu *afu = cfg->afu;
338062306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
338162306a36Sopenharmony_ci	struct sisl_ioarcb rcb;
338262306a36Sopenharmony_ci	struct sisl_ioasa asa;
338362306a36Sopenharmony_ci	char *buf = NULL;
338462306a36Sopenharmony_ci	char *kbuf = NULL;
338562306a36Sopenharmony_ci	void __user *ubuf = (__force void __user *)afu_dbg->data_ea;
338662306a36Sopenharmony_ci	u16 req_flags = SISL_REQ_FLAGS_AFU_CMD;
338762306a36Sopenharmony_ci	u32 ulen = afu_dbg->data_len;
338862306a36Sopenharmony_ci	bool is_write = afu_dbg->hdr.flags & HT_CXLFLASH_HOST_WRITE;
338962306a36Sopenharmony_ci	int rc = 0;
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci	if (!afu_is_afu_debug(afu)) {
339262306a36Sopenharmony_ci		rc = -ENOTSUPP;
339362306a36Sopenharmony_ci		goto out;
339462306a36Sopenharmony_ci	}
339562306a36Sopenharmony_ci
339662306a36Sopenharmony_ci	if (ulen) {
339762306a36Sopenharmony_ci		req_flags |= SISL_REQ_FLAGS_SUP_UNDERRUN;
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ci		if (ulen > HT_CXLFLASH_AFU_DEBUG_MAX_DATA_LEN) {
340062306a36Sopenharmony_ci			rc = -EINVAL;
340162306a36Sopenharmony_ci			goto out;
340262306a36Sopenharmony_ci		}
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci		buf = kmalloc(ulen + cache_line_size() - 1, GFP_KERNEL);
340562306a36Sopenharmony_ci		if (unlikely(!buf)) {
340662306a36Sopenharmony_ci			rc = -ENOMEM;
340762306a36Sopenharmony_ci			goto out;
340862306a36Sopenharmony_ci		}
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_ci		kbuf = PTR_ALIGN(buf, cache_line_size());
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_ci		if (is_write) {
341362306a36Sopenharmony_ci			req_flags |= SISL_REQ_FLAGS_HOST_WRITE;
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci			if (copy_from_user(kbuf, ubuf, ulen)) {
341662306a36Sopenharmony_ci				rc = -EFAULT;
341762306a36Sopenharmony_ci				goto out;
341862306a36Sopenharmony_ci			}
341962306a36Sopenharmony_ci		}
342062306a36Sopenharmony_ci	}
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_ci	memset(&rcb, 0, sizeof(rcb));
342362306a36Sopenharmony_ci	memset(&asa, 0, sizeof(asa));
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	rcb.req_flags = req_flags;
342662306a36Sopenharmony_ci	rcb.msi = SISL_MSI_RRQ_UPDATED;
342762306a36Sopenharmony_ci	rcb.timeout = MC_AFU_DEBUG_TIMEOUT;
342862306a36Sopenharmony_ci	rcb.ioasa = &asa;
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci	if (ulen) {
343162306a36Sopenharmony_ci		rcb.data_len = ulen;
343262306a36Sopenharmony_ci		rcb.data_ea = (uintptr_t)kbuf;
343362306a36Sopenharmony_ci	}
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci	rcb.cdb[0] = SISL_AFU_CMD_DEBUG;
343662306a36Sopenharmony_ci	memcpy(&rcb.cdb[4], afu_dbg->afu_subcmd,
343762306a36Sopenharmony_ci	       HT_CXLFLASH_AFU_DEBUG_SUBCMD_LEN);
343862306a36Sopenharmony_ci
343962306a36Sopenharmony_ci	rc = send_afu_cmd(afu, &rcb);
344062306a36Sopenharmony_ci	if (rc) {
344162306a36Sopenharmony_ci		dev_err(dev, "%s: send_afu_cmd failed rc=%d asc=%08x afux=%x\n",
344262306a36Sopenharmony_ci			__func__, rc, asa.ioasc, asa.afu_extra);
344362306a36Sopenharmony_ci		goto out;
344462306a36Sopenharmony_ci	}
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci	if (ulen && !is_write) {
344762306a36Sopenharmony_ci		if (copy_to_user(ubuf, kbuf, ulen))
344862306a36Sopenharmony_ci			rc = -EFAULT;
344962306a36Sopenharmony_ci	}
345062306a36Sopenharmony_ciout:
345162306a36Sopenharmony_ci	kfree(buf);
345262306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
345362306a36Sopenharmony_ci	return rc;
345462306a36Sopenharmony_ci}
345562306a36Sopenharmony_ci
345662306a36Sopenharmony_ci/**
345762306a36Sopenharmony_ci * cxlflash_chr_ioctl() - character device IOCTL handler
345862306a36Sopenharmony_ci * @file:	File pointer for this device.
345962306a36Sopenharmony_ci * @cmd:	IOCTL command.
346062306a36Sopenharmony_ci * @arg:	Userspace ioctl data structure.
346162306a36Sopenharmony_ci *
346262306a36Sopenharmony_ci * A read/write semaphore is used to implement a 'drain' of currently
346362306a36Sopenharmony_ci * running ioctls. The read semaphore is taken at the beginning of each
346462306a36Sopenharmony_ci * ioctl thread and released upon concluding execution. Additionally the
346562306a36Sopenharmony_ci * semaphore should be released and then reacquired in any ioctl execution
346662306a36Sopenharmony_ci * path which will wait for an event to occur that is outside the scope of
346762306a36Sopenharmony_ci * the ioctl (i.e. an adapter reset). To drain the ioctls currently running,
346862306a36Sopenharmony_ci * a thread simply needs to acquire the write semaphore.
346962306a36Sopenharmony_ci *
347062306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
347162306a36Sopenharmony_ci */
347262306a36Sopenharmony_cistatic long cxlflash_chr_ioctl(struct file *file, unsigned int cmd,
347362306a36Sopenharmony_ci			       unsigned long arg)
347462306a36Sopenharmony_ci{
347562306a36Sopenharmony_ci	typedef int (*hioctl) (struct cxlflash_cfg *, void *);
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = file->private_data;
347862306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
347962306a36Sopenharmony_ci	char buf[sizeof(union cxlflash_ht_ioctls)];
348062306a36Sopenharmony_ci	void __user *uarg = (void __user *)arg;
348162306a36Sopenharmony_ci	struct ht_cxlflash_hdr *hdr;
348262306a36Sopenharmony_ci	size_t size = 0;
348362306a36Sopenharmony_ci	bool known_ioctl = false;
348462306a36Sopenharmony_ci	int idx = 0;
348562306a36Sopenharmony_ci	int rc = 0;
348662306a36Sopenharmony_ci	hioctl do_ioctl = NULL;
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_ci	static const struct {
348962306a36Sopenharmony_ci		size_t size;
349062306a36Sopenharmony_ci		hioctl ioctl;
349162306a36Sopenharmony_ci	} ioctl_tbl[] = {	/* NOTE: order matters here */
349262306a36Sopenharmony_ci	{ sizeof(struct ht_cxlflash_lun_provision),
349362306a36Sopenharmony_ci		(hioctl)cxlflash_lun_provision },
349462306a36Sopenharmony_ci	{ sizeof(struct ht_cxlflash_afu_debug),
349562306a36Sopenharmony_ci		(hioctl)cxlflash_afu_debug },
349662306a36Sopenharmony_ci	};
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci	/* Hold read semaphore so we can drain if needed */
349962306a36Sopenharmony_ci	down_read(&cfg->ioctl_rwsem);
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci	dev_dbg(dev, "%s: cmd=%u idx=%d tbl_size=%lu\n",
350262306a36Sopenharmony_ci		__func__, cmd, idx, sizeof(ioctl_tbl));
350362306a36Sopenharmony_ci
350462306a36Sopenharmony_ci	switch (cmd) {
350562306a36Sopenharmony_ci	case HT_CXLFLASH_LUN_PROVISION:
350662306a36Sopenharmony_ci	case HT_CXLFLASH_AFU_DEBUG:
350762306a36Sopenharmony_ci		known_ioctl = true;
350862306a36Sopenharmony_ci		idx = _IOC_NR(HT_CXLFLASH_LUN_PROVISION) - _IOC_NR(cmd);
350962306a36Sopenharmony_ci		size = ioctl_tbl[idx].size;
351062306a36Sopenharmony_ci		do_ioctl = ioctl_tbl[idx].ioctl;
351162306a36Sopenharmony_ci
351262306a36Sopenharmony_ci		if (likely(do_ioctl))
351362306a36Sopenharmony_ci			break;
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci		fallthrough;
351662306a36Sopenharmony_ci	default:
351762306a36Sopenharmony_ci		rc = -EINVAL;
351862306a36Sopenharmony_ci		goto out;
351962306a36Sopenharmony_ci	}
352062306a36Sopenharmony_ci
352162306a36Sopenharmony_ci	if (unlikely(copy_from_user(&buf, uarg, size))) {
352262306a36Sopenharmony_ci		dev_err(dev, "%s: copy_from_user() fail "
352362306a36Sopenharmony_ci			"size=%lu cmd=%d (%s) uarg=%p\n",
352462306a36Sopenharmony_ci			__func__, size, cmd, decode_hioctl(cmd), uarg);
352562306a36Sopenharmony_ci		rc = -EFAULT;
352662306a36Sopenharmony_ci		goto out;
352762306a36Sopenharmony_ci	}
352862306a36Sopenharmony_ci
352962306a36Sopenharmony_ci	hdr = (struct ht_cxlflash_hdr *)&buf;
353062306a36Sopenharmony_ci	if (hdr->version != HT_CXLFLASH_VERSION_0) {
353162306a36Sopenharmony_ci		dev_dbg(dev, "%s: Version %u not supported for %s\n",
353262306a36Sopenharmony_ci			__func__, hdr->version, decode_hioctl(cmd));
353362306a36Sopenharmony_ci		rc = -EINVAL;
353462306a36Sopenharmony_ci		goto out;
353562306a36Sopenharmony_ci	}
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	if (hdr->rsvd[0] || hdr->rsvd[1] || hdr->return_flags) {
353862306a36Sopenharmony_ci		dev_dbg(dev, "%s: Reserved/rflags populated\n", __func__);
353962306a36Sopenharmony_ci		rc = -EINVAL;
354062306a36Sopenharmony_ci		goto out;
354162306a36Sopenharmony_ci	}
354262306a36Sopenharmony_ci
354362306a36Sopenharmony_ci	rc = do_ioctl(cfg, (void *)&buf);
354462306a36Sopenharmony_ci	if (likely(!rc))
354562306a36Sopenharmony_ci		if (unlikely(copy_to_user(uarg, &buf, size))) {
354662306a36Sopenharmony_ci			dev_err(dev, "%s: copy_to_user() fail "
354762306a36Sopenharmony_ci				"size=%lu cmd=%d (%s) uarg=%p\n",
354862306a36Sopenharmony_ci				__func__, size, cmd, decode_hioctl(cmd), uarg);
354962306a36Sopenharmony_ci			rc = -EFAULT;
355062306a36Sopenharmony_ci		}
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	/* fall through to exit */
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ciout:
355562306a36Sopenharmony_ci	up_read(&cfg->ioctl_rwsem);
355662306a36Sopenharmony_ci	if (unlikely(rc && known_ioctl))
355762306a36Sopenharmony_ci		dev_err(dev, "%s: ioctl %s (%08X) returned rc=%d\n",
355862306a36Sopenharmony_ci			__func__, decode_hioctl(cmd), cmd, rc);
355962306a36Sopenharmony_ci	else
356062306a36Sopenharmony_ci		dev_dbg(dev, "%s: ioctl %s (%08X) returned rc=%d\n",
356162306a36Sopenharmony_ci			__func__, decode_hioctl(cmd), cmd, rc);
356262306a36Sopenharmony_ci	return rc;
356362306a36Sopenharmony_ci}
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci/*
356662306a36Sopenharmony_ci * Character device file operations
356762306a36Sopenharmony_ci */
356862306a36Sopenharmony_cistatic const struct file_operations cxlflash_chr_fops = {
356962306a36Sopenharmony_ci	.owner          = THIS_MODULE,
357062306a36Sopenharmony_ci	.open           = cxlflash_chr_open,
357162306a36Sopenharmony_ci	.unlocked_ioctl	= cxlflash_chr_ioctl,
357262306a36Sopenharmony_ci	.compat_ioctl	= compat_ptr_ioctl,
357362306a36Sopenharmony_ci};
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_ci/**
357662306a36Sopenharmony_ci * init_chrdev() - initialize the character device for the host
357762306a36Sopenharmony_ci * @cfg:	Internal structure associated with the host.
357862306a36Sopenharmony_ci *
357962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
358062306a36Sopenharmony_ci */
358162306a36Sopenharmony_cistatic int init_chrdev(struct cxlflash_cfg *cfg)
358262306a36Sopenharmony_ci{
358362306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
358462306a36Sopenharmony_ci	struct device *char_dev;
358562306a36Sopenharmony_ci	dev_t devno;
358662306a36Sopenharmony_ci	int minor;
358762306a36Sopenharmony_ci	int rc = 0;
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci	minor = cxlflash_get_minor();
359062306a36Sopenharmony_ci	if (unlikely(minor < 0)) {
359162306a36Sopenharmony_ci		dev_err(dev, "%s: Exhausted allowed adapters\n", __func__);
359262306a36Sopenharmony_ci		rc = -ENOSPC;
359362306a36Sopenharmony_ci		goto out;
359462306a36Sopenharmony_ci	}
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_ci	devno = MKDEV(cxlflash_major, minor);
359762306a36Sopenharmony_ci	cdev_init(&cfg->cdev, &cxlflash_chr_fops);
359862306a36Sopenharmony_ci
359962306a36Sopenharmony_ci	rc = cdev_add(&cfg->cdev, devno, 1);
360062306a36Sopenharmony_ci	if (rc) {
360162306a36Sopenharmony_ci		dev_err(dev, "%s: cdev_add failed rc=%d\n", __func__, rc);
360262306a36Sopenharmony_ci		goto err1;
360362306a36Sopenharmony_ci	}
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci	char_dev = device_create(cxlflash_class, NULL, devno,
360662306a36Sopenharmony_ci				 NULL, "cxlflash%d", minor);
360762306a36Sopenharmony_ci	if (IS_ERR(char_dev)) {
360862306a36Sopenharmony_ci		rc = PTR_ERR(char_dev);
360962306a36Sopenharmony_ci		dev_err(dev, "%s: device_create failed rc=%d\n",
361062306a36Sopenharmony_ci			__func__, rc);
361162306a36Sopenharmony_ci		goto err2;
361262306a36Sopenharmony_ci	}
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	cfg->chardev = char_dev;
361562306a36Sopenharmony_ciout:
361662306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
361762306a36Sopenharmony_ci	return rc;
361862306a36Sopenharmony_cierr2:
361962306a36Sopenharmony_ci	cdev_del(&cfg->cdev);
362062306a36Sopenharmony_cierr1:
362162306a36Sopenharmony_ci	cxlflash_put_minor(minor);
362262306a36Sopenharmony_ci	goto out;
362362306a36Sopenharmony_ci}
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_ci/**
362662306a36Sopenharmony_ci * cxlflash_probe() - PCI entry point to add host
362762306a36Sopenharmony_ci * @pdev:	PCI device associated with the host.
362862306a36Sopenharmony_ci * @dev_id:	PCI device id associated with device.
362962306a36Sopenharmony_ci *
363062306a36Sopenharmony_ci * The device will initially start out in a 'probing' state and
363162306a36Sopenharmony_ci * transition to the 'normal' state at the end of a successful
363262306a36Sopenharmony_ci * probe. Should an EEH event occur during probe, the notification
363362306a36Sopenharmony_ci * thread (error_detected()) will wait until the probe handler
363462306a36Sopenharmony_ci * is nearly complete. At that time, the device will be moved to
363562306a36Sopenharmony_ci * a 'probed' state and the EEH thread woken up to drive the slot
363662306a36Sopenharmony_ci * reset and recovery (device moves to 'normal' state). Meanwhile,
363762306a36Sopenharmony_ci * the probe will be allowed to exit successfully.
363862306a36Sopenharmony_ci *
363962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
364062306a36Sopenharmony_ci */
364162306a36Sopenharmony_cistatic int cxlflash_probe(struct pci_dev *pdev,
364262306a36Sopenharmony_ci			  const struct pci_device_id *dev_id)
364362306a36Sopenharmony_ci{
364462306a36Sopenharmony_ci	struct Scsi_Host *host;
364562306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = NULL;
364662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
364762306a36Sopenharmony_ci	struct dev_dependent_vals *ddv;
364862306a36Sopenharmony_ci	int rc = 0;
364962306a36Sopenharmony_ci	int k;
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "%s: Found CXLFLASH with IRQ: %d\n",
365262306a36Sopenharmony_ci		__func__, pdev->irq);
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_ci	ddv = (struct dev_dependent_vals *)dev_id->driver_data;
365562306a36Sopenharmony_ci	driver_template.max_sectors = ddv->max_sectors;
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci	host = scsi_host_alloc(&driver_template, sizeof(struct cxlflash_cfg));
365862306a36Sopenharmony_ci	if (!host) {
365962306a36Sopenharmony_ci		dev_err(dev, "%s: scsi_host_alloc failed\n", __func__);
366062306a36Sopenharmony_ci		rc = -ENOMEM;
366162306a36Sopenharmony_ci		goto out;
366262306a36Sopenharmony_ci	}
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_ci	host->max_id = CXLFLASH_MAX_NUM_TARGETS_PER_BUS;
366562306a36Sopenharmony_ci	host->max_lun = CXLFLASH_MAX_NUM_LUNS_PER_TARGET;
366662306a36Sopenharmony_ci	host->unique_id = host->host_no;
366762306a36Sopenharmony_ci	host->max_cmd_len = CXLFLASH_MAX_CDB_LEN;
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	cfg = shost_priv(host);
367062306a36Sopenharmony_ci	cfg->state = STATE_PROBING;
367162306a36Sopenharmony_ci	cfg->host = host;
367262306a36Sopenharmony_ci	rc = alloc_mem(cfg);
367362306a36Sopenharmony_ci	if (rc) {
367462306a36Sopenharmony_ci		dev_err(dev, "%s: alloc_mem failed\n", __func__);
367562306a36Sopenharmony_ci		rc = -ENOMEM;
367662306a36Sopenharmony_ci		scsi_host_put(cfg->host);
367762306a36Sopenharmony_ci		goto out;
367862306a36Sopenharmony_ci	}
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_ci	cfg->init_state = INIT_STATE_NONE;
368162306a36Sopenharmony_ci	cfg->dev = pdev;
368262306a36Sopenharmony_ci	cfg->cxl_fops = cxlflash_cxl_fops;
368362306a36Sopenharmony_ci	cfg->ops = cxlflash_assign_ops(ddv);
368462306a36Sopenharmony_ci	WARN_ON_ONCE(!cfg->ops);
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	/*
368762306a36Sopenharmony_ci	 * Promoted LUNs move to the top of the LUN table. The rest stay on
368862306a36Sopenharmony_ci	 * the bottom half. The bottom half grows from the end (index = 255),
368962306a36Sopenharmony_ci	 * whereas the top half grows from the beginning (index = 0).
369062306a36Sopenharmony_ci	 *
369162306a36Sopenharmony_ci	 * Initialize the last LUN index for all possible ports.
369262306a36Sopenharmony_ci	 */
369362306a36Sopenharmony_ci	cfg->promote_lun_index = 0;
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_ci	for (k = 0; k < MAX_FC_PORTS; k++)
369662306a36Sopenharmony_ci		cfg->last_lun_index[k] = CXLFLASH_NUM_VLUNS/2 - 1;
369762306a36Sopenharmony_ci
369862306a36Sopenharmony_ci	cfg->dev_id = (struct pci_device_id *)dev_id;
369962306a36Sopenharmony_ci
370062306a36Sopenharmony_ci	init_waitqueue_head(&cfg->tmf_waitq);
370162306a36Sopenharmony_ci	init_waitqueue_head(&cfg->reset_waitq);
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci	INIT_WORK(&cfg->work_q, cxlflash_worker_thread);
370462306a36Sopenharmony_ci	cfg->lr_state = LINK_RESET_INVALID;
370562306a36Sopenharmony_ci	cfg->lr_port = -1;
370662306a36Sopenharmony_ci	spin_lock_init(&cfg->tmf_slock);
370762306a36Sopenharmony_ci	mutex_init(&cfg->ctx_tbl_list_mutex);
370862306a36Sopenharmony_ci	mutex_init(&cfg->ctx_recovery_mutex);
370962306a36Sopenharmony_ci	init_rwsem(&cfg->ioctl_rwsem);
371062306a36Sopenharmony_ci	INIT_LIST_HEAD(&cfg->ctx_err_recovery);
371162306a36Sopenharmony_ci	INIT_LIST_HEAD(&cfg->lluns);
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_ci	pci_set_drvdata(pdev, cfg);
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci	rc = init_pci(cfg);
371662306a36Sopenharmony_ci	if (rc) {
371762306a36Sopenharmony_ci		dev_err(dev, "%s: init_pci failed rc=%d\n", __func__, rc);
371862306a36Sopenharmony_ci		goto out_remove;
371962306a36Sopenharmony_ci	}
372062306a36Sopenharmony_ci	cfg->init_state = INIT_STATE_PCI;
372162306a36Sopenharmony_ci
372262306a36Sopenharmony_ci	cfg->afu_cookie = cfg->ops->create_afu(pdev);
372362306a36Sopenharmony_ci	if (unlikely(!cfg->afu_cookie)) {
372462306a36Sopenharmony_ci		dev_err(dev, "%s: create_afu failed\n", __func__);
372562306a36Sopenharmony_ci		rc = -ENOMEM;
372662306a36Sopenharmony_ci		goto out_remove;
372762306a36Sopenharmony_ci	}
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	rc = init_afu(cfg);
373062306a36Sopenharmony_ci	if (rc && !wq_has_sleeper(&cfg->reset_waitq)) {
373162306a36Sopenharmony_ci		dev_err(dev, "%s: init_afu failed rc=%d\n", __func__, rc);
373262306a36Sopenharmony_ci		goto out_remove;
373362306a36Sopenharmony_ci	}
373462306a36Sopenharmony_ci	cfg->init_state = INIT_STATE_AFU;
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	rc = init_scsi(cfg);
373762306a36Sopenharmony_ci	if (rc) {
373862306a36Sopenharmony_ci		dev_err(dev, "%s: init_scsi failed rc=%d\n", __func__, rc);
373962306a36Sopenharmony_ci		goto out_remove;
374062306a36Sopenharmony_ci	}
374162306a36Sopenharmony_ci	cfg->init_state = INIT_STATE_SCSI;
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ci	rc = init_chrdev(cfg);
374462306a36Sopenharmony_ci	if (rc) {
374562306a36Sopenharmony_ci		dev_err(dev, "%s: init_chrdev failed rc=%d\n", __func__, rc);
374662306a36Sopenharmony_ci		goto out_remove;
374762306a36Sopenharmony_ci	}
374862306a36Sopenharmony_ci	cfg->init_state = INIT_STATE_CDEV;
374962306a36Sopenharmony_ci
375062306a36Sopenharmony_ci	if (wq_has_sleeper(&cfg->reset_waitq)) {
375162306a36Sopenharmony_ci		cfg->state = STATE_PROBED;
375262306a36Sopenharmony_ci		wake_up_all(&cfg->reset_waitq);
375362306a36Sopenharmony_ci	} else
375462306a36Sopenharmony_ci		cfg->state = STATE_NORMAL;
375562306a36Sopenharmony_ciout:
375662306a36Sopenharmony_ci	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
375762306a36Sopenharmony_ci	return rc;
375862306a36Sopenharmony_ci
375962306a36Sopenharmony_ciout_remove:
376062306a36Sopenharmony_ci	cfg->state = STATE_PROBED;
376162306a36Sopenharmony_ci	cxlflash_remove(pdev);
376262306a36Sopenharmony_ci	goto out;
376362306a36Sopenharmony_ci}
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci/**
376662306a36Sopenharmony_ci * cxlflash_pci_error_detected() - called when a PCI error is detected
376762306a36Sopenharmony_ci * @pdev:	PCI device struct.
376862306a36Sopenharmony_ci * @state:	PCI channel state.
376962306a36Sopenharmony_ci *
377062306a36Sopenharmony_ci * When an EEH occurs during an active reset, wait until the reset is
377162306a36Sopenharmony_ci * complete and then take action based upon the device state.
377262306a36Sopenharmony_ci *
377362306a36Sopenharmony_ci * Return: PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT
377462306a36Sopenharmony_ci */
377562306a36Sopenharmony_cistatic pci_ers_result_t cxlflash_pci_error_detected(struct pci_dev *pdev,
377662306a36Sopenharmony_ci						    pci_channel_state_t state)
377762306a36Sopenharmony_ci{
377862306a36Sopenharmony_ci	int rc = 0;
377962306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = pci_get_drvdata(pdev);
378062306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci	dev_dbg(dev, "%s: pdev=%p state=%u\n", __func__, pdev, state);
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci	switch (state) {
378562306a36Sopenharmony_ci	case pci_channel_io_frozen:
378662306a36Sopenharmony_ci		wait_event(cfg->reset_waitq, cfg->state != STATE_RESET &&
378762306a36Sopenharmony_ci					     cfg->state != STATE_PROBING);
378862306a36Sopenharmony_ci		if (cfg->state == STATE_FAILTERM)
378962306a36Sopenharmony_ci			return PCI_ERS_RESULT_DISCONNECT;
379062306a36Sopenharmony_ci
379162306a36Sopenharmony_ci		cfg->state = STATE_RESET;
379262306a36Sopenharmony_ci		scsi_block_requests(cfg->host);
379362306a36Sopenharmony_ci		drain_ioctls(cfg);
379462306a36Sopenharmony_ci		rc = cxlflash_mark_contexts_error(cfg);
379562306a36Sopenharmony_ci		if (unlikely(rc))
379662306a36Sopenharmony_ci			dev_err(dev, "%s: Failed to mark user contexts rc=%d\n",
379762306a36Sopenharmony_ci				__func__, rc);
379862306a36Sopenharmony_ci		term_afu(cfg);
379962306a36Sopenharmony_ci		return PCI_ERS_RESULT_NEED_RESET;
380062306a36Sopenharmony_ci	case pci_channel_io_perm_failure:
380162306a36Sopenharmony_ci		cfg->state = STATE_FAILTERM;
380262306a36Sopenharmony_ci		wake_up_all(&cfg->reset_waitq);
380362306a36Sopenharmony_ci		scsi_unblock_requests(cfg->host);
380462306a36Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
380562306a36Sopenharmony_ci	default:
380662306a36Sopenharmony_ci		break;
380762306a36Sopenharmony_ci	}
380862306a36Sopenharmony_ci	return PCI_ERS_RESULT_NEED_RESET;
380962306a36Sopenharmony_ci}
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ci/**
381262306a36Sopenharmony_ci * cxlflash_pci_slot_reset() - called when PCI slot has been reset
381362306a36Sopenharmony_ci * @pdev:	PCI device struct.
381462306a36Sopenharmony_ci *
381562306a36Sopenharmony_ci * This routine is called by the pci error recovery code after the PCI
381662306a36Sopenharmony_ci * slot has been reset, just before we should resume normal operations.
381762306a36Sopenharmony_ci *
381862306a36Sopenharmony_ci * Return: PCI_ERS_RESULT_RECOVERED or PCI_ERS_RESULT_DISCONNECT
381962306a36Sopenharmony_ci */
382062306a36Sopenharmony_cistatic pci_ers_result_t cxlflash_pci_slot_reset(struct pci_dev *pdev)
382162306a36Sopenharmony_ci{
382262306a36Sopenharmony_ci	int rc = 0;
382362306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = pci_get_drvdata(pdev);
382462306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_ci	dev_dbg(dev, "%s: pdev=%p\n", __func__, pdev);
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci	rc = init_afu(cfg);
382962306a36Sopenharmony_ci	if (unlikely(rc)) {
383062306a36Sopenharmony_ci		dev_err(dev, "%s: EEH recovery failed rc=%d\n", __func__, rc);
383162306a36Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
383262306a36Sopenharmony_ci	}
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_ci	return PCI_ERS_RESULT_RECOVERED;
383562306a36Sopenharmony_ci}
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_ci/**
383862306a36Sopenharmony_ci * cxlflash_pci_resume() - called when normal operation can resume
383962306a36Sopenharmony_ci * @pdev:	PCI device struct
384062306a36Sopenharmony_ci */
384162306a36Sopenharmony_cistatic void cxlflash_pci_resume(struct pci_dev *pdev)
384262306a36Sopenharmony_ci{
384362306a36Sopenharmony_ci	struct cxlflash_cfg *cfg = pci_get_drvdata(pdev);
384462306a36Sopenharmony_ci	struct device *dev = &cfg->dev->dev;
384562306a36Sopenharmony_ci
384662306a36Sopenharmony_ci	dev_dbg(dev, "%s: pdev=%p\n", __func__, pdev);
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci	cfg->state = STATE_NORMAL;
384962306a36Sopenharmony_ci	wake_up_all(&cfg->reset_waitq);
385062306a36Sopenharmony_ci	scsi_unblock_requests(cfg->host);
385162306a36Sopenharmony_ci}
385262306a36Sopenharmony_ci
385362306a36Sopenharmony_ci/**
385462306a36Sopenharmony_ci * cxlflash_devnode() - provides devtmpfs for devices in the cxlflash class
385562306a36Sopenharmony_ci * @dev:	Character device.
385662306a36Sopenharmony_ci * @mode:	Mode that can be used to verify access.
385762306a36Sopenharmony_ci *
385862306a36Sopenharmony_ci * Return: Allocated string describing the devtmpfs structure.
385962306a36Sopenharmony_ci */
386062306a36Sopenharmony_cistatic char *cxlflash_devnode(const struct device *dev, umode_t *mode)
386162306a36Sopenharmony_ci{
386262306a36Sopenharmony_ci	return kasprintf(GFP_KERNEL, "cxlflash/%s", dev_name(dev));
386362306a36Sopenharmony_ci}
386462306a36Sopenharmony_ci
386562306a36Sopenharmony_ci/**
386662306a36Sopenharmony_ci * cxlflash_class_init() - create character device class
386762306a36Sopenharmony_ci *
386862306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
386962306a36Sopenharmony_ci */
387062306a36Sopenharmony_cistatic int cxlflash_class_init(void)
387162306a36Sopenharmony_ci{
387262306a36Sopenharmony_ci	dev_t devno;
387362306a36Sopenharmony_ci	int rc = 0;
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	rc = alloc_chrdev_region(&devno, 0, CXLFLASH_MAX_ADAPTERS, "cxlflash");
387662306a36Sopenharmony_ci	if (unlikely(rc)) {
387762306a36Sopenharmony_ci		pr_err("%s: alloc_chrdev_region failed rc=%d\n", __func__, rc);
387862306a36Sopenharmony_ci		goto out;
387962306a36Sopenharmony_ci	}
388062306a36Sopenharmony_ci
388162306a36Sopenharmony_ci	cxlflash_major = MAJOR(devno);
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	cxlflash_class = class_create("cxlflash");
388462306a36Sopenharmony_ci	if (IS_ERR(cxlflash_class)) {
388562306a36Sopenharmony_ci		rc = PTR_ERR(cxlflash_class);
388662306a36Sopenharmony_ci		pr_err("%s: class_create failed rc=%d\n", __func__, rc);
388762306a36Sopenharmony_ci		goto err;
388862306a36Sopenharmony_ci	}
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	cxlflash_class->devnode = cxlflash_devnode;
389162306a36Sopenharmony_ciout:
389262306a36Sopenharmony_ci	pr_debug("%s: returning rc=%d\n", __func__, rc);
389362306a36Sopenharmony_ci	return rc;
389462306a36Sopenharmony_cierr:
389562306a36Sopenharmony_ci	unregister_chrdev_region(devno, CXLFLASH_MAX_ADAPTERS);
389662306a36Sopenharmony_ci	goto out;
389762306a36Sopenharmony_ci}
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci/**
390062306a36Sopenharmony_ci * cxlflash_class_exit() - destroy character device class
390162306a36Sopenharmony_ci */
390262306a36Sopenharmony_cistatic void cxlflash_class_exit(void)
390362306a36Sopenharmony_ci{
390462306a36Sopenharmony_ci	dev_t devno = MKDEV(cxlflash_major, 0);
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	class_destroy(cxlflash_class);
390762306a36Sopenharmony_ci	unregister_chrdev_region(devno, CXLFLASH_MAX_ADAPTERS);
390862306a36Sopenharmony_ci}
390962306a36Sopenharmony_ci
391062306a36Sopenharmony_cistatic const struct pci_error_handlers cxlflash_err_handler = {
391162306a36Sopenharmony_ci	.error_detected = cxlflash_pci_error_detected,
391262306a36Sopenharmony_ci	.slot_reset = cxlflash_pci_slot_reset,
391362306a36Sopenharmony_ci	.resume = cxlflash_pci_resume,
391462306a36Sopenharmony_ci};
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci/*
391762306a36Sopenharmony_ci * PCI device structure
391862306a36Sopenharmony_ci */
391962306a36Sopenharmony_cistatic struct pci_driver cxlflash_driver = {
392062306a36Sopenharmony_ci	.name = CXLFLASH_NAME,
392162306a36Sopenharmony_ci	.id_table = cxlflash_pci_table,
392262306a36Sopenharmony_ci	.probe = cxlflash_probe,
392362306a36Sopenharmony_ci	.remove = cxlflash_remove,
392462306a36Sopenharmony_ci	.shutdown = cxlflash_remove,
392562306a36Sopenharmony_ci	.err_handler = &cxlflash_err_handler,
392662306a36Sopenharmony_ci};
392762306a36Sopenharmony_ci
392862306a36Sopenharmony_ci/**
392962306a36Sopenharmony_ci * init_cxlflash() - module entry point
393062306a36Sopenharmony_ci *
393162306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
393262306a36Sopenharmony_ci */
393362306a36Sopenharmony_cistatic int __init init_cxlflash(void)
393462306a36Sopenharmony_ci{
393562306a36Sopenharmony_ci	int rc;
393662306a36Sopenharmony_ci
393762306a36Sopenharmony_ci	check_sizes();
393862306a36Sopenharmony_ci	cxlflash_list_init();
393962306a36Sopenharmony_ci	rc = cxlflash_class_init();
394062306a36Sopenharmony_ci	if (unlikely(rc))
394162306a36Sopenharmony_ci		goto out;
394262306a36Sopenharmony_ci
394362306a36Sopenharmony_ci	rc = pci_register_driver(&cxlflash_driver);
394462306a36Sopenharmony_ci	if (unlikely(rc))
394562306a36Sopenharmony_ci		goto err;
394662306a36Sopenharmony_ciout:
394762306a36Sopenharmony_ci	pr_debug("%s: returning rc=%d\n", __func__, rc);
394862306a36Sopenharmony_ci	return rc;
394962306a36Sopenharmony_cierr:
395062306a36Sopenharmony_ci	cxlflash_class_exit();
395162306a36Sopenharmony_ci	goto out;
395262306a36Sopenharmony_ci}
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_ci/**
395562306a36Sopenharmony_ci * exit_cxlflash() - module exit point
395662306a36Sopenharmony_ci */
395762306a36Sopenharmony_cistatic void __exit exit_cxlflash(void)
395862306a36Sopenharmony_ci{
395962306a36Sopenharmony_ci	cxlflash_term_global_luns();
396062306a36Sopenharmony_ci	cxlflash_free_errpage();
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci	pci_unregister_driver(&cxlflash_driver);
396362306a36Sopenharmony_ci	cxlflash_class_exit();
396462306a36Sopenharmony_ci}
396562306a36Sopenharmony_ci
396662306a36Sopenharmony_cimodule_init(init_cxlflash);
396762306a36Sopenharmony_cimodule_exit(exit_cxlflash);
3968