/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2013 STEC, Inc.  All rights reserved.
 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
 */

#ifndef _SKD_H
#define	_SKD_H

#include	<sys/types.h>
#include	<sys/stropts.h>
#include	<sys/stream.h>
#include	<sys/cmn_err.h>
#include	<sys/kmem.h>
#include	<sys/modctl.h>
#include	<sys/ddi.h>
#include	<sys/sunddi.h>
#include	<sys/strsun.h>
#include	<sys/kstat.h>
#include 	<sys/conf.h>
#include 	<sys/debug.h>
#include 	<sys/modctl.h>
#include 	<sys/errno.h>
#include 	<sys/pci.h>
#include 	<sys/memlist.h>
#include 	<sys/param.h>
#include	<sys/queue.h>

#define	DRV_NAME 	"skd"
#define	DRV_VERSION 	"2.2.1"
#define	DRV_BUILD_ID 	"0264"
#define	PFX DRV_NAME    ": "
#define	DRV_BIN_VERSION 0x100
#define	DRV_VER_COMPL   DRV_VERSION "." DRV_BUILD_ID
#define	VERSIONSTR 	DRV_VERSION


#define	SG_BOUNDARY		0x20000


#ifdef _BIG_ENDIAN
#define	be64_to_cpu(x) (x)
#define	be32_to_cpu(x) (x)
#define	cpu_to_be64(x) (x)
#define	cpu_to_be32(x) (x)
#else
#define	be64_to_cpu(x) BSWAP_64(x)
#define	be32_to_cpu(x) BSWAP_32(x)
#define	cpu_to_be64(x) BSWAP_64(x)
#define	cpu_to_be32(x) BSWAP_32(x)
#endif

#define	ATYPE_64BIT		0
#define	ATYPE_32BIT		1

#define	BIT_0			0x00001
#define	BIT_1			0x00002
#define	BIT_2			0x00004
#define	BIT_3			0x00008
#define	BIT_4			0x00010
#define	BIT_5			0x00020
#define	BIT_6			0x00040
#define	BIT_7			0x00080
#define	BIT_8			0x00100
#define	BIT_9			0x00200
#define	BIT_10			0x00400
#define	BIT_11			0x00800
#define	BIT_12			0x01000
#define	BIT_13			0x02000
#define	BIT_14			0x04000
#define	BIT_15			0x08000
#define	BIT_16			0x10000
#define	BIT_17			0x20000
#define	BIT_18			0x40000
#define	BIT_19			0x80000

/* Attach progress flags */
#define	SKD_ATTACHED			BIT_0
#define	SKD_SOFT_STATE_ALLOCED		BIT_1
#define	SKD_CONFIG_SPACE_SETUP		BIT_3
#define	SKD_IOBASE_MAPPED		BIT_4
#define	SKD_IOMAP_IOBASE_MAPPED		BIT_5
#define	SKD_REGS_MAPPED			BIT_6
#define	SKD_DEV_IOBASE_MAPPED		BIT_7
#define	SKD_CONSTRUCTED			BIT_8
#define	SKD_PROBED			BIT_9
#define	SKD_INTR_ADDED			BIT_10
#define	SKD_PATHNAME_ALLOCED		BIT_11
#define	SKD_SUSPENDED			BIT_12
#define	SKD_CMD_ABORT_TMO		BIT_13
#define	SKD_MUTEX_INITED		BIT_14
#define	SKD_MUTEX_DESTROYED		BIT_15

#define	SKD_IODONE_WIOC			1	/* I/O done */
#define	SKD_IODONE_WNIOC		2	/* I/O NOT done */
#define	SKD_IODONE_WDEBUG		3	/* I/O - debug stuff */

#ifdef SKD_PM
#define	MAX_POWER_LEVEL			0
#define	LOW_POWER_LEVEL			(BIT_1 | BIT_0)
#endif

#define	SKD_MSIX_AIF		0x0
#define	SKD_MSIX_RSPQ		0x1
#define	SKD_MSIX_MAXAIF		SKD_MSIX_RSPQ + 1

/*
 * Stuff from Linux
 */
#define	SAM_STAT_GOOD			0x00
#define	SAM_STAT_CHECK_CONDITION	0x02

#define	TEST_UNIT_READY		0x00
#define	INQUIRY			0x12
#define	INQUIRY2		(0x12 + 0xe0)
#define	READ_CAPACITY		0x25
#define	READ_CAPACITY_EXT	0x9e
#define	SYNCHRONIZE_CACHE	0x35

/*
 *  SENSE KEYS
 */
#define	NO_SENSE	    0x00
#define	RECOVERED_ERROR	    0x01
#define	UNIT_ATTENTION	    0x06
#define	ABORTED_COMMAND	    0x0b

typedef struct dma_mem_t {
	void			*bp;
	ddi_acc_handle_t	acc_handle;
	ddi_dma_handle_t	dma_handle;
	ddi_dma_cookie_t	cookie;
	ddi_dma_cookie_t	*cookies;
	uint32_t		size;
} dma_mem_t;

#define	SKD_WRITEL(DEV, VAL, OFF)	skd_reg_write32(DEV, VAL, OFF)
#define	SKD_READL(DEV, OFF)		skd_reg_read32(DEV, OFF)
#define	SKD_WRITEQ(DEV, VAL, OFF)	skd_reg_write64(DEV, VAL, OFF)

/* Capability lists */
#define	PCI_CAP_ID_EXP		0x10	/* PCI Express */

/*
 * End Stuff from Linux
 */

#define	SKD_DMA_MAXXFER			(2048 * DEV_BSIZE)

#define	SKD_DMA_LOW_ADDRESS		(uint64_t)0
#define	SKD_DMA_HIGH_64BIT_ADDRESS	(uint64_t)0xffffffffffffffff
#define	SKD_DMA_HIGH_32BIT_ADDRESS	(uint64_t)0xffffffff
#define	SKD_DMA_XFER_COUNTER		(uint64_t)0xffffffff
#define	SKD_DMA_ADDRESS_ALIGNMENT	(uint64_t)SG_BOUNDARY
#define	SKD_DMA_BURSTSIZES		0xff
#define	SKD_DMA_MIN_XFER_SIZE		1
#define	SKD_DMA_MAX_XFER_SIZE		(uint64_t)0xfffffe00
#define	SKD_DMA_SEGMENT_BOUNDARY	(uint64_t)0xffffffff
#define	SKD_DMA_SG_LIST_LENGTH		256
#define	SKD_DMA_XFER_FLAGS		0
#define	SKD_DMA_GRANULARITY		512 /* 1 */

#define	PCI_VENDOR_ID_STEC  0x1B39
#define	PCI_DEVICE_ID_SUMO  0x0001

#define	SKD_N_FITMSG_BYTES	(512u)

#define	SKD_N_SPECIAL_CONTEXT	64u
#define	SKD_N_SPECIAL_FITMSG_BYTES (128u)
#define	SKD_N_SPECIAL_DATA_BYTES  (8u*1024u)


/*
 * SG elements are 32 bytes, so we can make this 4096 and still be under the
 * 128KB limit.	 That allows 4096*4K = 16M xfer size
 */
#define	SKD_N_SG_PER_REQ_DEFAULT 256u
#define	SKD_N_SG_PER_SPECIAL	256u

#define	SKD_N_COMPLETION_ENTRY		256u
#define	SKD_N_READ_CAP_BYTES		(8u)
#define	SKD_N_READ_CAP_EXT_BYTES	(16)

#define	SKD_N_INTERNAL_BYTES   (512u)

/* 5 bits of uniqifier, 0xF800 */
#define	SKD_ID_INCR		(0x400)
#define	SKD_ID_TABLE_MASK	(3u << 8u)
#define	SKD_ID_RW_REQUEST	(0u << 8u)
#define	SKD_ID_INTERNAL		(1u << 8u)
#define	SKD_ID_FIT_MSG		(3u << 8u)
#define	SKD_ID_SLOT_MASK	0x00FFu
#define	SKD_ID_SLOT_AND_TABLE_MASK 0x03FFu

#define	SKD_N_TIMEOUT_SLOT	8u
#define	SKD_TIMEOUT_SLOT_MASK	7u

#define	SKD_TIMER_SECONDS(seconds) (seconds)
#define	SKD_TIMER_MINUTES(minutes) ((minutes)*(60))

/*
 * NOTE:  INTR_LOCK() should be held prior to grabbing WAITQ_LOCK() if both
 * are needed.
 */
#define	INTR_LOCK(skdev)		mutex_enter(&skdev->skd_intr_mutex)
#define	INTR_UNLOCK(skdev)		mutex_exit(&skdev->skd_intr_mutex)
#define	INTR_LOCK_HELD(skdev)		MUTEX_HELD(&skdev->skd_intr_mutex)

#define	WAITQ_LOCK(skdev) \
	mutex_enter(&skdev->waitqueue_mutex)
#define	WAITQ_UNLOCK(skdev) \
	mutex_exit(&skdev->waitqueue_mutex)
#define	WAITQ_LOCK_HELD(skdev) \
	MUTEX_HELD(&skdev->waitqueue_mutex)

#define	ADAPTER_STATE_LOCK(skdev)	mutex_enter(&skdev->skd_lock_mutex)
#define	ADAPTER_STATE_UNLOCK(skdev)	mutex_exit(&skdev->skd_lock_mutex)

enum skd_drvr_state {
	SKD_DRVR_STATE_LOAD,			/* 0 when driver first loaded */
	SKD_DRVR_STATE_IDLE,			/* 1 when device goes offline */
	SKD_DRVR_STATE_BUSY,			/* 2 */
	SKD_DRVR_STATE_STARTING,		/* 3 */
	SKD_DRVR_STATE_ONLINE,			/* 4 */
	SKD_DRVR_STATE_PAUSING,			/* 5 */
	SKD_DRVR_STATE_PAUSED,			/* 6 */
	SKD_DRVR_STATE_DRAINING_TIMEOUT,	/* 7 */
	SKD_DRVR_STATE_RESTARTING,		/* 8 */
	SKD_DRVR_STATE_RESUMING,		/* 9 */
	SKD_DRVR_STATE_STOPPING,	/* 10 when driver is unloading */
	SKD_DRVR_STATE_FAULT,			/* 11 */
	SKD_DRVR_STATE_DISAPPEARED,		/* 12 */
	SKD_DRVR_STATE_PROTOCOL_MISMATCH,	/* 13 */
	SKD_DRVR_STATE_BUSY_ERASE,		/* 14 */
	SKD_DRVR_STATE_BUSY_SANITIZE,		/* 15 */
	SKD_DRVR_STATE_BUSY_IMMINENT,		/* 16 */
	SKD_DRVR_STATE_WAIT_BOOT,		/* 17 */
	SKD_DRVR_STATE_SYNCING			/* 18 */
};

#define	SKD_WAIT_BOOT_TO 90u
#define	SKD_STARTING_TO	 248u

enum skd_req_state {
	SKD_REQ_STATE_IDLE,
	SKD_REQ_STATE_SETUP,
	SKD_REQ_STATE_BUSY,
	SKD_REQ_STATE_COMPLETED,
	SKD_REQ_STATE_TIMEOUT,
	SKD_REQ_STATE_ABORTED,
};

enum skd_fit_msg_state {
	SKD_MSG_STATE_IDLE,
	SKD_MSG_STATE_BUSY,
};

enum skd_check_status_action {
	SKD_CHECK_STATUS_REPORT_GOOD,
	SKD_CHECK_STATUS_REPORT_SMART_ALERT,
	SKD_CHECK_STATUS_REQUEUE_REQUEST,
	SKD_CHECK_STATUS_REPORT_ERROR,
	SKD_CHECK_STATUS_BUSY_IMMINENT,
};

/* NOTE:  mbu_t users should name this field "mbu". */
typedef union {
	uint8_t *mb8;
	uint64_t *mb64;
} mbu_t;
#define	msg_buf mbu.mb8
#define	msg_buf64 mbu.mb64

struct skd_fitmsg_context {
	enum skd_fit_msg_state state;
	struct skd_fitmsg_context *next;
	uint32_t	id;
	uint16_t	outstanding;
	uint32_t	length;
	uint32_t	offset;
	mbu_t		mbu;	/* msg_buf & msg_buf64 */
	dma_mem_t	mb_dma_address;
};

struct skd_request_context {
	enum skd_req_state		state;
	struct skd_request_context	*next;
	uint16_t			did_complete;
	uint16_t			id;
	uint32_t			fitmsg_id;
	struct skd_buf_private		*pbuf;
	uint32_t			timeout_stamp;
	uint8_t				sg_data_dir;
	uint32_t			n_sg;
	ddi_dma_handle_t		io_dma_handle;
	struct fit_sg_descriptor	*sksg_list;
	dma_mem_t			sksg_dma_address;
	struct fit_completion_entry_v1	completion;
	struct fit_comp_error_info	err_info;
	int				total_sg_bcount;
};

#define	SKD_DATA_DIR_HOST_TO_CARD	1
#define	SKD_DATA_DIR_CARD_TO_HOST	2

struct skd_special_context {
	struct skd_request_context req;
	uint8_t			   orphaned;
	uint32_t		   sg_byte_count;
	void			   *data_buf;
	dma_mem_t		   db_dma_address;
	mbu_t			   mbu;	/* msg_buf & msg_buf64 */
	dma_mem_t		   mb_dma_address;
	int			   io_pending;
};

typedef struct skd_buf_private {
	SIMPLEQ_ENTRY(skd_buf_private) sq;
	struct skd_request_context *skreq;
	bd_xfer_t *x_xfer;
	int dir;
} skd_buf_private_t;

SIMPLEQ_HEAD(waitqueue, skd_buf_private);

typedef struct skd_device skd_device_t;

struct skd_device {
	int		irq_type;
	int		gendisk_on;
	int		sync_done;

	char		name[32];

	enum		skd_drvr_state state;
	uint32_t		drive_state;

	uint32_t	queue_depth_busy;
	uint32_t	queue_depth_limit;
	uint32_t	queue_depth_lowat;
	uint32_t	soft_queue_depth_limit;
	uint32_t	hard_queue_depth_limit;

	uint32_t	num_fitmsg_context;
	uint32_t	num_req_context;

	uint32_t	timeout_slot[SKD_N_TIMEOUT_SLOT];
	uint32_t	timeout_stamp;

	struct skd_fitmsg_context *skmsg_free_list;
	struct skd_fitmsg_context *skmsg_table;

	struct skd_request_context *skreq_free_list;
	struct skd_request_context *skreq_table;
	struct skd_special_context internal_skspcl;

	uint64_t	read_cap_last_lba;
	uint32_t	read_cap_blocksize;
	int		read_cap_is_valid;
	int		inquiry_is_valid;
	char		inq_serial_num[13]; /* 12 chars plus null term */
	char		inq_vendor_id[9];
	char		inq_product_id[17];
	char		inq_product_rev[5];
	char		id_str[128]; /* holds a composite name (pci + sernum) */

	uint8_t		skcomp_cycle;
	uint32_t	skcomp_ix;
	struct fit_completion_entry_v1 *skcomp_table;
	struct fit_comp_error_info *skerr_table;
	dma_mem_t	cq_dma_address;

	uint32_t	timer_active;
	uint32_t	timer_countdown;
	uint32_t	timer_substate;

	int		sgs_per_request;
	uint32_t	last_mtd;

	uint32_t	proto_ver;

	int		dbg_level;

	uint32_t	timo_slot;

	ddi_acc_handle_t	pci_handle;
	ddi_acc_handle_t	iobase_handle;
	ddi_acc_handle_t	iomap_handle;
	caddr_t			iobase;
	caddr_t			iomap_iobase;

	ddi_acc_handle_t	dev_handle;
	caddr_t			dev_iobase;
	int			dev_memsize;

	char			*pathname;

	dev_info_t		*dip;

	int			instance;
	uint16_t		vendor_id;
	uint16_t		device_id;

	kmutex_t		skd_lock_mutex;
	kmutex_t		skd_intr_mutex;
	kmutex_t		skd_fit_mutex;

	uint32_t		flags;

#ifdef SKD_PM
	uint8_t			power_level;
#endif

	/* AIF (Advanced Interrupt Framework) support */
	ddi_intr_handle_t	*htable;
	uint32_t		hsize;
	int32_t			intr_cnt;
	uint32_t		intr_pri;
	int32_t			intr_cap;

	uint64_t		Nblocks;

	ddi_iblock_cookie_t	iblock_cookie;

	int		n_req;
	uint32_t	progress;
	uint64_t	intr_cntr;
	uint64_t	fitmsg_sent1;
	uint64_t	fitmsg_sent2;
	uint64_t	active_cmds;

	kmutex_t	skd_internalio_mutex;
	kcondvar_t	cv_waitq;

	kmutex_t	waitqueue_mutex;
	struct waitqueue waitqueue;
	int		disks_initialized;

	ddi_devid_t	s1120_devid;
	char		devid_str[80];

	uint32_t	d_blkshift;

	int		attached;

	int		ios_queued;
	int		ios_started;
	int		ios_completed;
	int		ios_errors;
	int		iodone_wioc;
	int		iodone_wnioc;
	int		iodone_wdebug;
	int		iodone_unknown;

	bd_handle_t	s_bdh;
	int		bd_attached;

#ifdef USE_SKE_EMULATOR
	ske_device_t *ske_handle;
#endif

	timeout_id_t	skd_timer_timeout_id;
};

static void skd_disable_interrupts(struct skd_device *skdev);
static void skd_isr_completion_posted(struct skd_device *skdev);
static void skd_recover_requests(struct skd_device *skdev);
static void skd_log_skdev(struct skd_device *skdev, const char *event);
static void skd_restart_device(struct skd_device *skdev);
static void skd_destruct(struct skd_device *skdev);
static int skd_unquiesce_dev(struct skd_device *skdev);
static void skd_send_special_fitmsg(struct skd_device *skdev,
    struct skd_special_context *skspcl);
static void skd_end_request(struct skd_device *skdev,
    struct skd_request_context *skreq, int error);
static void skd_log_skmsg(struct skd_device *skdev,
    struct skd_fitmsg_context *skmsg, const char *event);
static void skd_log_skreq(struct skd_device *skdev,
    struct skd_request_context *skreq, const char *event);
static void skd_send_fitmsg(struct skd_device *skdev,
    struct skd_fitmsg_context *skmsg);

static const char *skd_drive_state_to_str(int state);
static const char *skd_skdev_state_to_str(enum skd_drvr_state state);

#endif /* _SKD_H */