/*-
 * Copyright (c) 2018 VMware, Inc.
 *
 * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
 *
 * $FreeBSD$
 */

#ifndef _PVSCSI_H_
#define _PVSCSI_H_

#define	MASK(v)	((1 << (v)) - 1)

#define	PCI_VENDOR_ID_VMWARE		0x15ad
#define	PCI_DEVICE_ID_VMWARE_PVSCSI	0x07c0

enum pvscsi_reg_offset {
	PVSCSI_REG_OFFSET_COMMAND		= 0x0000,
	PVSCSI_REG_OFFSET_COMMAND_DATA		= 0x0004,
	PVSCSI_REG_OFFSET_COMMAND_STATUS	= 0x0008,
	PVSCSI_REG_OFFSET_LAST_STS_0		= 0x0100,
	PVSCSI_REG_OFFSET_LAST_STS_1		= 0x0104,
	PVSCSI_REG_OFFSET_LAST_STS_2		= 0x0108,
	PVSCSI_REG_OFFSET_LAST_STS_3		= 0x010c,
	PVSCSI_REG_OFFSET_INTR_STATUS		= 0x100c,
	PVSCSI_REG_OFFSET_INTR_MASK		= 0x2010,
	PVSCSI_REG_OFFSET_KICK_NON_RW_IO	= 0x3014,
	PVSCSI_REG_OFFSET_DEBUG			= 0x3018,
	PVSCSI_REG_OFFSET_KICK_RW_IO		= 0x4018,
};

enum pvscsi_commands {
	PVSCSI_CMD_FIRST			= 0,

	PVSCSI_CMD_ADAPTER_RESET		= 1,
	PVSCSI_CMD_ISSUE_SCSI			= 2,
	PVSCSI_CMD_SETUP_RINGS			= 3,
	PVSCSI_CMD_RESET_BUS			= 4,
	PVSCSI_CMD_RESET_DEVICE			= 5,
	PVSCSI_CMD_ABORT_CMD			= 6,
	PVSCSI_CMD_CONFIG			= 7,
	PVSCSI_CMD_SETUP_MSG_RING		= 8,
	PVSCSI_CMD_DEVICE_UNPLUG		= 9,
	PVSCSI_CMD_SETUP_REQCALLTHRESHOLD	= 10,
	PVSCSI_CMD_GET_MAX_TARGETS		= 11,

	PVSCSI_CMD_LAST				= 12,
};

struct pvscsi_cmd_desc_reset_device {
	uint32_t	target;
	uint8_t		lun[8];
};

struct pvscsi_cmd_desc_abort_cmd {
	uint64_t	context;
	uint32_t	target;
	uint32_t	pad;
};

#define	PVSCSI_SETUP_RINGS_MAX_NUM_PAGES	32
#define	PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES	16

struct pvscsi_cmd_desc_setup_rings {
	uint32_t	req_ring_num_pages;
	uint32_t	cmp_ring_num_pages;
	uint64_t	rings_state_ppn;
	uint64_t	req_ring_ppns[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
	uint64_t	cmp_ring_ppns[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
};

struct pvscsi_cmd_desc_setup_msg_ring {
	uint32_t	num_pages;
	uint32_t	pad_;
	uint64_t	ring_ppns[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES];
};

struct pvscsi_rings_state {
	uint32_t	req_prod_idx;
	uint32_t	req_cons_idx;
	uint32_t	req_num_entries_log2;
	uint32_t	cmp_prod_idx;
	uint32_t	cmp_cons_idx;
	uint32_t	cmp_num_entries_log2;
	uint32_t	req_call_threshold;
	uint8_t		_pad[100];
	uint32_t	msg_prod_idx;
	uint32_t	msg_cons_idx;
	uint32_t	msg_num_entries_log2;
};

#define	PVSCSI_FLAG_CMD_WITH_SG_LIST	(1 << 0)
#define	PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB	(1 << 1)
#define	PVSCSI_FLAG_CMD_DIR_NONE	(1 << 2)
#define	PVSCSI_FLAG_CMD_DIR_TOHOST	(1 << 3)
#define	PVSCSI_FLAG_CMD_DIR_TODEVICE	(1 << 4)

#define	PVSCSI_FLAG_RESERVED_MASK	(~MASK(5))

#define	PVSCSI_INTR_CMPL_0	(1 << 0)
#define	PVSCSI_INTR_CMPL_1	(1 << 1)
#define	PVSCSI_INTR_CMPL_MASK	MASK(2)

#define	PVSCSI_INTR_MSG_0	(1 << 2)
#define	PVSCSI_INTR_MSG_1	(1 << 3)
#define	PVSCSI_INTR_MSG_MASK	(MASK(2) << 2)

#define	PVSCSI_INTR_ALL_SUPPORTED	MASK(4)

struct pvscsi_ring_req_desc {
	uint64_t	context;
	uint64_t	data_addr;
	uint64_t	data_len;
	uint64_t	sense_addr;
	uint32_t	sense_len;
	uint32_t	flags;
	uint8_t		cdb[16];
	uint8_t		cdb_len;
	uint8_t		lun[8];
	uint8_t		tag;
	uint8_t		bus;
	uint8_t		target;
	uint16_t	vcpu_hint;
	uint8_t		unused[58];
};

struct pvscsi_ring_cmp_desc {
	uint64_t	context;
	uint64_t	data_len;
	uint32_t	sense_len;
	uint16_t	host_status;
	uint16_t	scsi_status;
	uint32_t	_pad[2];
};

#define	PVSCSI_MAX_SG_ENTRIES_PER_SEGMENT	128
#define	PVSCSI_MAX_NUM_SG_SEGMENTS		128
#define	PVSCSI_SGE_FLAG_CHAIN_ELEMENT		(1 << 0)

struct pvscsi_sg_element {
	uint64_t	addr;
	uint32_t	length;
	uint32_t	flags;
};

enum pvscsi_msg_type {
	PVSCSI_MSG_DEV_ADDED	= 0,
	PVSCSI_MSG_DEV_REMOVED	= 1,
	PVSCSI_MSG_LAST	= 2,
};

struct pvscsi_ring_msg_desc {
	uint32_t	type;
	uint32_t	args[31];
};

struct pvscsi_ring_msg_dev_status_changed {
	uint32_t	type;
	uint32_t	bus;
	uint32_t	target;
	uint8_t		lun[8];
	uint32_t	pad[27];
};

struct pvscsi_cmd_desc_setup_req_call {
	uint32_t	enable;
};

#define	PVSCSI_MAX_NUM_PAGES_REQ_RING	PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
#define	PVSCSI_MAX_NUM_PAGES_CMP_RING	PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
#define	PVSCSI_MAX_NUM_PAGES_MSG_RING	PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES

#define	PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \
	(PAGE_SIZE / sizeof(struct pvscsi_ring_req_desc))
#define	PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE \
	(PAGE_SIZE / sizeof(struct pvscs_ring_cmp_desc))
#define	PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE \
	(PAGE_SIZE / sizeof(struct pvscsi_ring_msg_desc))

#define	PVSCSI_MAX_REQ_QUEUE_DEPTH \
	(PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE)
#define	PVSCSI_MAX_CMP_QUEUE_DEPTH \
	(PVSCSI_MAX_NUM_PAGES_CMP_RING * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE)
#define	PVSCSI_MAX_QUEUE_DEPTH \
	MAX(PVSCSI_MAX_REQ_QUEUE_DEPTH, PVSCSI_MAX_CMP_QUEUE_DEPTH)

enum pvscsi_host_status {
	BTSTAT_SUCCESS		= 0x00,
	BTSTAT_LINKED_COMMAND_COMPLETED			= 0x0a,
	BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG	= 0x0b,
	BTSTAT_DATA_UNDERRUN	= 0x0c,
	BTSTAT_SELTIMEO		= 0x11,
	BTSTAT_DATARUN		= 0x12,
	BTSTAT_BUSFREE		= 0x13,
	BTSTAT_INVPHASE		= 0x14,
	BTSTAT_INVCODE		= 0x15,
	BTSTAT_INVOPCODE	= 0x16,
	BTSTAT_LUNMISMATCH	= 0x17,
	BTSTAT_INVPARAM		= 0x1a,
	BTSTAT_SENSFAILED	= 0x1b,
	BTSTAT_TAGREJECT	= 0x1c,
	BTSTAT_BADMSG		= 0x1d,
	BTSTAT_HAHARDWARE	= 0x20,
	BTSTAT_NORESPONSE	= 0x21,
	BTSTAT_SENTRST		= 0x22,
	BTSTAT_RECVRST		= 0x23,
	BTSTAT_DISCONNECT	= 0x24,
	BTSTAT_BUSRESET		= 0x25,
	BTSTAT_ABORTQUEUE	= 0x26,
	BTSTAT_HASOFTWARE	= 0x27,
	BTSTAT_HATIMEOUT	= 0x30,
	BTSTAT_SCSIPARITY	= 0x34,
};

#endif /* !_PVSCSI_H_ */